[impeller] implement GetPositionUVBuffer (flutter/engine#40248)

[impeller] implement GetPositionUVBuffer
This commit is contained in:
Jonah Williams 2023-03-15 11:39:59 -07:00 committed by GitHub
parent 828f3deb86
commit ff292da861
8 changed files with 257 additions and 148 deletions

View File

@ -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;
@ -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_);
@ -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

View File

@ -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_;

View File

@ -246,6 +246,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);

View File

@ -6,6 +6,7 @@
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.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"
@ -52,6 +53,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) {}
@ -90,6 +125,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;
}
@ -301,9 +381,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,
@ -423,7 +503,7 @@ VertexBuffer StrokePathGeometry::CreateSolidStrokeVertices(
}
}
return vtx_builder.CreateVertexBuffer(buffer);
return vtx_builder;
}
GeometryResult StrokePathGeometry::GetPositionBuffer(
@ -442,14 +522,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(
[&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,
@ -516,6 +640,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;
}
@ -552,6 +688,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;
}

View File

@ -109,6 +109,13 @@ class FillPathGeometry : public Geometry {
// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
// |Geometry|
GeometryResult GetPositionUVBuffer(Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) override;
Path path_;
FML_DISALLOW_COPY_AND_ASSIGN(FillPathGeometry);
@ -154,26 +161,35 @@ class StrokePathGeometry : public Geometry {
const Entity& entity,
RenderPass& pass) override;
// |Geometry|
GeometryResult GetPositionUVBuffer(Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) override;
// |Geometry|
GeometryVertexType GetVertexType() const override;
// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
bool SkipRendering() const;
static Scalar CreateBevelAndGetDirection(
VertexBufferBuilder<SolidFillVertexShader::PerVertexData>& vtx_builder,
const Point& position,
const Point& start_offset,
const Point& end_offset);
static VertexBuffer CreateSolidStrokeVertices(const Path& path,
HostBuffer& buffer,
Scalar stroke_width,
Scalar scaled_miter_limit,
Cap cap,
const JoinProc& join_proc,
const CapProc& cap_proc,
Scalar scale);
static VertexBufferBuilder<SolidFillVertexShader::PerVertexData>
CreateSolidStrokeVertices(const Path& path,
Scalar stroke_width,
Scalar scaled_miter_limit,
Cap cap,
const JoinProc& join_proc,
const CapProc& cap_proc,
Scalar scale);
static StrokePathGeometry::JoinProc GetJoinProc(Join stroke_join);
@ -208,6 +224,13 @@ class CoverGeometry : public Geometry {
// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
// |Geometry|
GeometryResult GetPositionUVBuffer(Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) override;
FML_DISALLOW_COPY_AND_ASSIGN(CoverGeometry);
};
@ -229,6 +252,13 @@ class RectGeometry : public Geometry {
// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
// |Geometry|
GeometryResult GetPositionUVBuffer(Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) override;
Rect rect_;
FML_DISALLOW_COPY_AND_ASSIGN(RectGeometry);

View File

@ -3,27 +3,21 @@
// found in the LICENSE file.
#include <impeller/texture.glsl>
#include <impeller/transform.glsl>
#include <impeller/types.glsl>
uniform FrameInfo {
mat4 mvp;
mat4 effect_transform;
vec2 bounds_origin;
vec2 texture_size;
float texture_sampler_y_coord_scale;
}
frame_info;
in vec2 position;
in vec2 texture_coords;
out vec2 v_texture_coords;
void main() {
gl_Position = frame_info.mvp * vec4(position, 0.0, 1.0);
v_texture_coords = IPRemapCoords(
IPVec2TransformPosition(
frame_info.effect_transform,
(position - frame_info.bounds_origin) / frame_info.texture_size),
frame_info.texture_sampler_y_coord_scale);
v_texture_coords =
IPRemapCoords(texture_coords, frame_info.texture_sampler_y_coord_scale);
}

View File

@ -91,6 +91,12 @@ class VertexBufferBuilder {
return buffer;
};
void IterateVertices(const std::function<void(VertexType&)>& iterator) {
for (auto& vertex : vertices_) {
iterator(vertex);
}
}
private:
std::vector<VertexType> vertices_;
std::vector<IndexType> indices_;

View File

@ -10030,7 +10030,7 @@
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 22,
"uniform_registers_used": 16,
"work_registers_used": 32
},
"Varying": {
@ -10041,10 +10041,10 @@
"load_store"
],
"longest_path_cycles": [
0.125,
0.125,
0.03125,
0.0625,
0.015625,
0.03125,
0.0,
3.0,
0.0
],
@ -10060,10 +10060,10 @@
"load_store"
],
"shortest_path_cycles": [
0.125,
0.125,
0.03125,
0.0625,
0.015625,
0.03125,
0.0,
3.0,
0.0
],
@ -10071,18 +10071,18 @@
"load_store"
],
"total_cycles": [
0.125,
0.125,
0.03125,
0.0625,
0.015625,
0.03125,
0.0,
3.0,
0.0
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 16,
"work_registers_used": 8
"uniform_registers_used": 10,
"work_registers_used": 7
}
}
},
@ -10099,8 +10099,8 @@
"load_store"
],
"longest_path_cycles": [
3.9600000381469727,
4.0,
2.9700000286102295,
5.0,
0.0
],
"pipelines": [
@ -10112,23 +10112,22 @@
"load_store"
],
"shortest_path_cycles": [
3.9600000381469727,
4.0,
2.9700000286102295,
5.0,
0.0
],
"total_bound_pipelines": [
"arithmetic",
"load_store"
],
"total_cycles": [
4.0,
4.0,
3.0,
5.0,
0.0
]
},
"thread_occupancy": 100,
"uniform_registers_used": 6,
"work_registers_used": 3
"uniform_registers_used": 4,
"work_registers_used": 2
}
}
}
@ -12955,7 +12954,7 @@
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 28,
"uniform_registers_used": 20,
"work_registers_used": 32
},
"Varying": {
@ -12966,10 +12965,10 @@
"load_store"
],
"longest_path_cycles": [
0.125,
0.125,
0.03125,
0.0625,
0.015625,
0.03125,
0.0,
3.0,
0.0
],
@ -12985,10 +12984,10 @@
"load_store"
],
"shortest_path_cycles": [
0.125,
0.125,
0.03125,
0.0625,
0.015625,
0.03125,
0.0,
3.0,
0.0
],
@ -12996,18 +12995,18 @@
"load_store"
],
"total_cycles": [
0.125,
0.125,
0.03125,
0.0625,
0.015625,
0.03125,
0.0,
3.0,
0.0
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 24,
"work_registers_used": 8
"uniform_registers_used": 16,
"work_registers_used": 7
}
}
}