[Impeller] Add StC color source/clip behavior + constexpr flag for enabling StC. (#50817)
Included in this PR is:
- Behavior for clips/restores when StC is enabled.
- Behavior for color source drawing when StC is enabled.
- New filled path tessellator behavior.
- A constexpr flag for enabling StC.
This patch shouldn't actually change any behavior when StC is off.
diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc
index d65d527..1c8c963 100644
--- a/impeller/aiks/canvas.cc
+++ b/impeller/aiks/canvas.cc
@@ -219,8 +219,10 @@
}
transform_stack_.pop_back();
- if (num_clips > 0) {
- RestoreClip();
+ if constexpr (!ContentContext::kEnableStencilThenCover) {
+ if (num_clips > 0) {
+ RestoreClip();
+ }
}
return true;
@@ -597,7 +599,6 @@
entity.SetContents(std::make_shared<ClipRestoreContents>());
entity.SetClipDepth(GetClipDepth());
- // TODO(bdero): To be removed when swapping the clip strategy.
AddEntityToCurrentPass(std::move(entity));
}
diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc
index 22b81e5..b85beac 100644
--- a/impeller/entity/contents/clip_contents.cc
+++ b/impeller/entity/contents/clip_contents.cc
@@ -73,9 +73,97 @@
void ClipContents::SetInheritedOpacity(Scalar opacity) {}
-bool ClipContents::Render(const ContentContext& renderer,
- const Entity& entity,
- RenderPass& pass) const {
+bool ClipContents::RenderDepthClip(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass,
+ Entity::ClipOperation clip_op,
+ const Geometry& geometry) const {
+ using VS = ClipPipeline::VertexShader;
+
+ VS::FrameInfo info;
+ info.depth = entity.GetShaderClipDepth();
+
+ auto geometry_result = geometry.GetPositionBuffer(renderer, entity, pass);
+ auto options = OptionsFromPass(pass);
+ options.blend_mode = BlendMode::kDestination;
+
+ pass.SetStencilReference(0);
+
+ /// Stencil preparation draw.
+
+ options.depth_write_enabled = false;
+ options.primitive_type = geometry_result.type;
+ pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
+ switch (geometry_result.mode) {
+ case GeometryResult::Mode::kNonZero:
+ pass.SetCommandLabel("Clip stencil preparation (NonZero)");
+ options.stencil_mode =
+ ContentContextOptions::StencilMode::kStencilNonZeroFill;
+ break;
+ case GeometryResult::Mode::kEvenOdd:
+ pass.SetCommandLabel("Clip stencil preparation (EvenOdd)");
+ options.stencil_mode =
+ ContentContextOptions::StencilMode::kStencilEvenOddFill;
+ break;
+ case GeometryResult::Mode::kNormal:
+ case GeometryResult::Mode::kPreventOverdraw:
+ pass.SetCommandLabel("Clip stencil preparation (Increment)");
+ options.stencil_mode =
+ ContentContextOptions::StencilMode::kLegacyClipIncrement;
+ break;
+ }
+ pass.SetPipeline(renderer.GetClipPipeline(options));
+
+ info.mvp = geometry_result.transform;
+ VS::BindFrameInfo(pass, renderer.GetTransientsBuffer().EmplaceUniform(info));
+
+ if (!pass.Draw().ok()) {
+ return false;
+ }
+
+ /// Write depth.
+
+ options.depth_write_enabled = true;
+ options.primitive_type = PrimitiveType::kTriangleStrip;
+ Rect cover_area;
+ switch (clip_op) {
+ case Entity::ClipOperation::kIntersect:
+ pass.SetCommandLabel("Intersect Clip");
+ options.stencil_mode =
+ ContentContextOptions::StencilMode::kCoverCompareInverted;
+ cover_area = Rect::MakeSize(pass.GetRenderTargetSize());
+ break;
+ case Entity::ClipOperation::kDifference:
+ pass.SetCommandLabel("Difference Clip");
+ options.stencil_mode = ContentContextOptions::StencilMode::kCoverCompare;
+ std::optional<Rect> maybe_cover_area =
+ geometry.GetCoverage(entity.GetTransform());
+ if (!maybe_cover_area.has_value()) {
+ return true;
+ }
+ cover_area = maybe_cover_area.value();
+ break;
+ }
+ auto points = cover_area.GetPoints();
+ auto vertices =
+ VertexBufferBuilder<VS::PerVertexData>{}
+ .AddVertices({{points[0]}, {points[1]}, {points[2]}, {points[3]}})
+ .CreateVertexBuffer(renderer.GetTransientsBuffer());
+ pass.SetVertexBuffer(std::move(vertices));
+
+ pass.SetPipeline(renderer.GetClipPipeline(options));
+
+ info.mvp = pass.GetOrthographicTransform();
+ VS::BindFrameInfo(pass, renderer.GetTransientsBuffer().EmplaceUniform(info));
+
+ return pass.Draw().ok();
+}
+
+bool ClipContents::RenderStencilClip(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass,
+ Entity::ClipOperation clip_op,
+ const Geometry& geometry) const {
using VS = ClipPipeline::VertexShader;
VS::FrameInfo info;
@@ -85,7 +173,7 @@
options.blend_mode = BlendMode::kDestination;
pass.SetStencilReference(entity.GetClipDepth());
- if (clip_op_ == Entity::ClipOperation::kDifference) {
+ if (clip_op == Entity::ClipOperation::kDifference) {
{
pass.SetCommandLabel("Difference Clip (Increment)");
@@ -123,7 +211,7 @@
ContentContextOptions::StencilMode::kLegacyClipIncrement;
}
- auto geometry_result = geometry_->GetPositionBuffer(renderer, entity, pass);
+ auto geometry_result = geometry.GetPositionBuffer(renderer, entity, pass);
options.primitive_type = geometry_result.type;
pass.SetPipeline(renderer.GetClipPipeline(options));
@@ -135,6 +223,19 @@
return pass.Draw().ok();
}
+bool ClipContents::Render(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass) const {
+ if (!geometry_) {
+ return true;
+ }
+ if constexpr (ContentContext::kEnableStencilThenCover) {
+ return RenderDepthClip(renderer, entity, pass, clip_op_, *geometry_);
+ } else {
+ return RenderStencilClip(renderer, entity, pass, clip_op_, *geometry_);
+ }
+}
+
/*******************************************************************************
******* ClipRestoreContents
******************************************************************************/
diff --git a/impeller/entity/contents/clip_contents.h b/impeller/entity/contents/clip_contents.h
index b519cc2..8d7f162 100644
--- a/impeller/entity/contents/clip_contents.h
+++ b/impeller/entity/contents/clip_contents.h
@@ -49,6 +49,17 @@
void SetInheritedOpacity(Scalar opacity) override;
private:
+ bool RenderDepthClip(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass,
+ Entity::ClipOperation clip_op,
+ const Geometry& geometry) const;
+ bool RenderStencilClip(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass,
+ Entity::ClipOperation clip_op,
+ const Geometry& geometry) const;
+
std::shared_ptr<Geometry> geometry_;
Entity::ClipOperation clip_op_ = Entity::ClipOperation::kIntersect;
diff --git a/impeller/entity/contents/color_source_contents.h b/impeller/entity/contents/color_source_contents.h
index d89e522..4951494 100644
--- a/impeller/entity/contents/color_source_contents.h
+++ b/impeller/entity/contents/color_source_contents.h
@@ -5,7 +5,9 @@
#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_COLOR_SOURCE_CONTENTS_H_
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_COLOR_SOURCE_CONTENTS_H_
+#include "fml/logging.h"
#include "impeller/entity/contents/clip_contents.h"
+#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/contents.h"
#include "impeller/entity/geometry/geometry.h"
#include "impeller/geometry/matrix.h"
@@ -118,6 +120,71 @@
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_fragment_callback) const {
auto options = OptionsFromPassAndEntity(pass, entity);
+ options.primitive_type = geometry_result.type;
+
+ // Take the pre-populated vertex shader uniform struct and set managed
+ // values.
+ frame_info.depth = entity.GetShaderClipDepth();
+ frame_info.mvp = geometry_result.transform;
+
+ pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
+
+ if constexpr (ContentContext::kEnableStencilThenCover) {
+ const bool is_stencil_then_cover =
+ geometry_result.mode == GeometryResult::Mode::kNonZero ||
+ geometry_result.mode == GeometryResult::Mode::kEvenOdd;
+ if (is_stencil_then_cover) {
+ pass.SetStencilReference(0);
+
+ /// Stencil preparation draw.
+
+ options.blend_mode = BlendMode::kDestination;
+ switch (geometry_result.mode) {
+ case GeometryResult::Mode::kNonZero:
+ pass.SetCommandLabel("Stencil preparation (NonZero)");
+ options.stencil_mode =
+ ContentContextOptions::StencilMode::kStencilNonZeroFill;
+ break;
+ case GeometryResult::Mode::kEvenOdd:
+ pass.SetCommandLabel("Stencil preparation (EvenOdd)");
+ options.stencil_mode =
+ ContentContextOptions::StencilMode::kStencilEvenOddFill;
+ break;
+ default:
+ FML_UNREACHABLE();
+ }
+ pass.SetPipeline(renderer.GetClipPipeline(options));
+ ClipPipeline::VertexShader::FrameInfo clip_frame_info;
+ clip_frame_info.depth = entity.GetShaderClipDepth();
+ clip_frame_info.mvp = geometry_result.transform;
+ ClipPipeline::VertexShader::BindFrameInfo(
+ pass,
+ renderer.GetTransientsBuffer().EmplaceUniform(clip_frame_info));
+
+ if (!pass.Draw().ok()) {
+ return false;
+ }
+
+ /// Cover draw.
+
+ options.stencil_mode =
+ ContentContextOptions::StencilMode::kCoverCompare;
+ std::optional<Rect> maybe_cover_area =
+ GetGeometry()->GetCoverage(entity.GetTransform());
+ if (!maybe_cover_area.has_value()) {
+ return true;
+ }
+ auto points = maybe_cover_area.value().GetPoints();
+ auto vertices =
+ VertexBufferBuilder<typename VertexShaderT::PerVertexData>{}
+ .AddVertices(
+ {{points[0]}, {points[1]}, {points[2]}, {points[3]}})
+ .CreateVertexBuffer(renderer.GetTransientsBuffer());
+ pass.SetVertexBuffer(std::move(vertices));
+
+ frame_info.mvp = pass.GetOrthographicTransform();
+ }
+ }
// If overdraw prevention is enabled (like when drawing stroke paths), we
// increment the stencil buffer as we draw, preventing overlapping fragments
@@ -127,14 +194,11 @@
options.stencil_mode =
ContentContextOptions::StencilMode::kLegacyClipIncrement;
}
- options.primitive_type = geometry_result.type;
- pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
- pass.SetStencilReference(entity.GetClipDepth());
-
- // Take the pre-populated vertex shader uniform struct and set managed
- // values.
- frame_info.depth = entity.GetShaderClipDepth();
- frame_info.mvp = geometry_result.transform;
+ if constexpr (ContentContext::kEnableStencilThenCover) {
+ pass.SetStencilReference(0);
+ } else {
+ pass.SetStencilReference(entity.GetClipDepth());
+ }
VertexShaderT::BindFrameInfo(
pass, renderer.GetTransientsBuffer().EmplaceUniform(frame_info));
@@ -159,7 +223,13 @@
if (geometry_result.mode == GeometryResult::Mode::kPreventOverdraw) {
auto restore = ClipRestoreContents();
restore.SetRestoreCoverage(GetCoverage(entity));
- return restore.Render(renderer, entity, pass);
+ if constexpr (ContentContext::kEnableStencilThenCover) {
+ Entity restore_entity = entity.Clone();
+ restore_entity.SetClipDepth(0);
+ return restore.Render(renderer, restore_entity, pass);
+ } else {
+ return restore.Render(renderer, entity, pass);
+ }
}
return true;
}
diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc
index 05397d3..270e817 100644
--- a/impeller/entity/contents/content_context.cc
+++ b/impeller/entity/contents/content_context.cc
@@ -164,28 +164,32 @@
front_stencil.depth_stencil_pass = StencilOperation::kKeep;
desc.SetStencilAttachmentDescriptors(front_stencil);
break;
- case StencilMode::kSetToRef:
- front_stencil.stencil_compare = CompareFunction::kEqual;
- front_stencil.depth_stencil_pass = StencilOperation::kKeep;
- front_stencil.stencil_failure = StencilOperation::kSetToReferenceValue;
- desc.SetStencilAttachmentDescriptors(front_stencil);
- break;
- case StencilMode::kNonZeroWrite:
+ case StencilMode::kStencilNonZeroFill:
+ // The stencil ref should be 0 on commands that use this mode.
front_stencil.stencil_compare = CompareFunction::kAlways;
front_stencil.depth_stencil_pass = StencilOperation::kIncrementWrap;
back_stencil.stencil_compare = CompareFunction::kAlways;
back_stencil.depth_stencil_pass = StencilOperation::kDecrementWrap;
desc.SetStencilAttachmentDescriptors(front_stencil, back_stencil);
break;
- case StencilMode::kEvenOddWrite:
+ case StencilMode::kStencilEvenOddFill:
+ // The stencil ref should be 0 on commands that use this mode.
front_stencil.stencil_compare = CompareFunction::kEqual;
front_stencil.depth_stencil_pass = StencilOperation::kIncrementWrap;
front_stencil.stencil_failure = StencilOperation::kDecrementWrap;
desc.SetStencilAttachmentDescriptors(front_stencil);
break;
case StencilMode::kCoverCompare:
+ // The stencil ref should be 0 on commands that use this mode.
front_stencil.stencil_compare = CompareFunction::kNotEqual;
- front_stencil.depth_stencil_pass = StencilOperation::kKeep;
+ front_stencil.depth_stencil_pass =
+ StencilOperation::kSetToReferenceValue;
+ desc.SetStencilAttachmentDescriptors(front_stencil);
+ break;
+ case StencilMode::kCoverCompareInverted:
+ // The stencil ref should be 0 on commands that use this mode.
+ front_stencil.stencil_compare = CompareFunction::kEqual;
+ front_stencil.stencil_failure = StencilOperation::kSetToReferenceValue;
desc.SetStencilAttachmentDescriptors(front_stencil);
break;
case StencilMode::kLegacyClipRestore:
diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h
index 4b813e9..67e8407 100644
--- a/impeller/entity/contents/content_context.h
+++ b/impeller/entity/contents/content_context.h
@@ -287,21 +287,34 @@
/// but they shouldn't require e.g. 10s of thousands.
struct ContentContextOptions {
enum class StencilMode : uint8_t {
- // Operations used for stencil-then-cover
-
/// Turn the stencil test off. Used when drawing without stencil-then-cover.
kIgnore,
- /// Overwrite the stencil content to the ref value. Used for resetting the
- /// stencil buffer after a stencil-then-cover operation.
- kSetToRef,
- /// Draw the stencil for the nonzero fill path rule.
- kNonZeroWrite,
- /// Draw the stencil for the evenoff fill path rule.
- kEvenOddWrite,
+
+ // Operations used for stencil-then-cover
+
+ /// Draw the stencil for the NonZero fill path rule.
+ ///
+ /// The stencil ref should always be 0 on commands using this mode.
+ kStencilNonZeroFill,
+ /// Draw the stencil for the EvenOdd fill path rule.
+ ///
+ /// The stencil ref should always be 0 on commands using this mode.
+ kStencilEvenOddFill,
/// Used for draw calls which fill in the stenciled area. Intended to be
- /// used after `kNonZeroWrite` or `kEvenOddWrite` is used to set up the
- /// stencil buffer.
+ /// used after `kStencilNonZeroFill` or `kStencilEvenOddFill` is used to set
+ /// up the stencil buffer. Also cleans up the stencil buffer by resetting
+ /// everything to zero.
+ ///
+ /// The stencil ref should always be 0 on commands using this mode.
kCoverCompare,
+ /// The opposite of `kCoverCompare`. Used for draw calls which fill in the
+ /// non-stenciled area (intersection clips). Intended to be used after
+ /// `kStencilNonZeroFill` or `kStencilEvenOddFill` is used to set up the
+ /// stencil buffer. Also cleans up the stencil buffer by resetting
+ /// everything to zero.
+ ///
+ /// The stencil ref should always be 0 on commands using this mode.
+ kCoverCompareInverted,
// Operations to control the legacy clip implementation, which forms a
// heightmap on the stencil buffer.
@@ -387,6 +400,16 @@
bool IsValid() const;
+ /// This setting does two things:
+ /// 1. Enables clipping with the depth buffer, freeing up the stencil buffer.
+ /// See also: https://github.com/flutter/flutter/issues/138460
+ /// 2. Switches the generic tessellation fallback to use stencil-then-cover.
+ /// See also: https://github.com/flutter/flutter/issues/123671
+ ///
+ // TODO(bdero): Remove this setting once StC is fully de-risked
+ // https://github.com/flutter/flutter/issues/123671
+ static constexpr bool kEnableStencilThenCover = false;
+
#if IMPELLER_ENABLE_3D
std::shared_ptr<scene::SceneContext> GetSceneContext() const;
#endif // IMPELLER_ENABLE_3D
diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc
index 681c160..b71ceca 100644
--- a/impeller/entity/contents/contents.cc
+++ b/impeller/entity/contents/contents.cc
@@ -27,6 +27,10 @@
FML_DCHECK(pass.HasDepthAttachment() == pass.HasStencilAttachment());
opts.has_depth_stencil_attachments = has_depth_stencil_attachments;
+ if constexpr (ContentContext::kEnableStencilThenCover) {
+ opts.depth_compare = CompareFunction::kGreater;
+ opts.stencil_mode = ContentContextOptions::StencilMode::kIgnore;
+ }
return opts;
}
diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc
index e1f26f6..472f4b5 100644
--- a/impeller/entity/entity.cc
+++ b/impeller/entity/entity.cc
@@ -110,7 +110,7 @@
static const Scalar kDepthEpsilon = 1.0f / std::pow(2, 18);
float Entity::GetShaderClipDepth() const {
- return new_clip_depth_ * kDepthEpsilon;
+ return std::clamp(new_clip_depth_ * kDepthEpsilon, 0.0f, 1.0f);
}
void Entity::IncrementStencilDepth(uint32_t increment) {
diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc
index 608545b..76a5d31 100644
--- a/impeller/entity/entity_pass.cc
+++ b/impeller/entity/entity_pass.cc
@@ -4,6 +4,7 @@
#include "impeller/entity/entity_pass.h"
+#include <limits>
#include <memory>
#include <utility>
#include <variant>
@@ -765,6 +766,7 @@
Entity msaa_backdrop_entity;
msaa_backdrop_entity.SetContents(std::move(msaa_backdrop_contents));
msaa_backdrop_entity.SetBlendMode(BlendMode::kSource);
+ msaa_backdrop_entity.SetNewClipDepth(std::numeric_limits<uint32_t>::max());
if (!msaa_backdrop_entity.Render(renderer, *result.pass)) {
VALIDATION_LOG << "Failed to render MSAA backdrop filter entity.";
return false;
@@ -923,6 +925,7 @@
backdrop_entity.SetTransform(
Matrix::MakeTranslation(Vector3(-local_pass_position)));
backdrop_entity.SetClipDepth(clip_depth_floor);
+ backdrop_entity.SetNewClipDepth(std::numeric_limits<uint32_t>::max());
RenderElement(backdrop_entity, clip_depth_floor, pass_context, pass_depth,
renderer, clip_coverage_stack, global_pass_position);
diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc
index e5a6adc..07b065d 100644
--- a/impeller/entity/entity_unittests.cc
+++ b/impeller/entity/entity_unittests.cc
@@ -2758,6 +2758,40 @@
}
#endif
+TEST_P(EntityTest, FillPathGeometryGetPositionBufferReturnsExpectedMode) {
+ RenderTarget target;
+ testing::MockRenderPass mock_pass(GetContext(), target);
+
+ auto get_result = [this, &mock_pass](const Path& path) {
+ auto geometry = Geometry::MakeFillPath(
+ path, /* inner rect */ Rect::MakeLTRB(0, 0, 100, 100));
+ return geometry->GetPositionBuffer(*GetContentContext(), {}, mock_pass);
+ };
+
+ // Convex path
+ {
+ GeometryResult result =
+ get_result(PathBuilder{}
+ .AddRect(Rect::MakeLTRB(0, 0, 100, 100))
+ .SetConvexity(Convexity::kConvex)
+ .TakePath());
+ EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal);
+ }
+
+ // Concave path
+ {
+ Path path = PathBuilder{}
+ .MoveTo({0, 0})
+ .LineTo({100, 0})
+ .LineTo({100, 100})
+ .LineTo({50, 50})
+ .Close()
+ .TakePath();
+ GeometryResult result = get_result(path);
+ EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal);
+ }
+}
+
} // namespace testing
} // namespace impeller
diff --git a/impeller/entity/geometry/fill_path_geometry.cc b/impeller/entity/geometry/fill_path_geometry.cc
index 5cd721a..959d858 100644
--- a/impeller/entity/geometry/fill_path_geometry.cc
+++ b/impeller/entity/geometry/fill_path_geometry.cc
@@ -3,7 +3,11 @@
// found in the LICENSE file.
#include "impeller/entity/geometry/fill_path_geometry.h"
+
+#include "fml/logging.h"
#include "impeller/core/formats.h"
+#include "impeller/entity/contents/content_context.h"
+#include "impeller/entity/geometry/geometry.h"
namespace impeller {
@@ -11,6 +15,21 @@
std::optional<Rect> inner_rect)
: path_(path), inner_rect_(inner_rect) {}
+static GeometryResult::Mode GetGeometryMode(const Path& path) {
+ if (!ContentContext::kEnableStencilThenCover || path.IsConvex()) {
+ return GeometryResult::Mode::kNormal;
+ }
+
+ switch (path.GetFillType()) {
+ case FillType::kNonZero:
+ return GeometryResult::Mode::kNonZero;
+ case FillType::kOdd:
+ return GeometryResult::Mode::kEvenOdd;
+ }
+
+ FML_UNREACHABLE();
+}
+
GeometryResult FillPathGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
@@ -18,49 +37,51 @@
auto& host_buffer = renderer.GetTransientsBuffer();
VertexBuffer vertex_buffer;
- if (path_.GetFillType() == FillType::kNonZero && //
- path_.IsConvex()) {
- auto points = renderer.GetTessellator()->TessellateConvex(
- path_, entity.GetTransform().GetMaxBasisLength());
-
- vertex_buffer.vertex_buffer = host_buffer.Emplace(
- points.data(), points.size() * sizeof(Point), alignof(Point));
- vertex_buffer.index_buffer = {}, vertex_buffer.vertex_count = points.size();
- vertex_buffer.index_type = IndexType::kNone;
-
- return GeometryResult{
- .type = PrimitiveType::kTriangleStrip,
- .vertex_buffer = vertex_buffer,
- .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
- };
+ if constexpr (!ContentContext::kEnableStencilThenCover) {
+ if (!path_.IsConvex()) {
+ auto tesselation_result = renderer.GetTessellator()->Tessellate(
+ path_, entity.GetTransform().GetMaxBasisLength(),
+ [&vertex_buffer, &host_buffer](
+ const float* vertices, size_t vertices_count,
+ const uint16_t* indices, size_t indices_count) {
+ vertex_buffer.vertex_buffer = host_buffer.Emplace(
+ vertices, vertices_count * sizeof(float) * 2, alignof(float));
+ if (indices != nullptr) {
+ vertex_buffer.index_buffer = host_buffer.Emplace(
+ indices, indices_count * sizeof(uint16_t), alignof(uint16_t));
+ vertex_buffer.vertex_count = indices_count;
+ vertex_buffer.index_type = IndexType::k16bit;
+ } else {
+ vertex_buffer.index_buffer = {};
+ vertex_buffer.vertex_count = vertices_count;
+ vertex_buffer.index_type = IndexType::kNone;
+ }
+ return true;
+ });
+ if (tesselation_result != Tessellator::Result::kSuccess) {
+ return {};
+ }
+ return GeometryResult{
+ .type = PrimitiveType::kTriangle,
+ .vertex_buffer = vertex_buffer,
+ .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
+ };
+ }
}
- auto tesselation_result = renderer.GetTessellator()->Tessellate(
- path_, entity.GetTransform().GetMaxBasisLength(),
- [&vertex_buffer, &host_buffer](
- const float* vertices, size_t vertices_count, const uint16_t* indices,
- size_t indices_count) {
- vertex_buffer.vertex_buffer = host_buffer.Emplace(
- vertices, vertices_count * sizeof(float) * 2, alignof(float));
- if (indices != nullptr) {
- vertex_buffer.index_buffer = host_buffer.Emplace(
- indices, indices_count * sizeof(uint16_t), alignof(uint16_t));
- vertex_buffer.vertex_count = indices_count;
- vertex_buffer.index_type = IndexType::k16bit;
- } else {
- vertex_buffer.index_buffer = {};
- vertex_buffer.vertex_count = vertices_count;
- vertex_buffer.index_type = IndexType::kNone;
- }
- return true;
- });
- if (tesselation_result != Tessellator::Result::kSuccess) {
- return {};
- }
+ auto points = renderer.GetTessellator()->TessellateConvex(
+ path_, entity.GetTransform().GetMaxBasisLength());
+
+ vertex_buffer.vertex_buffer = host_buffer.Emplace(
+ points.data(), points.size() * sizeof(Point), alignof(Point));
+ vertex_buffer.index_buffer = {}, vertex_buffer.vertex_count = points.size();
+ vertex_buffer.index_type = IndexType::kNone;
+
return GeometryResult{
- .type = PrimitiveType::kTriangle,
+ .type = PrimitiveType::kTriangleStrip,
.vertex_buffer = vertex_buffer,
.transform = pass.GetOrthographicTransform() * entity.GetTransform(),
+ .mode = GetGeometryMode(path_),
};
}
@@ -76,57 +97,59 @@
auto uv_transform =
texture_coverage.GetNormalizingTransform() * effect_transform;
- if (path_.GetFillType() == FillType::kNonZero && //
- path_.IsConvex()) {
- auto points = renderer.GetTessellator()->TessellateConvex(
- path_, entity.GetTransform().GetMaxBasisLength());
-
- VertexBufferBuilder<VS::PerVertexData> vertex_builder;
- vertex_builder.Reserve(points.size());
- for (auto i = 0u; i < points.size(); i++) {
- VS::PerVertexData data;
- data.position = points[i];
- data.texture_coords = uv_transform * points[i];
- vertex_builder.AppendVertex(data);
+ if constexpr (!ContentContext::kEnableStencilThenCover) {
+ if (!path_.IsConvex()) {
+ VertexBufferBuilder<VS::PerVertexData> vertex_builder;
+ auto tesselation_result = renderer.GetTessellator()->Tessellate(
+ path_, entity.GetTransform().GetMaxBasisLength(),
+ [&vertex_builder, &uv_transform](
+ const float* vertices, size_t vertices_count,
+ const uint16_t* indices, size_t indices_count) {
+ for (auto i = 0u; i < vertices_count * 2; i += 2) {
+ VS::PerVertexData data;
+ Point vtx = {vertices[i], vertices[i + 1]};
+ data.position = vtx;
+ data.texture_coords = uv_transform * vtx;
+ vertex_builder.AppendVertex(data);
+ }
+ FML_DCHECK(vertex_builder.GetVertexCount() == vertices_count);
+ if (indices != nullptr) {
+ 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(renderer.GetTransientsBuffer()),
+ .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
+ };
}
-
- return GeometryResult{
- .type = PrimitiveType::kTriangleStrip,
- .vertex_buffer =
- vertex_builder.CreateVertexBuffer(renderer.GetTransientsBuffer()),
- .transform = pass.GetOrthographicTransform() * entity.GetTransform(),
- };
}
+ auto points = renderer.GetTessellator()->TessellateConvex(
+ path_, entity.GetTransform().GetMaxBasisLength());
+
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
- auto tesselation_result = renderer.GetTessellator()->Tessellate(
- path_, entity.GetTransform().GetMaxBasisLength(),
- [&vertex_builder, &uv_transform](
- const float* vertices, size_t vertices_count, const uint16_t* indices,
- size_t indices_count) {
- for (auto i = 0u; i < vertices_count * 2; i += 2) {
- VS::PerVertexData data;
- Point vtx = {vertices[i], vertices[i + 1]};
- data.position = vtx;
- data.texture_coords = uv_transform * vtx;
- vertex_builder.AppendVertex(data);
- }
- FML_DCHECK(vertex_builder.GetVertexCount() == vertices_count);
- if (indices != nullptr) {
- for (auto i = 0u; i < indices_count; i++) {
- vertex_builder.AppendIndex(indices[i]);
- }
- }
- return true;
- });
- if (tesselation_result != Tessellator::Result::kSuccess) {
- return {};
+ vertex_builder.Reserve(points.size());
+ for (auto i = 0u; i < points.size(); i++) {
+ VS::PerVertexData data;
+ data.position = points[i];
+ data.texture_coords = uv_transform * points[i];
+ vertex_builder.AppendVertex(data);
}
+
return GeometryResult{
- .type = PrimitiveType::kTriangle,
+ .type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
vertex_builder.CreateVertexBuffer(renderer.GetTransientsBuffer()),
.transform = pass.GetOrthographicTransform() * entity.GetTransform(),
+ .mode = GetGeometryMode(path_),
};
}
diff --git a/impeller/entity/inline_pass_context.cc b/impeller/entity/inline_pass_context.cc
index d9258ac..7fcaf51 100644
--- a/impeller/entity/inline_pass_context.cc
+++ b/impeller/entity/inline_pass_context.cc
@@ -146,6 +146,18 @@
color0.store_action =
is_msaa ? StoreAction::kMultisampleResolve : StoreAction::kStore;
+ if (ContentContext::kEnableStencilThenCover) {
+ auto depth = pass_target_.GetRenderTarget().GetDepthAttachment();
+ if (!depth.has_value()) {
+ VALIDATION_LOG << "Depth attachment unexpectedly missing from the "
+ "EntityPass render target.";
+ return {};
+ }
+ depth->load_action = LoadAction::kClear;
+ depth->store_action = StoreAction::kDontCare;
+ pass_target_.target_.SetDepthAttachment(depth.value());
+ }
+
auto stencil = pass_target_.GetRenderTarget().GetStencilAttachment();
if (!stencil.has_value()) {
VALIDATION_LOG << "Stencil attachment unexpectedly missing from the "