Skip to content

Commit

Permalink
[Impeller] Add StC color source/clip behavior + constexpr flag for en…
Browse files Browse the repository at this point in the history
…abling 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.
  • Loading branch information
bdero authored Feb 22, 2024
1 parent 00aaffc commit 2e09c41
Show file tree
Hide file tree
Showing 12 changed files with 404 additions and 118 deletions.
7 changes: 4 additions & 3 deletions impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,10 @@ bool Canvas::Restore() {
}

transform_stack_.pop_back();
if (num_clips > 0) {
RestoreClip();
if constexpr (!ContentContext::kEnableStencilThenCover) {
if (num_clips > 0) {
RestoreClip();
}
}

return true;
Expand Down Expand Up @@ -597,7 +599,6 @@ void Canvas::RestoreClip() {
entity.SetContents(std::make_shared<ClipRestoreContents>());
entity.SetClipDepth(GetClipDepth());

// TODO(bdero): To be removed when swapping the clip strategy.
AddEntityToCurrentPass(std::move(entity));
}

Expand Down
111 changes: 106 additions & 5 deletions impeller/entity/contents/clip_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,97 @@ bool ClipContents::CanInheritOpacity(const Entity& entity) const {

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;
Expand All @@ -85,7 +173,7 @@ bool ClipContents::Render(const ContentContext& renderer,
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)");

Expand Down Expand Up @@ -123,7 +211,7 @@ bool ClipContents::Render(const ContentContext& renderer,
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));

Expand All @@ -135,6 +223,19 @@ bool ClipContents::Render(const ContentContext& renderer,
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
******************************************************************************/
Expand Down
11 changes: 11 additions & 0 deletions impeller/entity/contents/clip_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ class ClipContents final : public Contents {
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;

Expand Down
88 changes: 79 additions & 9 deletions impeller/entity/contents/color_source_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -118,6 +120,71 @@ class ColorSourceContents : public Contents {
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
Expand All @@ -127,14 +194,11 @@ class ColorSourceContents : public Contents {
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));
Expand All @@ -159,7 +223,13 @@ class ColorSourceContents : public Contents {
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;
}
Expand Down
22 changes: 13 additions & 9 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -164,28 +164,32 @@ void ContentContextOptions::ApplyToPipelineDescriptor(
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:
Expand Down
45 changes: 34 additions & 11 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,21 +287,34 @@ struct PendingCommandBuffers {
/// 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.
Expand Down Expand Up @@ -387,6 +400,16 @@ class ContentContext {

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
Expand Down
Loading

0 comments on commit 2e09c41

Please sign in to comment.