mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Avoid extra copies when using CreateSolidFillVertices (flutter/engine#36555)
This commit is contained in:
parent
db5fd760a9
commit
0e7ce79e9c
@ -119,8 +119,8 @@ bool ClipContents::Render(const ContentContext& renderer,
|
||||
}
|
||||
|
||||
cmd.pipeline = renderer.GetClipPipeline(options);
|
||||
cmd.BindVertices(CreateSolidFillVertices<VS::PerVertexData>(
|
||||
renderer.GetTessellator(), path_, pass.GetTransientsBuffer()));
|
||||
cmd.BindVertices(CreateSolidFillVertices(renderer.GetTessellator(), path_,
|
||||
pass.GetTransientsBuffer()));
|
||||
|
||||
info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
|
||||
entity.GetTransformation();
|
||||
|
||||
@ -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<VS::PerVertexData>(
|
||||
cmd.BindVertices(CreateSolidFillVertices(
|
||||
renderer.GetTessellator(),
|
||||
GetCover()
|
||||
? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath()
|
||||
|
||||
@ -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<VS::PerVertexData>(
|
||||
cmd.BindVertices(CreateSolidFillVertices(
|
||||
renderer.GetTessellator(),
|
||||
GetCover()
|
||||
? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath()
|
||||
|
||||
@ -62,7 +62,7 @@ bool SolidColorContents::Render(const ContentContext& renderer,
|
||||
renderer.GetSolidFillPipeline(OptionsFromPassAndEntity(pass, entity));
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
|
||||
cmd.BindVertices(CreateSolidFillVertices<VS::PerVertexData>(
|
||||
cmd.BindVertices(CreateSolidFillVertices(
|
||||
renderer.GetTessellator(),
|
||||
cover_
|
||||
? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath()
|
||||
|
||||
@ -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> 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
|
||||
|
||||
@ -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 <typename PerVertexData>
|
||||
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> tessellator,
|
||||
const Path& path,
|
||||
HostBuffer& buffer) {
|
||||
VertexBufferBuilder<PerVertexData> 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
|
||||
|
||||
@ -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<VS::PerVertexData>(
|
||||
cmd.BindVertices(CreateSolidFillVertices(
|
||||
renderer.GetTessellator(),
|
||||
GetCover()
|
||||
? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath()
|
||||
|
||||
@ -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<VS::PerVertexData>(
|
||||
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_,
|
||||
|
||||
@ -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<uint16_t> indices(elementItemCount);
|
||||
for (int i = 0; i < elementItemCount; i++) {
|
||||
indices[i] = static_cast<uint16_t>(elements[i]);
|
||||
}
|
||||
if (!callback(vertices, vertexItemCount, indices.data(), elementItemCount)) {
|
||||
return Result::kInputError;
|
||||
}
|
||||
|
||||
return Result::kSuccess;
|
||||
}
|
||||
|
||||
void DestroyTessellator(TESStesselator* tessellator) {
|
||||
if (tessellator != nullptr) {
|
||||
::tessDeleteTess(tessellator);
|
||||
|
||||
@ -44,6 +44,11 @@ class Tessellator {
|
||||
~Tessellator();
|
||||
|
||||
using VertexCallback = std::function<void(Point)>;
|
||||
using BuilderCallback = std::function<bool(const float* vertices,
|
||||
size_t vertices_size,
|
||||
const uint16_t* indices,
|
||||
size_t indices_size)>;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @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_;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user