Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[impeller] implement GetPositionUVBuffer #40248

Merged
merged 6 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 4 additions & 87 deletions impeller/entity/contents/tiled_texture_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,6 @@ bool TiledTextureContents::Render(const ContentContext& renderer,
if (texture_ == nullptr) {
return true;
}
// TODO(jonahwilliams): this is a special case for VerticesGeometry which
// implements GetPositionUVBuffer. The general geometry case does not use
// this method (see note below).
auto geometry = GetGeometry();
if (geometry->GetVertexType() == GeometryVertexType::kUV) {
return RenderVertices(renderer, entity, pass);
}

using VS = TiledTextureFillVertexShader;
using FS = TiledTextureFillFragmentShader;
Expand All @@ -104,21 +97,14 @@ bool TiledTextureContents::Render(const ContentContext& renderer,

auto& host_buffer = pass.GetTransientsBuffer();

auto geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);

// TODO(bdero): The geometry should be fetched from GetPositionUVBuffer and
// contain coverage-mapped UVs, and this should use
// position_uv.vert.
// https://github.com/flutter/flutter/issues/118553
auto bounds_origin = GetGeometry()->GetCoverage(Matrix())->origin;
auto geometry_result = GetGeometry()->GetPositionUVBuffer(
Rect(bounds_origin, Size(texture_size)), GetInverseMatrix(), renderer,
entity, pass);

VS::FrameInfo frame_info;
frame_info.mvp = geometry_result.transform;
frame_info.texture_sampler_y_coord_scale = texture_->GetYCoordScale();
frame_info.effect_transform = GetInverseMatrix();
frame_info.bounds_origin = geometry->GetCoverage(Matrix())->origin;
frame_info.texture_size = Vector2(static_cast<Scalar>(texture_size.width),
static_cast<Scalar>(texture_size.height));

FS::FragInfo frag_info;
frag_info.x_tile_mode = static_cast<Scalar>(x_tile_mode_);
Expand Down Expand Up @@ -169,73 +155,4 @@ bool TiledTextureContents::Render(const ContentContext& renderer,
return true;
}

bool TiledTextureContents::RenderVertices(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = PositionUVPipeline::VertexShader;
using FS = PositionUVPipeline::FragmentShader;

const auto texture_size = texture_->GetSize();
if (texture_size.IsEmpty()) {
return true;
}

auto& host_buffer = pass.GetTransientsBuffer();

auto geometry_result = GetGeometry()->GetPositionUVBuffer(
Rect::MakeSize(texture_size), GetInverseMatrix(), renderer, entity, pass);

VS::FrameInfo frame_info;
frame_info.mvp = geometry_result.transform;
frame_info.texture_sampler_y_coord_scale = texture_->GetYCoordScale();

FS::FragInfo frag_info;
frag_info.x_tile_mode = static_cast<Scalar>(x_tile_mode_);
frag_info.y_tile_mode = static_cast<Scalar>(y_tile_mode_);
frag_info.alpha = GetAlpha();

Command cmd;
cmd.label = "PositionUV";
cmd.stencil_reference = entity.GetStencilDepth();

auto options = OptionsFromPassAndEntity(pass, entity);
if (geometry_result.prevent_overdraw) {
options.stencil_compare = CompareFunction::kEqual;
options.stencil_operation = StencilOperation::kIncrementClamp;
}
options.primitive_type = geometry_result.type;
cmd.pipeline = renderer.GetPositionUVPipeline(options);

cmd.BindVertices(geometry_result.vertex_buffer);
VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));

if (color_filter_.has_value()) {
auto filtered_texture = CreateFilterTexture(renderer);
if (!filtered_texture.has_value()) {
return false;
}
FS::BindTextureSampler(
cmd, filtered_texture.value(),
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
CreateDescriptor()));
} else {
FS::BindTextureSampler(
cmd, texture_,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
CreateDescriptor()));
}

if (!pass.AddCommand(std::move(cmd))) {
return false;
}

if (geometry_result.prevent_overdraw) {
auto restore = ClipRestoreContents();
restore.SetRestoreCoverage(GetCoverage(entity));
return restore.Render(renderer, entity, pass);
}
return true;
}

} // namespace impeller
4 changes: 0 additions & 4 deletions impeller/entity/contents/tiled_texture_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ class TiledTextureContents final : public ColorSourceContents {
std::optional<std::shared_ptr<Texture>> CreateFilterTexture(
const ContentContext& renderer) const;

bool RenderVertices(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const;

SamplerDescriptor CreateDescriptor() const;

std::shared_ptr<Texture> texture_;
Expand Down
22 changes: 22 additions & 0 deletions impeller/entity/entity_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "impeller/entity/contents/solid_color_contents.h"
#include "impeller/entity/contents/text_contents.h"
#include "impeller/entity/contents/texture_contents.h"
#include "impeller/entity/contents/tiled_texture_contents.h"
#include "impeller/entity/contents/vertices_contents.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/entity_pass.h"
Expand Down Expand Up @@ -243,6 +244,27 @@ TEST_P(EntityTest, ThreeStrokesInOnePath) {
ASSERT_TRUE(OpenPlaygroundHere(entity));
}

TEST_P(EntityTest, StrokeWithTextureContents) {
auto bridge = CreateTextureForFixture("bay_bridge.jpg");
Path path = PathBuilder{}
.MoveTo({100, 100})
.LineTo({100, 200})
.MoveTo({100, 300})
.LineTo({100, 400})
.MoveTo({100, 500})
.LineTo({100, 600})
.TakePath();

Entity entity;
entity.SetTransformation(Matrix::MakeScale(GetContentScale()));
auto contents = std::make_unique<TiledTextureContents>();
contents->SetGeometry(Geometry::MakeStrokePath(path, 100.0));
contents->SetTexture(bridge);
contents->SetTileModes(Entity::TileMode::kClamp, Entity::TileMode::kClamp);
entity.SetContents(std::move(contents));
ASSERT_TRUE(OpenPlaygroundHere(entity));
}

TEST_P(EntityTest, TriangleInsideASquare) {
auto callback = [&](ContentContext& context, RenderPass& pass) {
Point offset(100, 100);
Expand Down
160 changes: 153 additions & 7 deletions impeller/entity/geometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "impeller/entity/geometry.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/position_color.vert.h"
#include "impeller/entity/texture_fill.vert.h"
#include "impeller/geometry/matrix.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/renderer/device_buffer.h"
Expand Down Expand Up @@ -51,6 +52,40 @@ std::unique_ptr<Geometry> Geometry::MakeRect(Rect rect) {
return std::make_unique<RectGeometry>(rect);
}

static GeometryResult ComputeUVGeometryForRect(Rect source_rect,
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
constexpr uint16_t kRectIndicies[4] = {0, 1, 2, 3};
auto& host_buffer = pass.GetTransientsBuffer();

std::vector<Point> data(8);
auto points = source_rect.GetPoints();
for (auto i = 0u, j = 0u; i < 8; i += 2, j++) {
data[i] = points[j];
data[i + 1] = effect_transform * ((points[j] - texture_coverage.origin) /
texture_coverage.size);
}

return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = host_buffer.Emplace(
data.data(), 16 * sizeof(float), alignof(float)),
.index_buffer = host_buffer.Emplace(
kRectIndicies, 4 * sizeof(uint16_t), alignof(uint16_t)),
.index_count = 4,
.index_type = IndexType::k16bit,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}

/////// Path Geometry ///////

FillPathGeometry::FillPathGeometry(const Path& path) : path_(path) {}
Expand Down Expand Up @@ -89,6 +124,51 @@ GeometryResult FillPathGeometry::GetPositionBuffer(
};
}

// |Geometry|
GeometryResult FillPathGeometry::GetPositionUVBuffer(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
using VS = TextureFillVertexShader;

VertexBufferBuilder<VS::PerVertexData> vertex_builder;
auto tesselation_result = renderer.GetTessellator()->Tessellate(
path_.GetFillType(),
path_.CreatePolyline(entity.GetTransformation().GetMaxBasisLength()),
[&vertex_builder, &texture_coverage, &effect_transform](
const float* vertices, size_t vertices_count, const uint16_t* indices,
size_t indices_count) {
for (auto i = 0u; i < vertices_count; i += 2) {
VS::PerVertexData data;
Point vtx = {vertices[i], vertices[i + 1]};
data.position = vtx;
auto coverage_coords =
((vtx - texture_coverage.origin) / texture_coverage.size) /
texture_coverage.size;
data.texture_coords = effect_transform * coverage_coords;
vertex_builder.AppendVertex(data);
}
FML_DCHECK(vertex_builder.GetVertexCount() == vertices_count / 2);
for (auto i = 0u; i < indices_count; i++) {
vertex_builder.AppendIndex(indices[i]);
}
return true;
});
if (tesselation_result != Tessellator::Result::kSuccess) {
return {};
}
return GeometryResult{
.type = PrimitiveType::kTriangle,
.vertex_buffer =
vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()),
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}

GeometryVertexType FillPathGeometry::GetVertexType() const {
return GeometryVertexType::kPosition;
}
Expand Down Expand Up @@ -300,9 +380,9 @@ StrokePathGeometry::CapProc StrokePathGeometry::GetCapProc(Cap stroke_cap) {
}

// static
VertexBuffer StrokePathGeometry::CreateSolidStrokeVertices(
VertexBufferBuilder<SolidFillVertexShader::PerVertexData>
StrokePathGeometry::CreateSolidStrokeVertices(
const Path& path,
HostBuffer& buffer,
Scalar stroke_width,
Scalar scaled_miter_limit,
Cap cap,
Expand Down Expand Up @@ -422,7 +502,7 @@ VertexBuffer StrokePathGeometry::CreateSolidStrokeVertices(
}
}

return vtx_builder.CreateVertexBuffer(buffer);
return vtx_builder;
}

GeometryResult StrokePathGeometry::GetPositionBuffer(
Expand All @@ -441,14 +521,58 @@ GeometryResult StrokePathGeometry::GetPositionBuffer(
Scalar stroke_width = std::max(stroke_width_, min_size);

auto& host_buffer = pass.GetTransientsBuffer();
auto vertex_buffer = CreateSolidStrokeVertices(
path_, host_buffer, stroke_width, miter_limit_ * stroke_width_ * 0.5,
stroke_cap_, GetJoinProc(stroke_join_), GetCapProc(stroke_cap_),
auto vertex_builder = CreateSolidStrokeVertices(
path_, stroke_width, miter_limit_ * stroke_width_ * 0.5, stroke_cap_,
GetJoinProc(stroke_join_), GetCapProc(stroke_cap_),
entity.GetTransformation().GetMaxBasisLength());

return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer = vertex_buffer,
.vertex_buffer = vertex_builder.CreateVertexBuffer(host_buffer),
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = true,
};
}

GeometryResult StrokePathGeometry::GetPositionUVBuffer(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
if (stroke_width_ < 0.0) {
return {};
}
auto determinant = entity.GetTransformation().GetDeterminant();
if (determinant == 0) {
return {};
}

Scalar min_size = 1.0f / sqrt(std::abs(determinant));
Scalar stroke_width = std::max(stroke_width_, min_size);

auto& host_buffer = pass.GetTransientsBuffer();
auto stroke_builder = CreateSolidStrokeVertices(
path_, stroke_width, miter_limit_ * stroke_width_ * 0.5, stroke_cap_,
GetJoinProc(stroke_join_), GetCapProc(stroke_cap_),
entity.GetTransformation().GetMaxBasisLength());

VertexBufferBuilder<TextureFillVertexShader::PerVertexData> vertex_builder;
stroke_builder.IterateVertices(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a bit of a shortcut here, because refactoring the stroke computation to handle either path felt a bit like a premature optimization

[&vertex_builder, &texture_coverage,
&effect_transform](SolidFillVertexShader::PerVertexData old_vtx) {
TextureFillVertexShader::PerVertexData data;
data.position = old_vtx.position;
auto coverage_coords = (old_vtx.position - texture_coverage.origin) /
texture_coverage.size;
data.texture_coords = effect_transform * coverage_coords;
vertex_builder.AppendVertex(data);
});

return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer = vertex_builder.CreateVertexBuffer(host_buffer),
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = true,
Expand Down Expand Up @@ -515,6 +639,18 @@ GeometryResult CoverGeometry::GetPositionBuffer(const ContentContext& renderer,
};
}

// |Geometry|
GeometryResult CoverGeometry::GetPositionUVBuffer(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
auto rect = Rect(Size(pass.GetRenderTargetSize()));
return ComputeUVGeometryForRect(rect, texture_coverage, effect_transform,
renderer, entity, pass);
}

GeometryVertexType CoverGeometry::GetVertexType() const {
return GeometryVertexType::kPosition;
}
Expand Down Expand Up @@ -551,6 +687,16 @@ GeometryResult RectGeometry::GetPositionBuffer(const ContentContext& renderer,
};
}

// |Geometry|
GeometryResult RectGeometry::GetPositionUVBuffer(Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
return ComputeUVGeometryForRect(rect_, texture_coverage, effect_transform,
renderer, entity, pass);
}

GeometryVertexType RectGeometry::GetVertexType() const {
return GeometryVertexType::kPosition;
}
Expand Down
Loading