mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Add StC color source/clip behavior + constexpr flag for enabling StC. (flutter/engine#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.
This commit is contained in:
parent
dac2e3f861
commit
0aa0513471
@ -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;
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -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)");
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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
|
||||
******************************************************************************/
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 @@ 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
|
||||
@ -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));
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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.
|
||||
@ -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
|
||||
|
||||
@ -27,6 +27,10 @@ ContentContextOptions OptionsFromPass(const RenderPass& pass) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -110,7 +110,7 @@ uint32_t Entity::GetNewClipDepth() const {
|
||||
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) {
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "impeller/entity/entity_pass.h"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
@ -765,6 +766,7 @@ bool EntityPass::RenderElement(Entity& element_entity,
|
||||
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 @@ bool EntityPass::OnRender(
|
||||
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);
|
||||
|
||||
@ -2758,6 +2758,40 @@ TEST_P(EntityTest, FramebufferFetchVulkanBindingOffsetIsTheSame) {
|
||||
}
|
||||
#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
|
||||
|
||||
|
||||
@ -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 @@ FillPathGeometry::FillPathGeometry(const Path& path,
|
||||
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 @@ GeometryResult FillPathGeometry::GetPositionBuffer(
|
||||
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 @@ GeometryResult FillPathGeometry::GetPositionUVBuffer(
|
||||
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_),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -146,6 +146,18 @@ InlinePassContext::RenderPassResult InlinePassContext::GetRenderPass(
|
||||
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 "
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user