Skip to content

Commit 60c3b21

Browse files
chinmaygardednfield
authored andcommitted
Implement stencil-only clips.
1 parent 9c88b09 commit 60c3b21

18 files changed

+163
-92
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,9 @@ TEST_F(AiksTest, DISABLED_CanRenderImageRect) {
7070
source_rect.size.height /= 2;
7171
source_rect.origin.x += source_rect.size.width;
7272
source_rect.origin.y += source_rect.size.height;
73-
7473
canvas.DrawImageRect(image, source_rect, Rect::MakeXYWH(100, 100, 600, 600),
7574
paint);
76-
// ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
75+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
7776
}
7877

7978
TEST_F(AiksTest, CanRenderStrokes) {
@@ -102,9 +101,9 @@ TEST_F(AiksTest, CanRenderClips) {
102101
Paint paint;
103102
paint.color = Color::Fuchsia();
104103
canvas.ClipPath(
105-
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 100, 100)).CreatePath());
106-
canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).CreatePath(), paint);
107-
// ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
104+
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath());
105+
canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint);
106+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
108107
}
109108

110109
} // namespace testing

impeller/aiks/canvas.cc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ bool Canvas::Restore() {
3333
}
3434

3535
void Canvas::Concat(const Matrix& xformation) {
36-
xformation_stack_.top() = xformation * xformation_stack_.top();
36+
const auto current_xformation = xformation_stack_.top().xformation;
37+
xformation_stack_.top().xformation = xformation * current_xformation;
3738
}
3839

3940
const Matrix& Canvas::GetCurrentTransformation() const {
40-
return xformation_stack_.top();
41+
return xformation_stack_.top().xformation;
4142
}
4243

4344
void Canvas::Translate(const Vector3& offset) {
@@ -68,6 +69,7 @@ void Canvas::DrawPath(Path path, Paint paint) {
6869
Entity entity;
6970
entity.SetTransformation(GetCurrentTransformation());
7071
entity.SetPath(std::move(path));
72+
entity.SetStencilDepth(GetStencilDepth());
7173
entity.SetContents(paint.CreateContentsForEntity());
7274
GetCurrentPass().PushEntity(std::move(entity));
7375
}
@@ -77,10 +79,13 @@ void Canvas::SaveLayer(const Paint& paint, std::optional<Rect> bounds) {
7779
}
7880

7981
void Canvas::ClipPath(Path path) {
82+
IncrementStencilDepth();
83+
8084
Entity entity;
8185
entity.SetTransformation(GetCurrentTransformation());
8286
entity.SetPath(std::move(path));
8387
entity.SetContents(std::make_shared<ClipContents>());
88+
entity.SetStencilDepth(GetStencilDepth());
8489
GetCurrentPass().PushEntity(std::move(entity));
8590
}
8691

@@ -149,4 +154,12 @@ CanvasPass& Canvas::GetCurrentPass() {
149154
return passes_.back();
150155
}
151156

157+
void Canvas::IncrementStencilDepth() {
158+
++xformation_stack_.top().stencil_depth;
159+
}
160+
161+
size_t Canvas::GetStencilDepth() const {
162+
return xformation_stack_.top().stencil_depth;
163+
}
164+
152165
} // namespace impeller

impeller/aiks/canvas.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ namespace impeller {
2323

2424
class Entity;
2525

26+
struct CanvasStackEntry {
27+
Matrix xformation;
28+
size_t stencil_depth = 0u;
29+
};
30+
2631
class Canvas {
2732
public:
2833
Canvas();
@@ -67,11 +72,15 @@ class Canvas {
6772
Picture EndRecordingAsPicture();
6873

6974
private:
70-
std::stack<Matrix> xformation_stack_;
75+
std::stack<CanvasStackEntry> xformation_stack_;
7176
std::vector<CanvasPass> passes_;
7277

7378
CanvasPass& GetCurrentPass();
7479

80+
void IncrementStencilDepth();
81+
82+
size_t GetStencilDepth() const;
83+
7584
FML_DISALLOW_COPY_AND_ASSIGN(Canvas);
7685
};
7786

impeller/compiler/reflector.cc

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -526,17 +526,19 @@ std::vector<StructMember> Reflector::ReadStructMembers(
526526
}
527527
}
528528

529-
const auto struct_length = current_byte_offset;
530-
{
531-
const auto excess = struct_length % max_member_alignment;
532-
if (excess != 0) {
533-
const auto padding = max_member_alignment - excess;
534-
result.emplace_back(StructMember{
535-
.type = TypeNameWithPaddingOfSize(padding),
536-
.name = "_PADDING_",
537-
.offset = current_byte_offset,
538-
.byte_length = padding,
539-
});
529+
if (max_member_alignment > 0u) {
530+
const auto struct_length = current_byte_offset;
531+
{
532+
const auto excess = struct_length % max_member_alignment;
533+
if (excess != 0) {
534+
const auto padding = max_member_alignment - excess;
535+
result.emplace_back(StructMember{
536+
.type = TypeNameWithPaddingOfSize(padding),
537+
.name = "_PADDING_",
538+
.offset = current_byte_offset,
539+
.byte_length = padding,
540+
});
541+
}
540542
}
541543
}
542544

impeller/entity/content_renderer.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,35 @@ ContentRenderer::ContentRenderer(std::shared_ptr<Context> context)
1212
return;
1313
}
1414

15+
// Pipelines whose default descriptors work fine for the entity framework.
1516
gradient_fill_pipeline_ = std::make_unique<GradientFillPipeline>(*context_);
1617
solid_fill_pipeline_ = std::make_unique<SolidFillPipeline>(*context_);
1718
texture_pipeline_ = std::make_unique<TexturePipeline>(*context_);
1819
solid_stroke_pipeline_ = std::make_unique<SolidStrokePipeline>(*context_);
1920

21+
// Pipelines that are variants of the base pipelines with custom descriptors.
22+
if (auto solid_fill_pipeline = solid_fill_pipeline_->WaitAndGet()) {
23+
auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor();
24+
// Write to the stencil buffer.
25+
StencilAttachmentDescriptor stencil0;
26+
stencil0.stencil_compare = CompareFunction::kGreaterEqual;
27+
stencil0.depth_stencil_pass = StencilOperation::kSetToReferenceValue;
28+
clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0);
29+
// Disable write to all color attachments.
30+
auto color_attachments =
31+
clip_pipeline_descriptor.GetColorAttachmentDescriptors();
32+
for (auto& color_attachment : color_attachments) {
33+
color_attachment.second.write_mask =
34+
static_cast<uint64_t>(ColorWriteMask::kNone);
35+
}
36+
clip_pipeline_descriptor.SetColorAttachmentDescriptors(
37+
std::move(color_attachments));
38+
clip_pipeline_ = std::make_unique<ClipPipeline>(
39+
*context_, std::move(clip_pipeline_descriptor));
40+
} else {
41+
return;
42+
}
43+
2044
is_valid_ = true;
2145
}
2246

@@ -61,4 +85,12 @@ std::shared_ptr<Pipeline> ContentRenderer::GetSolidStrokePipeline() const {
6185
return solid_stroke_pipeline_->WaitAndGet();
6286
}
6387

88+
std::shared_ptr<Pipeline> ContentRenderer::GetClipPipeline() const {
89+
if (!IsValid()) {
90+
return nullptr;
91+
}
92+
93+
return clip_pipeline_->WaitAndGet();
94+
}
95+
6496
} // namespace impeller

impeller/entity/content_renderer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ using TexturePipeline =
2727
PipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
2828
using SolidStrokePipeline =
2929
PipelineT<SolidStrokeVertexShader, SolidStrokeFragmentShader>;
30+
// Instead of requiring new shaders for clips, the solid fill stages are used
31+
// to redirect writing to the stencil instead of color attachments.
32+
using ClipPipeline = PipelineT<SolidFillVertexShader, SolidFillFragmentShader>;
3033

3134
class ContentRenderer {
3235
public:
@@ -44,6 +47,8 @@ class ContentRenderer {
4447

4548
std::shared_ptr<Pipeline> GetSolidStrokePipeline() const;
4649

50+
std::shared_ptr<Pipeline> GetClipPipeline() const;
51+
4752
std::shared_ptr<Context> GetContext() const;
4853

4954
private:
@@ -52,6 +57,7 @@ class ContentRenderer {
5257
std::unique_ptr<SolidFillPipeline> solid_fill_pipeline_;
5358
std::unique_ptr<TexturePipeline> texture_pipeline_;
5459
std::unique_ptr<SolidStrokePipeline> solid_stroke_pipeline_;
60+
std::unique_ptr<ClipPipeline> clip_pipeline_;
5561
bool is_valid_ = false;
5662

5763
FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer);

impeller/entity/contents.cc

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer,
8787
Command cmd;
8888
cmd.label = "LinearGradientFill";
8989
cmd.pipeline = renderer.GetGradientFillPipeline();
90+
cmd.stencil_reference = entity.GetStencilDepth();
9091
cmd.BindVertices(vertices_builder.CreateVertexBuffer(
9192
*renderer.GetContext()->GetPermanentsAllocator()));
9293
cmd.primitive_type = PrimitiveType::kTriangle;
@@ -112,6 +113,25 @@ const Color& SolidColorContents::GetColor() const {
112113
return color_;
113114
}
114115

116+
static VertexBuffer CreateSolidFillVertices(const Path& path,
117+
HostBuffer& buffer) {
118+
using VS = SolidFillPipeline::VertexShader;
119+
120+
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
121+
122+
auto tesselation_result = Tessellator{}.Tessellate(
123+
path.CreatePolyline(), [&vtx_builder](auto point) {
124+
VS::PerVertexData vtx;
125+
vtx.vertices = point;
126+
vtx_builder.AppendVertex(vtx);
127+
});
128+
if (!tesselation_result) {
129+
return {};
130+
}
131+
132+
return vtx_builder.CreateVertexBuffer(buffer);
133+
}
134+
115135
bool SolidColorContents::Render(const ContentRenderer& renderer,
116136
const Entity& entity,
117137
const Surface& surface,
@@ -125,29 +145,9 @@ bool SolidColorContents::Render(const ContentRenderer& renderer,
125145
Command cmd;
126146
cmd.label = "SolidFill";
127147
cmd.pipeline = renderer.GetSolidFillPipeline();
128-
if (cmd.pipeline == nullptr) {
129-
return false;
130-
}
131-
132-
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
133-
{
134-
auto tesselation_result = Tessellator{}.Tessellate(
135-
entity.GetPath().CreatePolyline(), [&vtx_builder](auto point) {
136-
VS::PerVertexData vtx;
137-
vtx.vertices = point;
138-
vtx_builder.AppendVertex(vtx);
139-
});
140-
if (!tesselation_result) {
141-
return false;
142-
}
143-
}
144-
145-
if (!vtx_builder.HasVertices()) {
146-
return true;
147-
}
148-
149-
cmd.BindVertices(vtx_builder.CreateVertexBuffer(
150-
*renderer.GetContext()->GetPermanentsAllocator()));
148+
cmd.stencil_reference = entity.GetStencilDepth();
149+
cmd.BindVertices(
150+
CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer()));
151151

152152
VS::FrameInfo frame_info;
153153
frame_info.mvp =
@@ -240,6 +240,7 @@ bool TextureContents::Render(const ContentRenderer& renderer,
240240
Command cmd;
241241
cmd.label = "TextureFill";
242242
cmd.pipeline = renderer.GetTexturePipeline();
243+
cmd.stencil_reference = entity.GetStencilDepth();
243244
cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer));
244245
VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
245246
FS::BindTextureSampler(
@@ -335,6 +336,7 @@ bool SolidStrokeContents::Render(const ContentRenderer& renderer,
335336
cmd.primitive_type = PrimitiveType::kTriangleStrip;
336337
cmd.label = "SolidStroke";
337338
cmd.pipeline = renderer.GetSolidStrokePipeline();
339+
cmd.stencil_reference = entity.GetStencilDepth();
338340
cmd.BindVertices(
339341
CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer()));
340342
VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info));
@@ -366,6 +368,23 @@ bool ClipContents::Render(const ContentRenderer& renderer,
366368
const Entity& entity,
367369
const Surface& surface,
368370
RenderPass& pass) const {
371+
using VS = ClipPipeline::VertexShader;
372+
373+
Command cmd;
374+
cmd.label = "Clip";
375+
cmd.pipeline = renderer.GetClipPipeline();
376+
cmd.stencil_reference = entity.GetStencilDepth() + 1u;
377+
cmd.BindVertices(
378+
CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer()));
379+
380+
VS::FrameInfo info;
381+
// The color really doesn't matter.
382+
info.color = Color::SkyBlue();
383+
info.mvp = Matrix::MakeOrthographic(surface.GetSize());
384+
385+
VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info));
386+
387+
pass.AddCommand(std::move(cmd));
369388
return true;
370389
}
371390

impeller/entity/entity.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,12 @@ const std::shared_ptr<Contents>& Entity::GetContents() const {
3434
return contents_;
3535
}
3636

37+
void Entity::SetStencilDepth(uint32_t depth) {
38+
stencil_depth_ = depth;
39+
}
40+
41+
uint32_t Entity::GetStencilDepth() const {
42+
return stencil_depth_;
43+
}
44+
3745
} // namespace impeller

impeller/entity/entity.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ class Entity {
3131

3232
const std::shared_ptr<Contents>& GetContents() const;
3333

34+
void SetStencilDepth(uint32_t stencil_depth);
35+
36+
uint32_t GetStencilDepth() const;
37+
3438
private:
3539
Matrix transformation_;
3640
std::shared_ptr<Contents> contents_;
3741
Path path_;
42+
uint32_t stencil_depth_ = 0u;
3843
};
3944

4045
} // namespace impeller

impeller/playground/playground.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window,
172172
stencil0.texture = stencil_texture;
173173
stencil0.clear_stencil = 0;
174174
stencil0.load_action = LoadAction::kClear;
175-
stencil0.store_action = StoreAction::kStore;
175+
stencil0.store_action = StoreAction::kDontCare;
176176

177177
RenderTarget desc;
178178
desc.SetColorAttachment(color0, 0u);

impeller/renderer/backend/metal/formats_mtl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ constexpr MTLStencilOperation ToMTLStencilOperation(StencilOperation op) {
156156
return MTLStencilOperationKeep;
157157
case StencilOperation::kZero:
158158
return MTLStencilOperationZero;
159-
case StencilOperation::kSetToReferneceValue:
159+
case StencilOperation::kSetToReferenceValue:
160160
return MTLStencilOperationReplace;
161161
case StencilOperation::kIncrementClamp:
162162
return MTLStencilOperationIncrementClamp;

impeller/renderer/backend/metal/render_pass_mtl.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ static bool Bind(PassBindingsCache& pass,
371371
? MTLWindingClockwise
372372
: MTLWindingCounterClockwise];
373373
[pass setCullMode:MTLCullModeNone];
374+
[pass setStencilReferenceValue:command.stencil_reference];
374375
if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) {
375376
return false;
376377
}

impeller/renderer/command.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct Command {
6767
std::string label;
6868
PrimitiveType primitive_type = PrimitiveType::kTriangle;
6969
WindingOrder winding = WindingOrder::kClockwise;
70+
uint32_t stencil_reference = 0u;
7071

7172
bool BindVertices(const VertexBuffer& buffer);
7273

impeller/renderer/formats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ enum class StencilOperation {
241241
/// Reset the stencil value to zero.
242242
kZero,
243243
/// Reset the stencil value to the reference value.
244-
kSetToReferneceValue,
244+
kSetToReferenceValue,
245245
/// Increment the current stencil value by 1. Clamp it to the maximum.
246246
kIncrementClamp,
247247
/// Decrement the current stencil value by 1. Clamp it to zero.

impeller/renderer/pipeline_builder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ struct PipelineBuilder {
107107
// Setup default stencil buffer descriptions.
108108
{
109109
StencilAttachmentDescriptor stencil0;
110-
stencil0.depth_stencil_pass = StencilOperation::kIncrementClamp;
110+
stencil0.stencil_compare = CompareFunction::kLessEqual;
111111
desc.SetStencilAttachmentDescriptors(stencil0);
112112
desc.SetStencilPixelFormat(PixelFormat::kD32FloatS8UNormInt);
113113
}

0 commit comments

Comments
 (0)