Merge pull request #55 from mapbox/common-buffer

Create a common buffer implementation
This commit is contained in:
Konstantin Käfer
2014-02-12 11:14:34 +01:00
15 changed files with 175 additions and 219 deletions

View File

@@ -0,0 +1,76 @@
#ifndef LLMR_GEOMETRY_BUFFER
#define LLMR_GEOMETRY_BUFFER
#include <cstdlib>
#include <cassert>
#include <llmr/platform/gl.hpp>
namespace llmr {
template <
size_t itemSize,
int bufferType = GL_ARRAY_BUFFER,
size_t defaultLength = 8192
>
class Buffer {
public:
~Buffer() {
if (array) {
free(array);
array = nullptr;
}
if (buffer != 0) {
glDeleteBuffers(1, &buffer);
buffer = 0;
}
}
// Returns the number of elements in this buffer. This is not the number of
// bytes, but rather the number of coordinates with associated information.
inline size_t index() const {
return pos / itemSize;
}
// Transfers this buffer to the GPU and binds the buffer to the GL context.
void bind() {
if (buffer == 0) {
glGenBuffers(1, &buffer);
glBindBuffer(bufferType, buffer);
glBufferData(bufferType, pos, array, GL_STATIC_DRAW);
free(array);
array = nullptr;
} else {
glBindBuffer(bufferType, buffer);
}
}
protected:
// increase the buffer size by at least /required/ bytes.
void *addElement() {
assert("Buffer is already bound to GPU" && buffer == 0);
if (length < pos + itemSize) {
while (length < pos + itemSize) length += defaultLength;
array = realloc(array, length);
assert("Buffer reallocation failed" && array != nullptr);
}
pos += itemSize;
return static_cast<char *>(array) + (pos - itemSize);
}
private:
// CPU buffer
void *array = nullptr;
// Byte position where we are writing.
size_t pos = 0;
// Number of bytes that are valid in this buffer.
size_t length = 0;
// GL buffer ID
uint32_t buffer = 0;
};
}
#endif

View File

@@ -1,26 +1,15 @@
#ifndef LLMR_GEOMETRY_DEBUG_FONT_BUFFER
#define LLMR_GEOMETRY_DEBUG_FONT_BUFFER
#include <vector>
#include <cstdint>
#include "buffer.hpp"
namespace llmr {
class DebugFontBuffer {
class DebugFontBuffer : public Buffer<
4 // 2 bytes per coordinate, 2 coordinates
> {
public:
DebugFontBuffer();
~DebugFontBuffer();
void addText(const char *text, double left, double baseline, double scale = 1);
void clear();
uint32_t length();
void bind();
private:
std::vector<uint16_t> array;
uint32_t buffer;
bool dirty;
};
}

View File

@@ -1,35 +1,32 @@
#ifndef LLMR_GEOMETRY_FILL_BUFFER
#define LLMR_GEOMETRY_FILL_BUFFER
#include "buffer.hpp"
#include <vector>
#include <cstdint>
namespace llmr {
class FillBuffer {
class FillVertexBuffer : public Buffer<
4 // bytes per coordinates (2 * unsigned short == 4 bytes)
> {
public:
typedef int16_t vertex_type;
typedef uint16_t element_type;
const uint8_t components = 2;
FillBuffer();
~FillBuffer();
uint32_t vertex_length() const;
uint32_t elements_length() const;
void addDegenerate();
void addCoordinate(vertex_type x, vertex_type y);
void addElements(element_type a, element_type b, element_type c);
void add(vertex_type x, vertex_type y);
};
void bind();
private:
std::vector<vertex_type> vertices;
std::vector<element_type> elements;
uint32_t vertex_buffer;
uint32_t element_buffer;
bool dirty;
class FillElementsBuffer : public Buffer<
6, // bytes per triangle (3 * unsigned short == 6 bytes)
GL_ELEMENT_ARRAY_BUFFER
> {
public:
typedef uint16_t element_type;
void add(element_type a, element_type b, element_type c);
};
}

View File

@@ -1,17 +1,16 @@
#ifndef LLMR_GEOMETRY_LINE_BUFFER
#define LLMR_GEOMETRY_LINE_BUFFER
#include <vector>
#include <cstdint>
#include <cmath>
#include "buffer.hpp"
namespace llmr {
class LineBuffer {
class LineBuffer : public Buffer<
8 // 2 coordinates per vertex + 1 linesofar + 1 extrude coord pair == 4 (== 8 bytes)
> {
public:
typedef int16_t vertex_type;
~LineBuffer();
/*
* Scale the extrusion vector so that the normal length is this value.
@@ -44,22 +43,6 @@ public:
* coordinate of the next triangle strip.
*/
void addDegenerate();
/*
* Returns the number of elements in this buffer. This is not the number of
* bytes, but rather the number of coordinates with associated information.
*/
uint32_t length() const;
/*
* Transfers this buffer to the GPU and binds the buffer to the GL context.
*/
void bind();
private:
bool dirty = true;
std::vector<vertex_type> array;
uint32_t buffer = 0;
};
}

View File

@@ -17,7 +17,7 @@ public:
* Returns the number of elements in this buffer. This is not the number of
* bytes, but rather the number of coordinates with associated information.
*/
uint32_t length() const;
uint32_t index() const;
/*
* Transfers this buffer to the GPU and binds the buffer to the GL context.

View File

@@ -21,7 +21,8 @@ class LayerDescription;
class BucketDescription;
class VectorTile;
class VectorTileLayer;
class FillBuffer;
class FillVertexBuffer;
class FillElementsBuffer;
class LineBuffer;
class PlainShader;
@@ -72,7 +73,8 @@ public:
DebugFontBuffer debugFontBuffer;
VertexArrayObject<PlainShader> debugFontArray;
std::shared_ptr<FillBuffer> fillBuffer;
std::shared_ptr<FillVertexBuffer> fillVertexBuffer;
std::shared_ptr<FillElementsBuffer> fillElementsBuffer;
std::shared_ptr<LineBuffer> lineBuffer;
// Holds the buckets of this tile.

View File

@@ -16,7 +16,8 @@
namespace llmr {
class Style;
class FillBuffer;
class FillVertexBuffer;
class FillElementsBuffer;
class BucketDescription;
class OutlineShader;
class PlainShader;
@@ -25,7 +26,9 @@ struct pbf;
class FillBucket : public Bucket {
public:
FillBucket(const std::shared_ptr<FillBuffer>& buffer, const BucketDescription& bucket_desc);
FillBucket(const std::shared_ptr<FillVertexBuffer>& vertexBuffer,
const std::shared_ptr<FillElementsBuffer>& elementsBuffer,
const BucketDescription& bucket_desc);
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
@@ -40,7 +43,8 @@ public:
const BucketGeometryDescription geom_desc;
private:
std::shared_ptr<FillBuffer> buffer;
std::shared_ptr<FillVertexBuffer> vertexBuffer;
std::shared_ptr<FillElementsBuffer> elementsBuffer;
struct group {
VertexArrayObject<PlainShader> array;

View File

@@ -7,20 +7,9 @@
using namespace llmr;
DebugFontBuffer::DebugFontBuffer()
: buffer(0),
dirty(true) {
}
DebugFontBuffer::~DebugFontBuffer() {
if (buffer != 0) {
glDeleteBuffers(1, &buffer);
buffer = 0;
}
}
void DebugFontBuffer::addText(const char *text, double left, double baseline, double scale) {
uint16_t *coords = nullptr;
int32_t length = strlen(text);
for (int32_t i = 0; i < length; ++i) {
if (text[i] < 32 || text[i] > 127) {
@@ -45,39 +34,17 @@ void DebugFontBuffer::addText(const char *text, double left, double baseline, do
int16_t x = round(left + glyph.data[j] * scale);
int16_t y = round(baseline - glyph.data[j + 1] * scale);
if (prev) {
array.push_back(prev_x);
array.push_back(prev_y);
array.push_back(x);
array.push_back(y);
coords = static_cast<uint16_t *>(addElement());
coords[0] = prev_x;
coords[1] = prev_y;
coords = static_cast<uint16_t *>(addElement());
coords[0] = x;
coords[1] = y;
}
prev_x = x; prev_y = y; prev = true;
}
}
left += glyph.width * scale;
}
dirty = true;
}
void DebugFontBuffer::clear() {
array.clear();
dirty = true;
}
uint32_t DebugFontBuffer::length() {
// We store 2 coordinates per vertex
return array.size() / 2;
}
void DebugFontBuffer::bind() {
if (buffer == 0) {
glGenBuffers(1, &buffer);
}
glBindBuffer(GL_ARRAY_BUFFER, buffer);
if (dirty) {
glBufferData(GL_ARRAY_BUFFER, array.size() * sizeof(uint16_t), array.data(), GL_STATIC_DRAW);
dirty = false;
}
}

View File

@@ -5,65 +5,21 @@
using namespace llmr;
FillBuffer::FillBuffer()
: vertex_buffer(0),
element_buffer(0),
dirty(true) {
void FillVertexBuffer::addDegenerate() {
vertex_type *vertices = static_cast<vertex_type *>(addElement());
vertices[0] = 32767;
vertices[1] = 0;
}
FillBuffer::~FillBuffer() {
if (vertex_buffer != 0) {
glDeleteBuffers(1, &vertex_buffer);
vertex_buffer = 0;
}
if (element_buffer != 0) {
glDeleteBuffers(1, &element_buffer);
element_buffer = 0;
}
void FillVertexBuffer::add(vertex_type x, vertex_type y) {
vertex_type *vertices = static_cast<vertex_type *>(addElement());
vertices[0] = x;
vertices[1] = y;
}
uint32_t FillBuffer::vertex_length() const {
// We store 2 coordinates per vertex
return vertices.size() / 2;
}
uint32_t FillBuffer::elements_length() const {
// A triangle has 3 indices
return elements.size() / 3;
}
void FillBuffer::addDegenerate() {
vertices.push_back(32767);
vertices.push_back(0);
}
void FillBuffer::addCoordinate(vertex_type x, vertex_type y) {
vertices.push_back(x);
vertices.push_back(y);
}
void FillBuffer::addElements(element_type a, element_type b, element_type c) {
elements.push_back(a);
elements.push_back(b);
elements.push_back(c);
}
void FillBuffer::bind() {
if (vertex_buffer == 0) {
glGenBuffers(1, &vertex_buffer);
}
if (element_buffer == 0) {
glGenBuffers(1, &element_buffer);
}
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
if (dirty) {
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertex_type), vertices.data(), GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(element_type), elements.data(), GL_STATIC_DRAW);
dirty = false;
}
void FillElementsBuffer::add(element_type a, element_type b, element_type c) {
element_type *elements = static_cast<element_type *>(addElement());
elements[0] = a;
elements[1] = b;
elements[2] = c;
}

View File

@@ -1,46 +1,23 @@
#include <llmr/geometry/line_buffer.hpp>
#include <llmr/platform/gl.hpp>
#include <cmath>
using namespace llmr;
LineBuffer::~LineBuffer() {
if (buffer != 0) {
glDeleteBuffers(1, &buffer);
buffer = 0;
}
}
uint32_t LineBuffer::length() const {
// We store 2 coordinates per vertex + 1 linesofar + 1 extrude coord pair == 4 (== 8 bytes)
return array.size() / 4;
}
void LineBuffer::add(vertex_type x, vertex_type y, float ex, float ey, int8_t tx, int8_t ty, int32_t linesofar) {
array.push_back((x * 2) | tx);
array.push_back((y * 2) | ty);
array.push_back(linesofar);
void *data = addElement();
int16_t extrude = 0;
*(((int8_t *)&extrude) + 0) = round(extrudeScale * ex);
*(((int8_t *)&extrude) + 1) = round(extrudeScale * ey);
int16_t *coords = static_cast<int16_t *>(data);
coords[0] = (x * 2) | tx;
coords[1] = (y * 2) | ty;
coords[2] = linesofar;
array.push_back(extrude);
if (!dirty) dirty = true;
int8_t *extrude = static_cast<int8_t *>(data);
extrude[6] = round(extrudeScale * ex);
extrude[7] = round(extrudeScale * ey);
};
void LineBuffer::addDegenerate() {
add(16383, 16383, 0, 0, 1, 1, 0);
}
void LineBuffer::bind() {
if (buffer == 0) {
glGenBuffers(1, &buffer);
}
glBindBuffer(GL_ARRAY_BUFFER, buffer);
if (dirty) {
glBufferData(GL_ARRAY_BUFFER, array.size() * sizeof(vertex_type), array.data(), GL_STATIC_DRAW);
dirty = false;
}
}

View File

@@ -12,7 +12,7 @@ VertexBuffer::~VertexBuffer() {
}
}
uint32_t VertexBuffer::length() const {
uint32_t VertexBuffer::index() const {
// We store 2 coordinates per vertex + 1 linesofar + 1 extrude coord pair == 4 (== 8 bytes)
return array.size() / 2;
}

View File

@@ -42,7 +42,8 @@ std::forward_list<Tile::ID> Tile::children(const ID& id, int32_t z) {
Tile::Tile(ID id, const Style& style)
: id(id),
state(initial),
fillBuffer(std::make_shared<FillBuffer>()),
fillVertexBuffer(std::make_shared<FillVertexBuffer>()),
fillElementsBuffer(std::make_shared<FillElementsBuffer>()),
lineBuffer(std::make_shared<LineBuffer>()),
style(style) {
@@ -166,7 +167,7 @@ std::shared_ptr<Bucket> Tile::createBucket(const VectorTile& tile, const BucketD
}
std::shared_ptr<Bucket> Tile::createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) {
std::shared_ptr<FillBucket> bucket = std::make_shared<FillBucket>(fillBuffer, bucket_desc);
std::shared_ptr<FillBucket> bucket = std::make_shared<FillBucket>(fillVertexBuffer, fillElementsBuffer, bucket_desc);
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
for (pbf feature : filtered_layer) {

View File

@@ -14,11 +14,14 @@ struct geometry_too_long_exception : std::exception {};
using namespace llmr;
FillBucket::FillBucket(const std::shared_ptr<FillBuffer>& buffer, const BucketDescription& bucket_desc)
FillBucket::FillBucket(const std::shared_ptr<FillVertexBuffer>& vertexBuffer,
const std::shared_ptr<FillElementsBuffer>& elementsBuffer,
const BucketDescription& bucket_desc)
: geom_desc(bucket_desc.geometry),
buffer(buffer),
vertex_start(buffer->vertex_length()),
elements_start(buffer->elements_length()),
vertexBuffer(vertexBuffer),
elementsBuffer(elementsBuffer),
vertex_start(vertexBuffer->index()),
elements_start(elementsBuffer->index()),
length(0) {
}
@@ -44,13 +47,13 @@ void FillBucket::addGeometry(pbf& geom) {
}
void FillBucket::addGeometry(const std::vector<Coordinate>& line) {
uint32_t vertex_start = buffer->vertex_length();
buffer->addDegenerate();
uint32_t vertex_start = vertexBuffer->index();
vertexBuffer->addDegenerate();
for (const Coordinate& coord : line) {
buffer->addCoordinate(coord.x, coord.y);
vertexBuffer->add(coord.x, coord.y);
}
uint32_t vertex_end = buffer->vertex_length();
uint32_t vertex_end = vertexBuffer->index();
if (vertex_end - vertex_start > 65535) {
throw geometry_too_long_exception();
@@ -78,13 +81,13 @@ void FillBucket::addGeometry(const std::vector<Coordinate>& line) {
assert(firstIndex + vertex_count - 1 < 65536);
uint32_t elements_start = buffer->elements_length();
uint32_t elements_start = elementsBuffer->index();
for (uint32_t i = 2; i < vertex_count; ++i) {
buffer->addElements(firstIndex, firstIndex + i - 1, firstIndex + i);
elementsBuffer->add(firstIndex, firstIndex + i - 1, firstIndex + i);
}
uint32_t elements_end = buffer->elements_length();
uint32_t elements_end = elementsBuffer->index();
uint32_t elements_count = elements_end - elements_start;
group.vertex_length += vertex_count;
group.elements_length += elements_count;
@@ -105,7 +108,8 @@ void FillBucket::drawElements(PlainShader& shader) {
char *vertex_index = BUFFER_OFFSET(vertex_start * 2 * sizeof(int16_t));
char *elements_index = BUFFER_OFFSET(elements_start * 3 * sizeof(int16_t));
for (group& group : groups) {
group.array.bind(shader, *buffer, vertex_index);
group.array.bind(shader, *vertexBuffer, vertex_index);
elementsBuffer->bind();
glDrawElements(GL_TRIANGLES, group.elements_length * 3 - 3, GL_UNSIGNED_SHORT, elements_index);
vertex_index += group.vertex_length * 2 * sizeof(uint16_t);
elements_index += group.elements_length * 3 * sizeof(uint16_t);
@@ -115,6 +119,6 @@ void FillBucket::drawElements(PlainShader& shader) {
void FillBucket::drawVertices(OutlineShader& shader) {
// Draw the entire line
char *vertex_index = BUFFER_OFFSET(vertex_start * 2 * sizeof(int16_t));
array.bind(shader, *buffer, vertex_index);
array.bind(shader, *vertexBuffer, vertex_index);
glDrawArrays(GL_LINE_STRIP, 0, length);
}

View File

@@ -21,7 +21,7 @@ using namespace llmr;
LineBucket::LineBucket(const std::shared_ptr<LineBuffer>& buffer, const BucketDescription& bucket_desc)
: geometry(bucket_desc.geometry),
buffer(buffer),
start(buffer->length()),
start(buffer->index()),
length(0) {
}
@@ -258,7 +258,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
}
// Update the length.
length = buffer->length() - start;
length = buffer->index() - start;
}
void LineBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {

View File

@@ -96,7 +96,7 @@ void Painter::drawClippingMask() {
// Draw the clipping mask
glUniform4f(plainShader->u_color, 1.0f, 0.0f, 1.0f, 1.0f);
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.length());
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
// glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
@@ -265,7 +265,7 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons
// Draw a rectangle that covers the entire viewport.
coveringPatternArray.bind(*patternShader, tileStencilBuffer, BUFFER_OFFSET(0));
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.length());
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
} else {
// Draw filling rectangle.
@@ -275,7 +275,7 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons
// Draw a rectangle that covers the entire viewport.
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.length());
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
}
glStencilMask(0x00);
@@ -365,16 +365,16 @@ void Painter::renderDebug(const Tile::Ptr& tile) {
tileBorderArray.bind(*plainShader, tileBorderBuffer, BUFFER_OFFSET(0));
glUniform4f(plainShader->u_color, 1.0f, 0.0f, 0.0f, 1.0f);
glLineWidth(4.0f);
glDrawArrays(GL_LINE_STRIP, 0, tileBorderBuffer.length());
glDrawArrays(GL_LINE_STRIP, 0, tileBorderBuffer.index());
// draw debug info
tile->debugFontArray.bind(*plainShader, tile->debugFontBuffer, BUFFER_OFFSET(0));
glUniform4f(plainShader->u_color, 1.0f, 1.0f, 1.0f, 1.0f);
glLineWidth(4.0f);
glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.length());
glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.index());
glUniform4f(plainShader->u_color, 0.0f, 0.0f, 0.0f, 1.0f);
glLineWidth(2.0f);
glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.length());
glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.index());
// Revert blending mode to blend to the back.
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
@@ -391,7 +391,7 @@ void Painter::renderMatte() {
// Draw the clipping mask
matteArray.bind(*plainShader, matteBuffer, BUFFER_OFFSET(0));
glUniform4fv(plainShader->u_color, 1, white.data());
glDrawArrays(GL_TRIANGLES, 0, matteBuffer.length());
glDrawArrays(GL_TRIANGLES, 0, matteBuffer.index());
glEnable(GL_STENCIL_TEST);
}
@@ -405,5 +405,5 @@ void Painter::renderBackground() {
// Draw the clipping mask
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
glUniform4fv(plainShader->u_color, 1, white.data());
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.length());
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
}