diff --git a/engine/src/flutter/impeller/entity/contents/text_contents.cc b/engine/src/flutter/impeller/entity/contents/text_contents.cc index a2a43aaca4..3e37ea5a6e 100644 --- a/engine/src/flutter/impeller/entity/contents/text_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/text_contents.cc @@ -108,8 +108,7 @@ void TextContents::ComputeVertexData( // interpolated vertex information is also used in the fragment shader to // sample from the glyph atlas. - constexpr std::array unit_points = {Point{0, 0}, Point{1, 0}, - Point{0, 1}, Point{1, 0}, + constexpr std::array unit_points = {Point{0, 0}, Point{1, 0}, Point{0, 1}, Point{1, 1}}; ISize atlas_size = atlas->GetTexture()->GetSize(); @@ -119,9 +118,19 @@ void TextContents::ComputeVertexData( VS::PerVertexData vtx; size_t i = 0u; size_t bounds_offset = 0u; + Rational rounded_scale = frame->GetScale(); + Scalar inverted_rounded_scale = static_cast(rounded_scale.Invert()); + Matrix unscaled_basis = + basis_transform * + Matrix::MakeScale({inverted_rounded_scale, inverted_rounded_scale, 1}); + + // In typical scales < 48x these values should be -1 or 1. We round to + // those to avoid inaccuracies. + unscaled_basis.m[0] = AttractToOne(unscaled_basis.m[0]); + unscaled_basis.m[5] = AttractToOne(unscaled_basis.m[5]); + for (const TextRun& run : frame->GetRuns()) { const Font& font = run.GetFont(); - Rational rounded_scale = frame->GetScale(); const Matrix transform = frame->GetOffsetTransform(); FontGlyphAtlas* font_atlas = nullptr; @@ -181,26 +190,15 @@ void TextContents::ComputeVertexData( atlas_glyph_bounds = maybe_atlas_glyph_bounds.value().atlas_bounds; } - Scalar inverted_rounded_scale = - static_cast(rounded_scale.Invert()); Rect scaled_bounds = glyph_bounds.Scale(inverted_rounded_scale); // For each glyph, we compute two rectangles. One for the vertex // positions and one for the texture coordinates (UVs). The atlas // glyph bounds are used to compute UVs in cases where the // destination and source sizes may differ due to clamping the sizes // of large glyphs. - Point uv_origin = (atlas_glyph_bounds.GetLeftTop()) / atlas_size; + Point uv_origin = atlas_glyph_bounds.GetLeftTop() / atlas_size; Point uv_size = SizeToPoint(atlas_glyph_bounds.GetSize()) / atlas_size; - Matrix unscaled_basis = - basis_transform * Matrix::MakeScale({inverted_rounded_scale, - inverted_rounded_scale, 1}); - - // In typical scales < 48x these values should be -1 or 1. We round to - // those to avoid inaccuracies. - unscaled_basis.m[0] = AttractToOne(unscaled_basis.m[0]); - unscaled_basis.m[5] = AttractToOne(unscaled_basis.m[5]); - Point unrounded_glyph_position = // This is for RTL text. unscaled_basis * glyph_bounds.GetLeftTop() + @@ -231,12 +229,12 @@ void TextContents::ComputeVertexData( bool TextContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { - auto color = GetColor(); + Color color = GetColor(); if (color.IsTransparent()) { return true; } - auto type = frame_->GetAtlasType(); + GlyphAtlas::Type type = frame_->GetAtlasType(); const std::shared_ptr& atlas = renderer.GetLazyGlyphAtlas()->CreateOrGetGlyphAtlas( *renderer.GetContext(), renderer.GetTransientsBuffer(), type); @@ -298,26 +296,45 @@ bool TextContents::Render(const ContentContext& renderer, sampler_desc) // sampler ); - auto& host_buffer = renderer.GetTransientsBuffer(); - size_t vertex_count = 0; + HostBuffer& host_buffer = renderer.GetTransientsBuffer(); + size_t glyph_count = 0; for (const auto& run : frame_->GetRuns()) { - vertex_count += run.GetGlyphPositions().size(); + glyph_count += run.GetGlyphPositions().size(); } - vertex_count *= 6; + size_t vertex_count = glyph_count * 4; + size_t index_count = glyph_count * 6; BufferView buffer_view = host_buffer.Emplace( vertex_count * sizeof(VS::PerVertexData), alignof(VS::PerVertexData), - [&](uint8_t* contents) { + [&](uint8_t* data) { VS::PerVertexData* vtx_contents = - reinterpret_cast(contents); - ComputeVertexData(vtx_contents, frame_, scale_, - /*entity_transform=*/entity_transform, offset_, - GetGlyphProperties(), atlas); + reinterpret_cast(data); + ComputeVertexData(/*vtx_contents=*/vtx_contents, + /*frame=*/frame_, + /*scale=*/scale_, + /*entity_transform=*/entity_transform, + /*offset=*/offset_, + /*glyph_properties=*/GetGlyphProperties(), + /*atlas=*/atlas); + }); + BufferView index_buffer_view = host_buffer.Emplace( + index_count * sizeof(uint16_t), alignof(uint16_t), [&](uint8_t* data) { + uint16_t* indices = reinterpret_cast(data); + size_t j = 0; + for (auto i = 0u; i < glyph_count; i++) { + size_t base = i * 4; + indices[j++] = base + 0; + indices[j++] = base + 1; + indices[j++] = base + 2; + indices[j++] = base + 1; + indices[j++] = base + 2; + indices[j++] = base + 3; + } }); pass.SetVertexBuffer(std::move(buffer_view)); - pass.SetIndexBuffer({}, IndexType::kNone); - pass.SetElementCount(vertex_count); + pass.SetIndexBuffer(index_buffer_view, IndexType::k16bit); + pass.SetElementCount(index_count); return pass.Draw().ok(); } diff --git a/engine/src/flutter/impeller/entity/contents/text_contents_unittests.cc b/engine/src/flutter/impeller/entity/contents/text_contents_unittests.cc index 54503f135f..44f745017a 100644 --- a/engine/src/flutter/impeller/entity/contents/text_contents_unittests.cc +++ b/engine/src/flutter/impeller/entity/contents/text_contents_unittests.cc @@ -69,12 +69,13 @@ std::shared_ptr CreateGlyphAtlas( } Rect PerVertexDataPositionToRect( - GlyphAtlasPipeline::VertexShader::PerVertexData data[6]) { + std::vector::iterator + data) { Scalar right = FLT_MIN; Scalar left = FLT_MAX; Scalar top = FLT_MAX; Scalar bottom = FLT_MIN; - for (int i = 0; i < 6; ++i) { + for (int i = 0; i < 4; ++i) { right = std::max(right, data[i].position.x); left = std::min(left, data[i].position.x); top = std::min(top, data[i].position.y); @@ -85,13 +86,13 @@ Rect PerVertexDataPositionToRect( } Rect PerVertexDataUVToRect( - GlyphAtlasPipeline::VertexShader::PerVertexData data[6], + std::vector::iterator data, ISize texture_size) { Scalar right = FLT_MIN; Scalar left = FLT_MAX; Scalar top = FLT_MAX; Scalar bottom = FLT_MIN; - for (int i = 0; i < 6; ++i) { + for (int i = 0; i < 4; ++i) { right = std::max(right, data[i].uv.x * texture_size.width); left = std::min(left, data[i].uv.x * texture_size.width); top = std::min(top, data[i].uv.y * texture_size.height); @@ -111,7 +112,7 @@ TEST_P(TextContentsTest, SimpleComputeVertexData) { GTEST_SKIP() << "Results aren't stable across linux and macos."; #endif - GlyphAtlasPipeline::VertexShader::PerVertexData data[6]; + std::vector data(4); std::shared_ptr text_frame = MakeTextFrame("1", "ahem.ttf", TextOptions{.font_size = 50}); @@ -128,13 +129,13 @@ TEST_P(TextContentsTest, SimpleComputeVertexData) { atlas_context, text_frame, /*offset=*/{0, 0}); ISize texture_size = atlas->GetTexture()->GetSize(); - TextContents::ComputeVertexData(data, text_frame, /*scale=*/1.0, + TextContents::ComputeVertexData(data.data(), text_frame, /*scale=*/1.0, /*entity_transform=*/Matrix(), /*offset=*/Vector2(0, 0), /*glyph_properties=*/std::nullopt, atlas); - Rect position_rect = PerVertexDataPositionToRect(data); - Rect uv_rect = PerVertexDataUVToRect(data, texture_size); + Rect position_rect = PerVertexDataPositionToRect(data.begin()); + Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size); // The -1 offset comes from Skia in `ComputeGlyphSize`. So since the font size // is 50, the math appears to be to get back a 50x50 rect and apply 1 pixel // of padding. @@ -147,8 +148,7 @@ TEST_P(TextContentsTest, SimpleComputeVertexData2x) { GTEST_SKIP() << "Results aren't stable across linux and macos."; #endif - GlyphAtlasPipeline::VertexShader::PerVertexData data[6]; - + std::vector data(4); std::shared_ptr text_frame = MakeTextFrame("1", "ahem.ttf", TextOptions{.font_size = 50}); @@ -166,15 +166,15 @@ TEST_P(TextContentsTest, SimpleComputeVertexData2x) { ISize texture_size = atlas->GetTexture()->GetSize(); TextContents::ComputeVertexData( - data, text_frame, static_cast(font_scale), + data.data(), text_frame, static_cast(font_scale), /*entity_transform=*/ Matrix::MakeScale({static_cast(font_scale), static_cast(font_scale), 1}), /*offset=*/Vector2(0, 0), /*glyph_properties=*/std::nullopt, atlas); - Rect position_rect = PerVertexDataPositionToRect(data); - Rect uv_rect = PerVertexDataUVToRect(data, texture_size); + Rect position_rect = PerVertexDataPositionToRect(data.begin()); + Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size); EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -81, 102, 102)); EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 102, 102)); } @@ -196,7 +196,8 @@ TEST_P(TextContentsTest, MaintainsShape) { Rect uv_rect[2]; { - GlyphAtlasPipeline::VertexShader::PerVertexData data[12]; + std::vector data(12); + std::shared_ptr atlas = CreateGlyphAtlas(*GetContext(), context.get(), *host_buffer, GlyphAtlas::Type::kAlphaBitmap, font_scale, @@ -204,16 +205,16 @@ TEST_P(TextContentsTest, MaintainsShape) { ISize texture_size = atlas->GetTexture()->GetSize(); TextContents::ComputeVertexData( - data, text_frame, static_cast(font_scale), + data.data(), text_frame, static_cast(font_scale), /*entity_transform=*/ Matrix::MakeScale({static_cast(font_scale), static_cast(font_scale), 1}), /*offset=*/Vector2(0, 0), /*glyph_properties=*/std::nullopt, atlas); - position_rect[0] = PerVertexDataPositionToRect(data); - uv_rect[0] = PerVertexDataUVToRect(data, texture_size); - position_rect[1] = PerVertexDataPositionToRect(data + 6); - uv_rect[1] = PerVertexDataUVToRect(data + 6, texture_size); + position_rect[0] = PerVertexDataPositionToRect(data.begin()); + uv_rect[0] = PerVertexDataUVToRect(data.begin(), texture_size); + position_rect[1] = PerVertexDataPositionToRect(data.begin() + 4); + uv_rect[1] = PerVertexDataUVToRect(data.begin() + 4, texture_size); } EXPECT_NEAR(GetAspectRatio(position_rect[1]), GetAspectRatio(uv_rect[1]), 0.001) @@ -226,7 +227,7 @@ TEST_P(TextContentsTest, SimpleSubpixel) { GTEST_SKIP() << "Results aren't stable across linux and macos."; #endif - GlyphAtlasPipeline::VertexShader::PerVertexData data[6]; + std::vector data(4); std::shared_ptr text_frame = MakeTextFrame( "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true}); @@ -245,12 +246,12 @@ TEST_P(TextContentsTest, SimpleSubpixel) { ISize texture_size = atlas->GetTexture()->GetSize(); TextContents::ComputeVertexData( - data, text_frame, /*scale=*/1.0, + data.data(), text_frame, /*scale=*/1.0, /*entity_transform=*/Matrix::MakeTranslation(offset), offset, /*glyph_properties=*/std::nullopt, atlas); - Rect position_rect = PerVertexDataPositionToRect(data); - Rect uv_rect = PerVertexDataUVToRect(data, texture_size); + Rect position_rect = PerVertexDataPositionToRect(data.begin()); + Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size); // The values at Point(0, 0). // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52)); // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52)); @@ -263,7 +264,7 @@ TEST_P(TextContentsTest, SimpleSubpixel3x) { GTEST_SKIP() << "Results aren't stable across linux and macos."; #endif - GlyphAtlasPipeline::VertexShader::PerVertexData data[6]; + std::vector data(4); std::shared_ptr text_frame = MakeTextFrame( "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true}); @@ -283,7 +284,7 @@ TEST_P(TextContentsTest, SimpleSubpixel3x) { ISize texture_size = atlas->GetTexture()->GetSize(); TextContents::ComputeVertexData( - data, text_frame, static_cast(font_scale), + data.data(), text_frame, static_cast(font_scale), /*entity_transform=*/ Matrix::MakeTranslation(offset) * Matrix::MakeScale({static_cast(font_scale), @@ -291,8 +292,8 @@ TEST_P(TextContentsTest, SimpleSubpixel3x) { offset, /*glyph_properties=*/std::nullopt, atlas); - Rect position_rect = PerVertexDataPositionToRect(data); - Rect uv_rect = PerVertexDataUVToRect(data, texture_size); + Rect position_rect = PerVertexDataPositionToRect(data.begin()); + Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size); // Values at Point(0, 0) // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -121, 152, 152)); // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 152, 152)); @@ -307,7 +308,7 @@ TEST_P(TextContentsTest, SimpleSubpixel26) { GTEST_SKIP() << "Results aren't stable across linux and macos."; #endif - GlyphAtlasPipeline::VertexShader::PerVertexData data[6]; + std::vector data(4); std::shared_ptr text_frame = MakeTextFrame( "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true}); @@ -326,12 +327,12 @@ TEST_P(TextContentsTest, SimpleSubpixel26) { ISize texture_size = atlas->GetTexture()->GetSize(); TextContents::ComputeVertexData( - data, text_frame, /*scale=*/1.0, + data.data(), text_frame, /*scale=*/1.0, /*entity_transform=*/Matrix::MakeTranslation(offset), offset, /*glyph_properties=*/std::nullopt, atlas); - Rect position_rect = PerVertexDataPositionToRect(data); - Rect uv_rect = PerVertexDataUVToRect(data, texture_size); + Rect position_rect = PerVertexDataPositionToRect(data.begin()); + Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size); // The values without subpixel. // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52)); // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52)); @@ -344,7 +345,7 @@ TEST_P(TextContentsTest, SimpleSubpixel80) { GTEST_SKIP() << "Results aren't stable across linux and macos."; #endif - GlyphAtlasPipeline::VertexShader::PerVertexData data[6]; + std::vector data(4); std::shared_ptr text_frame = MakeTextFrame( "1", "ahem.ttf", TextOptions{.font_size = 50, .is_subpixel = true}); @@ -363,12 +364,12 @@ TEST_P(TextContentsTest, SimpleSubpixel80) { ISize texture_size = atlas->GetTexture()->GetSize(); TextContents::ComputeVertexData( - data, text_frame, /*scale=*/1.0, + data.data(), text_frame, /*scale=*/1.0, /*entity_transform=*/Matrix::MakeTranslation(offset), offset, /*glyph_properties=*/std::nullopt, atlas); - Rect position_rect = PerVertexDataPositionToRect(data); - Rect uv_rect = PerVertexDataUVToRect(data, texture_size); + Rect position_rect = PerVertexDataPositionToRect(data.begin()); + Rect uv_rect = PerVertexDataUVToRect(data.begin(), texture_size); // The values without subpixel. // EXPECT_RECT_NEAR(position_rect, Rect::MakeXYWH(-1, -41, 52, 52)); // EXPECT_RECT_NEAR(uv_rect, Rect::MakeXYWH(1.0, 1.0, 52, 52));