blob: 77bbccc82f0c7ad1bed384fb282b7c38df4fc8d7 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include "fml/logging.h"
#include "impeller/core/formats.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/vertex_buffer_builder.h"
namespace impeller {
/*******************************************************************************
******* ClipContents
******************************************************************************/
ClipContents::ClipContents() = default;
ClipContents::~ClipContents() = default;
void ClipContents::SetGeometry(std::unique_ptr<Geometry> geometry) {
geometry_ = std::move(geometry);
}
void ClipContents::SetClipOperation(Entity::ClipOperation clip_op) {
clip_op_ = clip_op;
}
std::optional<Rect> ClipContents::GetCoverage(const Entity& entity) const {
return std::nullopt;
};
Contents::StencilCoverage ClipContents::GetStencilCoverage(
const Entity& entity,
const std::optional<Rect>& current_stencil_coverage) const {
if (!current_stencil_coverage.has_value()) {
return {.type = StencilCoverage::Type::kAppend, .coverage = std::nullopt};
}
switch (clip_op_) {
case Entity::ClipOperation::kDifference:
// This can be optimized further by considering cases when the bounds of
// the current stencil will shrink.
return {.type = StencilCoverage::Type::kAppend,
.coverage = current_stencil_coverage};
case Entity::ClipOperation::kIntersect:
if (!geometry_) {
return {.type = StencilCoverage::Type::kAppend,
.coverage = std::nullopt};
}
auto coverage = geometry_->GetCoverage(entity.GetTransformation());
if (!coverage.has_value() || !current_stencil_coverage.has_value()) {
return {.type = StencilCoverage::Type::kAppend,
.coverage = std::nullopt};
}
return {
.type = StencilCoverage::Type::kAppend,
.coverage = current_stencil_coverage->Intersection(coverage.value()),
};
}
FML_UNREACHABLE();
}
bool ClipContents::ShouldRender(
const Entity& entity,
const std::optional<Rect> stencil_coverage) const {
return true;
}
bool ClipContents::CanInheritOpacity(const Entity& entity) const {
return true;
}
void ClipContents::SetInheritedOpacity(Scalar opacity) {}
bool ClipContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = ClipPipeline::VertexShader;
VS::FrameInfo info;
Command cmd;
auto options = OptionsFromPass(pass);
options.blend_mode = BlendMode::kDestination;
cmd.stencil_reference = entity.GetStencilDepth();
options.stencil_compare = CompareFunction::kEqual;
options.stencil_operation = StencilOperation::kIncrementClamp;
if (clip_op_ == Entity::ClipOperation::kDifference) {
{
DEBUG_COMMAND_INFO(cmd, "Difference Clip (Increment)");
auto points = Rect(Size(pass.GetRenderTargetSize())).GetPoints();
auto vertices =
VertexBufferBuilder<VS::PerVertexData>{}
.AddVertices({{points[0]}, {points[1]}, {points[2]}, {points[3]}})
.CreateVertexBuffer(pass.GetTransientsBuffer());
cmd.BindVertices(vertices);
info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize());
VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info));
options.primitive_type = PrimitiveType::kTriangleStrip;
cmd.pipeline = renderer.GetClipPipeline(options);
pass.AddCommand(Command(cmd));
}
{
DEBUG_COMMAND_INFO(cmd, "Difference Clip (Punch)");
cmd.stencil_reference = entity.GetStencilDepth() + 1;
options.stencil_compare = CompareFunction::kEqual;
options.stencil_operation = StencilOperation::kDecrementClamp;
}
} else {
DEBUG_COMMAND_INFO(cmd, "Intersect Clip");
options.stencil_compare = CompareFunction::kEqual;
options.stencil_operation = StencilOperation::kIncrementClamp;
}
auto geometry_result = geometry_->GetPositionBuffer(renderer, entity, pass);
options.primitive_type = geometry_result.type;
cmd.pipeline = renderer.GetClipPipeline(options);
auto allocator = renderer.GetContext()->GetResourceAllocator();
cmd.BindVertices(geometry_result.vertex_buffer);
info.mvp = geometry_result.transform;
VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info));
pass.AddCommand(std::move(cmd));
return true;
}
/*******************************************************************************
******* ClipRestoreContents
******************************************************************************/
ClipRestoreContents::ClipRestoreContents() = default;
ClipRestoreContents::~ClipRestoreContents() = default;
void ClipRestoreContents::SetRestoreCoverage(
std::optional<Rect> restore_coverage) {
restore_coverage_ = restore_coverage;
}
std::optional<Rect> ClipRestoreContents::GetCoverage(
const Entity& entity) const {
return std::nullopt;
};
Contents::StencilCoverage ClipRestoreContents::GetStencilCoverage(
const Entity& entity,
const std::optional<Rect>& current_stencil_coverage) const {
return {.type = StencilCoverage::Type::kRestore, .coverage = std::nullopt};
}
bool ClipRestoreContents::ShouldRender(
const Entity& entity,
const std::optional<Rect> stencil_coverage) const {
return true;
}
bool ClipRestoreContents::CanInheritOpacity(const Entity& entity) const {
return true;
}
void ClipRestoreContents::SetInheritedOpacity(Scalar opacity) {}
bool ClipRestoreContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = ClipPipeline::VertexShader;
Command cmd;
DEBUG_COMMAND_INFO(cmd, "Restore Clip");
auto options = OptionsFromPass(pass);
options.blend_mode = BlendMode::kDestination;
options.stencil_compare = CompareFunction::kLess;
options.stencil_operation = StencilOperation::kSetToReferenceValue;
options.primitive_type = PrimitiveType::kTriangleStrip;
cmd.pipeline = renderer.GetClipPipeline(options);
cmd.stencil_reference = entity.GetStencilDepth();
// Create a rect that covers either the given restore area, or the whole
// render target texture.
auto ltrb = restore_coverage_.value_or(Rect(Size(pass.GetRenderTargetSize())))
.GetLTRB();
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{Point(ltrb[0], ltrb[1])},
{Point(ltrb[2], ltrb[1])},
{Point(ltrb[0], ltrb[3])},
{Point(ltrb[2], ltrb[3])},
});
cmd.BindVertices(vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
VS::FrameInfo info;
info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize());
VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info));
pass.AddCommand(std::move(cmd));
return true;
}
}; // namespace impeller