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

4
src/Text/Renderer.h

@ -115,12 +115,10 @@ class MAGNUM_TEXT_EXPORT AbstractRenderer {
#else
private:
#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();
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;
Buffer _vertexBuffer, _indexBuffer;
#ifdef CORRADE_TARGET_EMSCRIPTEN

Loading…
Cancel
Save