blob: 146ba4eeb5ca184e0c9b47b44fe57c8f90fe24fb [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 <cmath>
#include <optional>
#include "fml/logging.h"
#include "impeller/core/formats.h"
#include "impeller/core/vertex_buffer.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/pipelines.h"
#include "impeller/entity/entity.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/vertex_buffer_builder.h"
namespace impeller {
static Scalar GetShaderClipDepth(uint32_t clip_depth) {
// Draw the clip at the max of the clip entity's depth slice, so that other
// draw calls with this same depth value will be culled even if they have a
// perspective transform.
return std::nextafterf(Entity::GetShaderClipDepth(clip_depth + 1), 0.0f);
}
/*******************************************************************************
******* ClipContents
******************************************************************************/
ClipContents::ClipContents(Rect coverage_rect, bool is_axis_aligned_rect)
: coverage_rect_(coverage_rect),
is_axis_aligned_rect_(is_axis_aligned_rect) {}
ClipContents::~ClipContents() = default;
void ClipContents::SetGeometry(GeometryResult clip_geometry) {
clip_geometry_ = std::move(clip_geometry);
}
void ClipContents::SetClipOperation(Entity::ClipOperation clip_op) {
clip_op_ = clip_op;
}
ClipCoverage ClipContents::GetClipCoverage(
const std::optional<Rect>& current_clip_coverage) const {
if (!current_clip_coverage.has_value()) {
return ClipCoverage{.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 {
.is_difference_or_non_square = true, //
.coverage = current_clip_coverage //
};
case Entity::ClipOperation::kIntersect:
if (coverage_rect_.IsEmpty() || !current_clip_coverage.has_value()) {
return ClipCoverage{.coverage = std::nullopt};
}
return {
.is_difference_or_non_square = !is_axis_aligned_rect_, //
.coverage = current_clip_coverage->Intersection(coverage_rect_), //
};
}
FML_UNREACHABLE();
}
bool ClipContents::Render(const ContentContext& renderer,
RenderPass& pass,
uint32_t clip_depth) const {
if (!clip_geometry_.vertex_buffer) {
return true;
}
using VS = ClipPipeline::VertexShader;
VS::FrameInfo info;
info.depth = GetShaderClipDepth(clip_depth);
info.mvp = clip_geometry_.transform;
auto options = OptionsFromPass(pass);
options.blend_mode = BlendMode::kDst;
options.primitive_type = clip_geometry_.type;
pass.SetVertexBuffer(clip_geometry_.vertex_buffer);
// kNormal and kPreventOverdraw geometries can do a difference clip by writing
// depth directly without stencil-and-cover.
if ((clip_geometry_.mode == GeometryResult::Mode::kNormal ||
clip_geometry_.mode == GeometryResult::Mode::kPreventOverdraw) &&
clip_op_ == Entity::ClipOperation::kDifference) {
options.depth_write_enabled = true;
pass.SetPipeline(renderer.GetClipPipeline(options));
VS::BindFrameInfo(pass,
renderer.GetTransientsDataBuffer().EmplaceUniform(info));
return pass.Draw().ok();
}
/// Stencil preparation draw.
pass.SetStencilReference(0);
options.depth_write_enabled = false;
switch (clip_geometry_.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::kStencilIncrementAll;
break;
}
pass.SetPipeline(renderer.GetClipPipeline(options));
VS::BindFrameInfo(pass,
renderer.GetTransientsDataBuffer().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;
cover_area = coverage_rect_;
break;
}
auto points = cover_area.GetPoints();
pass.SetVertexBuffer(
CreateVertexBuffer(points, renderer.GetTransientsDataBuffer()));
pass.SetPipeline(renderer.GetClipPipeline(options));
info.mvp = pass.GetOrthographicTransform();
VS::BindFrameInfo(pass,
renderer.GetTransientsDataBuffer().EmplaceUniform(info));
return pass.Draw().ok();
}
} // namespace impeller