Browse Source

Text: de-duplicated rendering code in Renderer.

All three rendering functions (separated, interleaved with upload to GPU,
interleaved with mapping to GPU) are now using common code and then they
reorder the data to fit particular requirements. The data shuffling might
slow down things a bit, but it is optimized for the most common path (we
are rendering to interleaved array, which then get unpacked if needed,
not the other way around), so it shouldn't be an issue. No premature
optimization again, please.
pull/34/head
Vladimír Vondruš 13 years ago
parent
commit
b3f2a7900e
  1. 253
      src/Text/Renderer.cpp
  2. 4
      src/Text/Renderer.h

253
src/Text/Renderer.cpp

@ -54,45 +54,17 @@ template<class T> void createIndices(void* output, const UnsignedInt glyphCount)
} }
} }
Vector2 alignmentOffset(Rectangle& bounds, Alignment alignment) {
const Vector2 size = bounds.size();
const auto value = UnsignedByte(alignment);
/** @todo What about top-down text? */
Vector2 offset;
/* Horizontal alignment */
if((value & Implementation::AlignmentHorizontal) == Implementation::AlignmentCenter) offset -= Vector2::xAxis(size.x()*0.5f);
else if((value & Implementation::AlignmentHorizontal) == Implementation::AlignmentRight) offset -= Vector2::xAxis(size.x());
/* Vertical alignment */
if((value & Implementation::AlignmentVertical) == Implementation::AlignmentMiddle) offset -= Vector2::yAxis(size.y()*0.5f);
else if((value & Implementation::AlignmentVertical) == Implementation::AlignmentTop) offset -= Vector2::yAxis(size.y());
/* Integer alignment */
if(value & Implementation::AlignmentIntegral) offset = Math::round(offset);
/* Update also the bounds */
bounds.bottomLeft() += offset;
bounds.topRight() += offset;
return offset;
}
struct Vertex { struct Vertex {
Vector2 position, texcoords; Vector2 position, textureCoordinates;
}; };
} std::tuple<std::vector<Vertex>, Rectangle> renderVerticesInternal(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Alignment alignment) {
std::tuple<std::vector<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>, Rectangle> AbstractRenderer::render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Alignment alignment) {
const auto layouter = font.layout(cache, size, text); const auto layouter = font.layout(cache, size, text);
const UnsignedInt vertexCount = layouter->glyphCount()*4; const UnsignedInt vertexCount = layouter->glyphCount()*4;
/* Output data */ /* Output data */
std::vector<Vector2> positions, texcoords; std::vector<Vertex> vertices;
positions.reserve(vertexCount); vertices.reserve(vertexCount);
texcoords.reserve(vertexCount);
/* Rendered rectangle */ /* Rendered rectangle */
Rectangle rectangle; Rectangle rectangle;
@ -109,86 +81,82 @@ std::tuple<std::vector<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>,
| | | |
1---3 */ 1---3 */
positions.insert(positions.end(), { vertices.insert(vertices.end(), {
quadPosition.topLeft(), {quadPosition.topLeft(), textureCoordinates.topLeft()},
quadPosition.bottomLeft(), {quadPosition.bottomLeft(), textureCoordinates.bottomLeft()},
quadPosition.topRight(), {quadPosition.topRight(), textureCoordinates.topRight()},
quadPosition.bottomRight(), {quadPosition.bottomRight(), textureCoordinates.bottomRight()}
});
texcoords.insert(texcoords.end(), {
textureCoordinates.topLeft(),
textureCoordinates.bottomLeft(),
textureCoordinates.topRight(),
textureCoordinates.bottomRight()
}); });
} }
/* Respect the alignment */ /* Align the rendered mesh */
const Vector2 offset = alignmentOffset(rectangle, alignment); const Vector2 renderedSize = rectangle.size();
for(auto& p: positions) p += offset; Vector2 alignmentOffset;
/* Create indices */
std::vector<UnsignedInt> indices(layouter->glyphCount()*6);
createIndices<UnsignedInt>(indices.data(), layouter->glyphCount());
return std::make_tuple(std::move(positions), std::move(texcoords), std::move(indices), rectangle);
}
std::tuple<Mesh, Rectangle> AbstractRenderer::render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Buffer& vertexBuffer, Buffer& indexBuffer, Buffer::Usage usage, Alignment alignment) {
const auto layouter = font.layout(cache, size, text);
const UnsignedInt vertexCount = layouter->glyphCount()*4; /** @todo What about top-down text? */
const UnsignedInt indexCount = layouter->glyphCount()*6;
/* Vertex buffer */ /* Horizontal alignment */
std::vector<Vertex> vertices; if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentCenter)
vertices.reserve(vertexCount); alignmentOffset -= Vector2::xAxis(renderedSize.x()*0.5f);
else if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentRight)
alignmentOffset -= Vector2::xAxis(renderedSize.x());
/* Rendered rectangle */ /* Vertical alignment */
Rectangle rectangle; if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentMiddle)
alignmentOffset -= Vector2::yAxis(renderedSize.y()*0.5f);
else if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentTop)
alignmentOffset -= Vector2::yAxis(renderedSize.y());
/* Render all glyphs */ /* Integer alignment */
Vector2 cursorPosition; if(UnsignedByte(alignment) & Implementation::AlignmentIntegral)
for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { alignmentOffset = Math::round(alignmentOffset);
Rectangle quadPosition, textureCoordinates;
std::tie(quadPosition, textureCoordinates) = layouter->renderGlyph(i, cursorPosition, rectangle);
vertices.insert(vertices.end(), { /* Update positions and bounds */
{quadPosition.topLeft(), textureCoordinates.topLeft()}, rectangle = rectangle.translated(alignmentOffset);
{quadPosition.bottomLeft(), textureCoordinates.bottomLeft()}, for(auto& v: vertices) v.position += alignmentOffset;
{quadPosition.topRight(), textureCoordinates.topRight()},
{quadPosition.bottomRight(), textureCoordinates.bottomRight()}
});
}
/* Respect the alignment */ return std::make_tuple(std::move(vertices), rectangle);
const Vector2 offset = alignmentOffset(rectangle, alignment); }
for(auto& v: vertices) v.position += offset;
vertexBuffer.setData(vertices, usage); std::pair<Containers::Array<unsigned char>, Mesh::IndexType> renderIndicesInternal(const UnsignedInt glyphCount) {
const UnsignedInt vertexCount = glyphCount*4;
const UnsignedInt indexCount = glyphCount*6;
/* Fill index buffer */ Containers::Array<unsigned char> indices;
Mesh::IndexType indexType; Mesh::IndexType indexType;
std::size_t indicesSize;
char* indices;
if(vertexCount < 255) { if(vertexCount < 255) {
indexType = Mesh::IndexType::UnsignedByte; indexType = Mesh::IndexType::UnsignedByte;
indicesSize = indexCount*sizeof(UnsignedByte); indices = Containers::Array<unsigned char>(indexCount*sizeof(UnsignedByte));
indices = new char[indicesSize]; createIndices<UnsignedByte>(indices, glyphCount);
createIndices<UnsignedByte>(indices, layouter->glyphCount());
} else if(vertexCount < 65535) { } else if(vertexCount < 65535) {
indexType = Mesh::IndexType::UnsignedShort; indexType = Mesh::IndexType::UnsignedShort;
indicesSize = indexCount*sizeof(UnsignedShort); indices = Containers::Array<unsigned char>(indexCount*sizeof(UnsignedShort));
indices = new char[indicesSize]; createIndices<UnsignedShort>(indices, glyphCount);
createIndices<UnsignedShort>(indices, layouter->glyphCount());
} else { } else {
indexType = Mesh::IndexType::UnsignedInt; indexType = Mesh::IndexType::UnsignedInt;
indicesSize = indexCount*sizeof(UnsignedInt); indices = Containers::Array<unsigned char>(indexCount*sizeof(UnsignedInt));
indices = new char[indicesSize]; createIndices<UnsignedInt>(indices, glyphCount);
createIndices<UnsignedInt>(indices, layouter->glyphCount());
} }
indexBuffer.setData({indices, indicesSize}, usage);
delete indices; return {std::move(indices), indexType};
}
std::tuple<Mesh, Rectangle> renderInternal(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Buffer& vertexBuffer, Buffer& indexBuffer, Buffer::Usage usage, Alignment alignment) {
/* Render vertices and upload them */
std::vector<Vertex> vertices;
Rectangle rectangle;
std::tie(vertices, rectangle) = renderVerticesInternal(font, cache, size, text, alignment);
vertexBuffer.setData(vertices, usage);
const UnsignedInt glyphCount = vertices.size()/4;
const UnsignedInt vertexCount = glyphCount*4;
const UnsignedInt indexCount = glyphCount*6;
/* Render indices and upload them */
Containers::Array<unsigned char> indices;
Mesh::IndexType indexType;
std::tie(indices, indexType) = renderIndicesInternal(glyphCount);
indexBuffer.setData(indices, usage);
/* Configure mesh except for vertex buffer (depends on dimension count, done /* Configure mesh except for vertex buffer (depends on dimension count, done
in subclass) */ in subclass) */
@ -200,9 +168,34 @@ std::tuple<Mesh, Rectangle> AbstractRenderer::render(AbstractFont& font, const G
return std::make_tuple(std::move(mesh), rectangle); return std::make_tuple(std::move(mesh), rectangle);
} }
}
std::tuple<std::vector<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>, Rectangle> AbstractRenderer::render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Alignment alignment) {
/* Render vertices */
std::vector<Vertex> vertices;
Rectangle rectangle;
std::tie(vertices, rectangle) = renderVerticesInternal(font, cache, size, text, alignment);
/* Deinterleave the vertices */
std::vector<Vector2> positions, textureCoordinates;
positions.reserve(vertices.size());
positions.reserve(textureCoordinates.size());
for(const auto& v: vertices) {
positions.push_back(v.position);
textureCoordinates.push_back(v.textureCoordinates);
}
/* Render indices */
const UnsignedInt glyphCount = vertices.size()/4;
std::vector<UnsignedInt> indices(glyphCount*6);
createIndices<UnsignedInt>(indices.data(), glyphCount);
return std::make_tuple(std::move(positions), std::move(textureCoordinates), std::move(indices), rectangle);
}
template<UnsignedInt dimensions> std::tuple<Mesh, Rectangle> Renderer<dimensions>::render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Buffer& vertexBuffer, Buffer& indexBuffer, Buffer::Usage usage, Alignment alignment) { template<UnsignedInt dimensions> std::tuple<Mesh, Rectangle> Renderer<dimensions>::render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Buffer& vertexBuffer, Buffer& indexBuffer, Buffer::Usage usage, Alignment alignment) {
/* Finalize mesh configuration and return the result */ /* Finalize mesh configuration and return the result */
auto r = AbstractRenderer::render(font, cache, size, text, vertexBuffer, indexBuffer, usage, alignment); auto r = renderInternal(font, cache, size, text, vertexBuffer, indexBuffer, usage, alignment);
Mesh& mesh = std::get<0>(r); Mesh& mesh = std::get<0>(r);
mesh.addVertexBuffer(vertexBuffer, 0, mesh.addVertexBuffer(vertexBuffer, 0,
typename Shaders::AbstractVector<dimensions>::Position( typename Shaders::AbstractVector<dimensions>::Position(
@ -290,7 +283,6 @@ void AbstractRenderer::reserve(const uint32_t glyphCount, const Buffer::Usage ve
_capacity = glyphCount; _capacity = glyphCount;
const UnsignedInt vertexCount = glyphCount*4; const UnsignedInt vertexCount = glyphCount*4;
const UnsignedInt indexCount = glyphCount*6;
/* Allocate vertex buffer, reset vertex count */ /* Allocate vertex buffer, reset vertex count */
_vertexBuffer.setData({nullptr, vertexCount*sizeof(Vertex)}, vertexBufferUsage); _vertexBuffer.setData({nullptr, vertexCount*sizeof(Vertex)}, vertexBufferUsage);
@ -299,75 +291,48 @@ void AbstractRenderer::reserve(const uint32_t glyphCount, const Buffer::Usage ve
#endif #endif
_mesh.setVertexCount(0); _mesh.setVertexCount(0);
/* Allocate index buffer, reset index count and reconfigure buffer binding */ /* Render indices */
Containers::Array<unsigned char> indexData;
Mesh::IndexType indexType; Mesh::IndexType indexType;
std::size_t indicesSize; std::tie(indexData, indexType) = renderIndicesInternal(glyphCount);
if(vertexCount < 255) {
indexType = Mesh::IndexType::UnsignedByte; /* Allocate index buffer, reset index count and reconfigure buffer binding */
indicesSize = indexCount*sizeof(UnsignedByte); _indexBuffer.setData({nullptr, indexData.size()}, indexBufferUsage);
} else if(vertexCount < 65535) {
indexType = Mesh::IndexType::UnsignedShort;
indicesSize = indexCount*sizeof(UnsignedShort);
} else {
indexType = Mesh::IndexType::UnsignedInt;
indicesSize = indexCount*sizeof(UnsignedInt);
}
_indexBuffer.setData({nullptr, indicesSize}, indexBufferUsage);
#ifdef CORRADE_TARGET_EMSCRIPTEN #ifdef CORRADE_TARGET_EMSCRIPTEN
_indexBufferData = Containers::Array<UnsignedByte>(indicesSize); _indexBufferData = Containers::Array<UnsignedByte>(indicesSize);
#endif #endif
_mesh.setIndexCount(0) _mesh.setIndexCount(0)
.setIndexBuffer(_indexBuffer, 0, indexType, 0, vertexCount); .setIndexBuffer(_indexBuffer, 0, indexType, 0, vertexCount);
/* Map buffer for filling */
void* const indices = bufferMapImplementation(_indexBuffer, indicesSize);
CORRADE_INTERNAL_ASSERT(indices);
/* Prefill index buffer */ /* Prefill index buffer */
if(vertexCount < 255) unsigned char* const indices = static_cast<unsigned char*>(bufferMapImplementation(_indexBuffer, indexData.size()));
createIndices<UnsignedByte>(indices, glyphCount); CORRADE_INTERNAL_ASSERT(indices);
else if(vertexCount < 65535) std::copy(indexData.begin(), indexData.end(), indices);
createIndices<UnsignedShort>(indices, glyphCount);
else
createIndices<UnsignedInt>(indices, glyphCount);
bufferUnmapImplementation(_indexBuffer); bufferUnmapImplementation(_indexBuffer);
} }
void AbstractRenderer::render(const std::string& text) { void AbstractRenderer::render(const std::string& text) {
const auto layouter = font.layout(cache, size, text); /* Render vertex data */
std::vector<Vertex> vertexData;
_rectangle = {};
std::tie(vertexData, _rectangle) = renderVerticesInternal(font, cache, size, text, _alignment);
CORRADE_ASSERT(layouter->glyphCount() <= _capacity, const UnsignedInt glyphCount = vertexData.size()/4;
"Text::Renderer::render(): capacity" << _capacity << "too small to render" << layouter->glyphCount() << "glyphs", ); const UnsignedInt vertexCount = glyphCount*4;
const UnsignedInt indexCount = glyphCount*6;
/* Reset rendered rectangle */ CORRADE_ASSERT(glyphCount <= _capacity,
_rectangle = {}; "Text::Renderer::render(): capacity" << _capacity << "too small to render" << glyphCount << "glyphs", );
/* Map buffer for rendering */ /* Interleave the data into mapped buffer*/
Containers::ArrayReference<Vertex> vertices(static_cast<Vertex*>(bufferMapImplementation(_vertexBuffer, Containers::ArrayReference<Vertex> vertices(static_cast<Vertex*>(bufferMapImplementation(_vertexBuffer,
layouter->glyphCount()*4*sizeof(Vertex))), layouter->glyphCount()*4); vertexCount*sizeof(Vertex))), vertexCount);
CORRADE_INTERNAL_ASSERT_OUTPUT(vertices); CORRADE_INTERNAL_ASSERT_OUTPUT(vertices);
std::copy(vertexData.begin(), vertexData.end(), vertices.begin());
/* Render all glyphs */
Vector2 cursorPosition;
for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) {
Rectangle quadPosition, textureCoordinates;
std::tie(quadPosition, textureCoordinates) = layouter->renderGlyph(i, cursorPosition, _rectangle);
const std::size_t vertex = i*4;
vertices[vertex] = {quadPosition.topLeft(), textureCoordinates.topLeft()};
vertices[vertex+1] = {quadPosition.bottomLeft(), textureCoordinates.bottomLeft()};
vertices[vertex+2] = {quadPosition.topRight(), textureCoordinates.topRight()};
vertices[vertex+3] = {quadPosition.bottomRight(), textureCoordinates.bottomRight()};
}
/* Respect the alignment */
const Vector2 offset = alignmentOffset(_rectangle, _alignment);
for(auto& v: vertices) v.position += offset;
bufferUnmapImplementation(_vertexBuffer); bufferUnmapImplementation(_vertexBuffer);
/* Update index count */ /* Update index count */
_mesh.setIndexCount(layouter->glyphCount()*6); _mesh.setIndexCount(indexCount);
} }
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT

4
src/Text/Renderer.h

@ -115,12 +115,10 @@ class MAGNUM_TEXT_EXPORT AbstractRenderer {
#else #else
private: private:
#endif #endif
explicit MAGNUM_LOCAL AbstractRenderer(AbstractFont& font, const GlyphCache& cache, Float size, Alignment alignment); explicit MAGNUM_TEXT_LOCAL AbstractRenderer(AbstractFont& font, const GlyphCache& cache, Float size, Alignment alignment);
~AbstractRenderer(); ~AbstractRenderer();
static std::tuple<Mesh, Rectangle> MAGNUM_LOCAL render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Buffer& vertexBuffer, Buffer& indexBuffer, Buffer::Usage usage, Alignment alignment);
Mesh _mesh; Mesh _mesh;
Buffer _vertexBuffer, _indexBuffer; Buffer _vertexBuffer, _indexBuffer;
#ifdef CORRADE_TARGET_EMSCRIPTEN #ifdef CORRADE_TARGET_EMSCRIPTEN

Loading…
Cancel
Save