From b3f2a7900ea201d9d13d0deecdc6441baed9386a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 9 Nov 2013 18:29:44 +0100 Subject: [PATCH] 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. --- src/Text/Renderer.cpp | 253 ++++++++++++++++++------------------------ src/Text/Renderer.h | 4 +- 2 files changed, 110 insertions(+), 147 deletions(-) diff --git a/src/Text/Renderer.cpp b/src/Text/Renderer.cpp index bb7fdacd4..5aa464e0d 100644 --- a/src/Text/Renderer.cpp +++ b/src/Text/Renderer.cpp @@ -54,45 +54,17 @@ template 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, std::vector, Rectangle> AbstractRenderer::render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Alignment alignment) { +std::tuple, 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 positions, texcoords; - positions.reserve(vertexCount); - texcoords.reserve(vertexCount); + std::vector vertices; + vertices.reserve(vertexCount); /* Rendered rectangle */ Rectangle rectangle; @@ -109,86 +81,82 @@ std::tuple, std::vector, std::vector, | | 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 indices(layouter->glyphCount()*6); - createIndices(indices.data(), layouter->glyphCount()); - - return std::make_tuple(std::move(positions), std::move(texcoords), std::move(indices), rectangle); -} - -std::tuple 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 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, Mesh::IndexType> renderIndicesInternal(const UnsignedInt glyphCount) { + const UnsignedInt vertexCount = glyphCount*4; + const UnsignedInt indexCount = glyphCount*6; - /* Fill index buffer */ + Containers::Array 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(indices, layouter->glyphCount()); + indices = Containers::Array(indexCount*sizeof(UnsignedByte)); + createIndices(indices, glyphCount); } else if(vertexCount < 65535) { indexType = Mesh::IndexType::UnsignedShort; - indicesSize = indexCount*sizeof(UnsignedShort); - indices = new char[indicesSize]; - createIndices(indices, layouter->glyphCount()); + indices = Containers::Array(indexCount*sizeof(UnsignedShort)); + createIndices(indices, glyphCount); } else { indexType = Mesh::IndexType::UnsignedInt; - indicesSize = indexCount*sizeof(UnsignedInt); - indices = new char[indicesSize]; - createIndices(indices, layouter->glyphCount()); + indices = Containers::Array(indexCount*sizeof(UnsignedInt)); + createIndices(indices, glyphCount); } - indexBuffer.setData({indices, indicesSize}, usage); - delete indices; + + return {std::move(indices), indexType}; +} + +std::tuple 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 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 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 AbstractRenderer::render(AbstractFont& font, const G return std::make_tuple(std::move(mesh), rectangle); } +} + +std::tuple, std::vector, std::vector, Rectangle> AbstractRenderer::render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, Alignment alignment) { + /* Render vertices */ + std::vector vertices; + Rectangle rectangle; + std::tie(vertices, rectangle) = renderVerticesInternal(font, cache, size, text, alignment); + + /* Deinterleave the vertices */ + std::vector 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 indices(glyphCount*6); + createIndices(indices.data(), glyphCount); + + return std::make_tuple(std::move(positions), std::move(textureCoordinates), std::move(indices), rectangle); +} + template std::tuple Renderer::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::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 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(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(indices, glyphCount); - else if(vertexCount < 65535) - createIndices(indices, glyphCount); - else - createIndices(indices, glyphCount); + unsigned char* const indices = static_cast(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 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 vertices(static_cast(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 diff --git a/src/Text/Renderer.h b/src/Text/Renderer.h index 36da61e0d..a74740d85 100644 --- a/src/Text/Renderer.h +++ b/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 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