diff --git a/engine/src/flutter/impeller/entity/contents/clip_contents.cc b/engine/src/flutter/impeller/entity/contents/clip_contents.cc index 1914b833530..bbe6c61aebf 100644 --- a/engine/src/flutter/impeller/entity/contents/clip_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/clip_contents.cc @@ -119,8 +119,8 @@ bool ClipContents::Render(const ContentContext& renderer, } cmd.pipeline = renderer.GetClipPipeline(options); - cmd.BindVertices(CreateSolidFillVertices( - renderer.GetTessellator(), path_, pass.GetTransientsBuffer())); + cmd.BindVertices(CreateSolidFillVertices(renderer.GetTessellator(), path_, + pass.GetTransientsBuffer())); info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * entity.GetTransformation(); diff --git a/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc b/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc index 94de5fc8dd4..d09692aecf4 100644 --- a/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc @@ -77,7 +77,7 @@ bool LinearGradientContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetLinearGradientFillPipeline( OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(CreateSolidFillVertices( + cmd.BindVertices(CreateSolidFillVertices( renderer.GetTessellator(), GetCover() ? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath() diff --git a/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc b/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc index 4ea83c93bae..8cec68be61c 100644 --- a/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc @@ -76,7 +76,7 @@ bool RadialGradientContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetRadialGradientFillPipeline( OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(CreateSolidFillVertices( + cmd.BindVertices(CreateSolidFillVertices( renderer.GetTessellator(), GetCover() ? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath() diff --git a/engine/src/flutter/impeller/entity/contents/solid_color_contents.cc b/engine/src/flutter/impeller/entity/contents/solid_color_contents.cc index 57f022e15bb..96c24727c69 100644 --- a/engine/src/flutter/impeller/entity/contents/solid_color_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/solid_color_contents.cc @@ -62,7 +62,7 @@ bool SolidColorContents::Render(const ContentContext& renderer, renderer.GetSolidFillPipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(CreateSolidFillVertices( + cmd.BindVertices(CreateSolidFillVertices( renderer.GetTessellator(), cover_ ? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath() diff --git a/engine/src/flutter/impeller/entity/contents/solid_fill_utils.cc b/engine/src/flutter/impeller/entity/contents/solid_fill_utils.cc index 13c0684127b..e61e3a55e46 100644 --- a/engine/src/flutter/impeller/entity/contents/solid_fill_utils.cc +++ b/engine/src/flutter/impeller/entity/contents/solid_fill_utils.cc @@ -4,8 +4,33 @@ #include "impeller/entity/contents/solid_fill_utils.h" +#include "impeller/geometry/path.h" +#include "impeller/renderer/device_buffer.h" +#include "impeller/renderer/host_buffer.h" +#include "impeller/tessellator/tessellator.h" + namespace impeller { -// +VertexBuffer CreateSolidFillVertices(std::shared_ptr tessellator, + const Path& path, + HostBuffer& buffer) { + VertexBuffer vertex_buffer; + auto tesselation_result = tessellator->TessellateBuilder( + path.GetFillType(), path.CreatePolyline(), + [&vertex_buffer, &buffer](const float* vertices, size_t vertices_count, + const uint16_t* indices, size_t indices_count) { + vertex_buffer.vertex_buffer = buffer.Emplace( + vertices, vertices_count * sizeof(float), alignof(float)); + vertex_buffer.index_buffer = buffer.Emplace( + indices, indices_count * sizeof(uint16_t), alignof(uint16_t)); + vertex_buffer.index_count = indices_count; + vertex_buffer.index_type = IndexType::k16bit; + return true; + }); + if (tesselation_result != Tessellator::Result::kSuccess) { + return {}; + } + return vertex_buffer; +} } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/solid_fill_utils.h b/engine/src/flutter/impeller/entity/contents/solid_fill_utils.h index 6b5fb427398..3e55f605adc 100644 --- a/engine/src/flutter/impeller/entity/contents/solid_fill_utils.h +++ b/engine/src/flutter/impeller/entity/contents/solid_fill_utils.h @@ -3,26 +3,26 @@ // found in the LICENSE file. #pragma once - -#include "impeller/renderer/vertex_buffer_builder.h" -#include "impeller/tessellator/tessellator.h" +#include "impeller/renderer/vertex_buffer.h" namespace impeller { -template +class Tessellator; +class Path; +class HostBuffer; + +/** + * @brief Populate a VertexBuffer with solid fill vertices created by + * tessellating an input path. + * + * @param tessellator The tessellator + * @param path The path to be tessellated + * @param buffer The transient buffer + * @return VertexBuffer A populated vertex buffer if successful, otherwise + * empty. + */ VertexBuffer CreateSolidFillVertices(std::shared_ptr tessellator, const Path& path, - HostBuffer& buffer) { - VertexBufferBuilder vtx_builder; - - auto tesselation_result = tessellator->Tessellate( - path.GetFillType(), path.CreatePolyline(), - [&vtx_builder](auto point) { vtx_builder.AppendVertex({point}); }); - if (tesselation_result != Tessellator::Result::kSuccess) { - return {}; - } - - return vtx_builder.CreateVertexBuffer(buffer); -} + HostBuffer& buffer); } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc b/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc index fc6bf4aac48..a55d497c3b5 100644 --- a/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc @@ -83,7 +83,7 @@ bool SweepGradientContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetSweepGradientFillPipeline( OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(CreateSolidFillVertices( + cmd.BindVertices(CreateSolidFillVertices( renderer.GetTessellator(), GetCover() ? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath() diff --git a/engine/src/flutter/impeller/entity/contents/tiled_texture_contents.cc b/engine/src/flutter/impeller/entity/contents/tiled_texture_contents.cc index ab4286d61b6..c9de3101ee6 100644 --- a/engine/src/flutter/impeller/entity/contents/tiled_texture_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/tiled_texture_contents.cc @@ -67,12 +67,12 @@ bool TiledTextureContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetTiledTexturePipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(CreateSolidFillVertices( + cmd.BindVertices(CreateSolidFillVertices( renderer.GetTessellator(), GetCover() ? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath() : GetPath(), - pass.GetTransientsBuffer())); + host_buffer)); VS::BindVertInfo(cmd, host_buffer.EmplaceUniform(vert_info)); FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info)); FS::BindTextureSampler(cmd, texture_, diff --git a/engine/src/flutter/impeller/tessellator/tessellator.cc b/engine/src/flutter/impeller/tessellator/tessellator.cc index 603532d095c..72732310d77 100644 --- a/engine/src/flutter/impeller/tessellator/tessellator.cc +++ b/engine/src/flutter/impeller/tessellator/tessellator.cc @@ -128,6 +128,76 @@ Tessellator::Result Tessellator::Tessellate( return Result::kSuccess; } +Tessellator::Result Tessellator::TessellateBuilder( + FillType fill_type, + const Path::Polyline& polyline, + const BuilderCallback& callback) const { + if (!callback) { + return Result::kInputError; + } + + if (polyline.points.empty()) { + return Result::kInputError; + } + + auto tessellator = c_tessellator_.get(); + if (!tessellator) { + return Result::kTessellationError; + } + + constexpr int kVertexSize = 2; + constexpr int kPolygonSize = 3; + + //---------------------------------------------------------------------------- + /// Feed contour information to the tessellator. + /// + static_assert(sizeof(Point) == 2 * sizeof(float)); + for (size_t contour_i = 0; contour_i < polyline.contours.size(); + contour_i++) { + size_t start_point_index, end_point_index; + std::tie(start_point_index, end_point_index) = + polyline.GetContourPointBounds(contour_i); + + ::tessAddContour(tessellator, // the C tessellator + kVertexSize, // + polyline.points.data() + start_point_index, // + sizeof(Point), // + end_point_index - start_point_index // + ); + } + + //---------------------------------------------------------------------------- + /// Let's tessellate. + /// + auto result = ::tessTesselate(tessellator, // tessellator + ToTessWindingRule(fill_type), // winding + TESS_POLYGONS, // element type + kPolygonSize, // polygon size + kVertexSize, // vertex size + nullptr // normal (null is automatic) + ); + + if (result != 1) { + return Result::kTessellationError; + } + + int vertexItemCount = tessGetVertexCount(tessellator) * kVertexSize; + auto vertices = tessGetVertices(tessellator); + int elementItemCount = tessGetElementCount(tessellator) * kPolygonSize; + auto elements = tessGetElements(tessellator); + // libtess uses an int index internally due to usage of -1 as a sentinel + // value. + std::vector indices(elementItemCount); + for (int i = 0; i < elementItemCount; i++) { + indices[i] = static_cast(elements[i]); + } + if (!callback(vertices, vertexItemCount, indices.data(), elementItemCount)) { + return Result::kInputError; + } + + return Result::kSuccess; +} + void DestroyTessellator(TESStesselator* tessellator) { if (tessellator != nullptr) { ::tessDeleteTess(tessellator); diff --git a/engine/src/flutter/impeller/tessellator/tessellator.h b/engine/src/flutter/impeller/tessellator/tessellator.h index 7a2fded0e8e..de2b8be1682 100644 --- a/engine/src/flutter/impeller/tessellator/tessellator.h +++ b/engine/src/flutter/impeller/tessellator/tessellator.h @@ -44,6 +44,11 @@ class Tessellator { ~Tessellator(); using VertexCallback = std::function; + using BuilderCallback = std::function; + //---------------------------------------------------------------------------- /// @brief Generates filled triangles from the polyline. A callback is /// invoked for each vertex of the triangle. @@ -58,6 +63,20 @@ class Tessellator { const Path::Polyline& polyline, const VertexCallback& callback) const; + //---------------------------------------------------------------------------- + /// @brief Generates filled triangles from the polyline. A callback is + /// invoked once for the entire tessellation. + /// + /// @param[in] fill_type The fill rule to use when filling. + /// @param[in] polyline The polyline + /// @param[in] callback The callback, return false to indicate failure. + /// + /// @return The result status of the tessellation. + /// + Tessellator::Result TessellateBuilder(FillType fill_type, + const Path::Polyline& polyline, + const BuilderCallback& callback) const; + private: CTessellator c_tessellator_; diff --git a/engine/src/flutter/impeller/tessellator/tessellator_unittests.cc b/engine/src/flutter/impeller/tessellator/tessellator_unittests.cc index f9184b6805a..24af4306c4f 100644 --- a/engine/src/flutter/impeller/tessellator/tessellator_unittests.cc +++ b/engine/src/flutter/impeller/tessellator/tessellator_unittests.cc @@ -62,5 +62,78 @@ TEST(TessellatorTest, TessellatorReturnsCorrectResultStatus) { } } +TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) { + // Zero points. + { + Tessellator t; + auto polyline = PathBuilder{}.TakePath().CreatePolyline(); + Tessellator::Result result = t.TessellateBuilder( + FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_size, const uint16_t* indices, + size_t indices_size) { return true; }); + + ASSERT_EQ(polyline.points.size(), 0u); + ASSERT_EQ(result, Tessellator::Result::kInputError); + } + + // One point. + { + Tessellator t; + auto polyline = PathBuilder{}.LineTo({0, 0}).TakePath().CreatePolyline(); + Tessellator::Result result = t.TessellateBuilder( + FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_size, const uint16_t* indices, + size_t indices_size) { return true; }); + ASSERT_EQ(polyline.points.size(), 1u); + ASSERT_EQ(result, Tessellator::Result::kSuccess); + } + + // Two points. + { + Tessellator t; + auto polyline = + PathBuilder{}.AddLine({0, 0}, {0, 1}).TakePath().CreatePolyline(); + Tessellator::Result result = t.TessellateBuilder( + FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_size, const uint16_t* indices, + size_t indices_size) { return true; }); + + ASSERT_EQ(polyline.points.size(), 2u); + ASSERT_EQ(result, Tessellator::Result::kSuccess); + } + + // Many points. + { + Tessellator t; + PathBuilder builder; + for (int i = 0; i < 1000; i++) { + auto coord = i * 1.0f; + builder.AddLine({coord, coord}, {coord + 1, coord + 1}); + } + auto polyline = builder.TakePath().CreatePolyline(); + Tessellator::Result result = t.TessellateBuilder( + FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_size, const uint16_t* indices, + size_t indices_size) { return true; }); + + ASSERT_EQ(polyline.points.size(), 2000u); + ASSERT_EQ(result, Tessellator::Result::kSuccess); + } + + // Closure fails. + { + Tessellator t; + auto polyline = + PathBuilder{}.AddLine({0, 0}, {0, 1}).TakePath().CreatePolyline(); + Tessellator::Result result = t.TessellateBuilder( + FillType::kPositive, polyline, + [](const float* vertices, size_t vertices_size, const uint16_t* indices, + size_t indices_size) { return false; }); + + ASSERT_EQ(polyline.points.size(), 2u); + ASSERT_EQ(result, Tessellator::Result::kInputError); + } +} + } // namespace testing } // namespace impeller