mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] take advantage of DisplayList culling (flutter/engine#41606)
Switching the calls to dispatch into an Impeller Dispatcher to use a cull rect to enable pre-culling of the out-of-bounds ops. This change showed an improvement of around 2x on the rendering performance of the non-intersecting platform view benchmark, but that was measured without the recent changes to the destructive blend modes in Impeller renderer.
This commit is contained in:
parent
3b8e1ec80f
commit
6ad6014776
@ -120,6 +120,7 @@
|
||||
../../../flutter/impeller/.gitignore
|
||||
../../../flutter/impeller/README.md
|
||||
../../../flutter/impeller/aiks/aiks_unittests.cc
|
||||
../../../flutter/impeller/aiks/canvas_unittests.cc
|
||||
../../../flutter/impeller/archivist/archivist_unittests.cc
|
||||
../../../flutter/impeller/base/README.md
|
||||
../../../flutter/impeller/base/base_unittests.cc
|
||||
|
||||
@ -140,6 +140,11 @@ void DisplayList::Dispatch(DlOpReceiver& receiver) const {
|
||||
Dispatch(receiver, ptr, ptr + byte_count_, NopCuller::instance);
|
||||
}
|
||||
|
||||
void DisplayList::Dispatch(DlOpReceiver& receiver,
|
||||
const SkIRect& cull_rect) const {
|
||||
Dispatch(receiver, SkRect::Make(cull_rect));
|
||||
}
|
||||
|
||||
void DisplayList::Dispatch(DlOpReceiver& receiver,
|
||||
const SkRect& cull_rect) const {
|
||||
if (cull_rect.isEmpty()) {
|
||||
|
||||
@ -235,6 +235,7 @@ class DisplayList : public SkRefCnt {
|
||||
|
||||
void Dispatch(DlOpReceiver& ctx) const;
|
||||
void Dispatch(DlOpReceiver& ctx, const SkRect& cull_rect) const;
|
||||
void Dispatch(DlOpReceiver& ctx, const SkIRect& cull_rect) const;
|
||||
|
||||
// From historical behavior, SkPicture always included nested bytes,
|
||||
// but nested ops are only included if requested. The defaults used
|
||||
|
||||
@ -2532,81 +2532,79 @@ TEST_F(DisplayListTest, RTreeRenderCulling) {
|
||||
main_receiver.drawRect({20, 20, 30, 30});
|
||||
auto main = main_builder.Build();
|
||||
|
||||
auto test = [main](SkIRect cull_rect, const sk_sp<DisplayList>& expected) {
|
||||
{ // Test SkIRect culling
|
||||
DisplayListBuilder culling_builder;
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
}
|
||||
|
||||
{ // Test SkRect culling
|
||||
DisplayListBuilder culling_builder;
|
||||
main->Dispatch(ToReceiver(culling_builder), SkRect::Make(cull_rect));
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
}
|
||||
};
|
||||
|
||||
{ // No rects
|
||||
SkRect cull_rect = {11, 11, 19, 19};
|
||||
SkIRect cull_rect = {11, 11, 19, 19};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
DisplayListBuilder culling_builder(cull_rect);
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 1
|
||||
SkRect cull_rect = {9, 9, 19, 19};
|
||||
SkIRect cull_rect = {9, 9, 19, 19};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({0, 0, 10, 10});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
DisplayListBuilder culling_builder(cull_rect);
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 2
|
||||
SkRect cull_rect = {11, 9, 21, 19};
|
||||
SkIRect cull_rect = {11, 9, 21, 19};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({20, 0, 30, 10});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
DisplayListBuilder culling_builder(cull_rect);
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 3
|
||||
SkRect cull_rect = {9, 11, 19, 21};
|
||||
SkIRect cull_rect = {9, 11, 19, 21};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({0, 20, 10, 30});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
DisplayListBuilder culling_builder(cull_rect);
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // Rect 4
|
||||
SkRect cull_rect = {11, 11, 21, 21};
|
||||
SkIRect cull_rect = {11, 11, 21, 21};
|
||||
|
||||
DisplayListBuilder expected_builder;
|
||||
DlOpReceiver& expected_receiver = ToReceiver(expected_builder);
|
||||
expected_receiver.drawRect({20, 20, 30, 30});
|
||||
auto expected = expected_builder.Build();
|
||||
|
||||
DisplayListBuilder culling_builder(cull_rect);
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected));
|
||||
test(cull_rect, expected);
|
||||
}
|
||||
|
||||
{ // All 4 rects
|
||||
SkRect cull_rect = {9, 9, 21, 21};
|
||||
SkIRect cull_rect = {9, 9, 21, 21};
|
||||
|
||||
DisplayListBuilder culling_builder(cull_rect);
|
||||
main->Dispatch(ToReceiver(culling_builder), cull_rect);
|
||||
|
||||
EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), main));
|
||||
test(cull_rect, main);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,8 @@ SurfaceFrame::SurfaceFrame(sk_sp<SkSurface> surface,
|
||||
canvas_ = &adapter_;
|
||||
} else if (display_list_fallback) {
|
||||
FML_DCHECK(!frame_size.isEmpty());
|
||||
dl_builder_ = sk_make_sp<DisplayListBuilder>(SkRect::Make(frame_size));
|
||||
dl_builder_ =
|
||||
sk_make_sp<DisplayListBuilder>(SkRect::Make(frame_size), true);
|
||||
canvas_ = dl_builder_.get();
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +47,10 @@ impeller_component("aiks_playground") {
|
||||
|
||||
impeller_component("aiks_unittests") {
|
||||
testonly = true
|
||||
sources = [ "aiks_unittests.cc" ]
|
||||
sources = [
|
||||
"aiks_unittests.cc",
|
||||
"canvas_unittests.cc",
|
||||
]
|
||||
deps = [
|
||||
":aiks",
|
||||
":aiks_playground",
|
||||
|
||||
@ -23,15 +23,25 @@
|
||||
namespace impeller {
|
||||
|
||||
Canvas::Canvas() {
|
||||
Initialize();
|
||||
Initialize(std::nullopt);
|
||||
}
|
||||
|
||||
Canvas::Canvas(Rect cull_rect) {
|
||||
Initialize(cull_rect);
|
||||
}
|
||||
|
||||
Canvas::Canvas(IRect cull_rect) {
|
||||
Initialize(Rect::MakeLTRB(cull_rect.GetLeft(), cull_rect.GetTop(),
|
||||
cull_rect.GetRight(), cull_rect.GetBottom()));
|
||||
}
|
||||
|
||||
Canvas::~Canvas() = default;
|
||||
|
||||
void Canvas::Initialize() {
|
||||
void Canvas::Initialize(std::optional<Rect> cull_rect) {
|
||||
initial_cull_rect_ = cull_rect;
|
||||
base_pass_ = std::make_unique<EntityPass>();
|
||||
current_pass_ = base_pass_.get();
|
||||
xformation_stack_.emplace_back(CanvasStackEntry{});
|
||||
xformation_stack_.emplace_back(CanvasStackEntry{.cull_rect = cull_rect});
|
||||
lazy_glyph_atlas_ = std::make_shared<LazyGlyphAtlas>();
|
||||
FML_DCHECK(GetSaveCount() == 1u);
|
||||
FML_DCHECK(base_pass_->GetSubpassesDepth() == 1u);
|
||||
@ -54,6 +64,7 @@ void Canvas::Save(
|
||||
std::optional<EntityPass::BackdropFilterProc> backdrop_filter) {
|
||||
auto entry = CanvasStackEntry{};
|
||||
entry.xformation = xformation_stack_.back().xformation;
|
||||
entry.cull_rect = xformation_stack_.back().cull_rect;
|
||||
entry.stencil_depth = xformation_stack_.back().stencil_depth;
|
||||
if (create_subpass) {
|
||||
entry.is_subpass = true;
|
||||
@ -109,6 +120,15 @@ const Matrix& Canvas::GetCurrentTransformation() const {
|
||||
return xformation_stack_.back().xformation;
|
||||
}
|
||||
|
||||
const std::optional<Rect> Canvas::GetCurrentLocalCullingBounds() const {
|
||||
auto cull_rect = xformation_stack_.back().cull_rect;
|
||||
if (cull_rect.has_value()) {
|
||||
Matrix inverse = xformation_stack_.back().xformation.Invert();
|
||||
cull_rect = cull_rect.value().TransformBounds(inverse);
|
||||
}
|
||||
return cull_rect;
|
||||
}
|
||||
|
||||
void Canvas::Translate(const Vector3& offset) {
|
||||
Concat(Matrix::MakeTranslation(offset));
|
||||
}
|
||||
@ -258,16 +278,56 @@ void Canvas::DrawCircle(Point center, Scalar radius, const Paint& paint) {
|
||||
|
||||
void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {
|
||||
ClipGeometry(Geometry::MakeFillPath(path), clip_op);
|
||||
if (clip_op == Entity::ClipOperation::kIntersect) {
|
||||
auto bounds = path.GetBoundingBox();
|
||||
if (bounds.has_value()) {
|
||||
IntersectCulling(bounds.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::ClipRect(const Rect& rect, Entity::ClipOperation clip_op) {
|
||||
ClipGeometry(Geometry::MakeRect(rect), clip_op);
|
||||
switch (clip_op) {
|
||||
case Entity::ClipOperation::kIntersect:
|
||||
IntersectCulling(rect);
|
||||
break;
|
||||
case Entity::ClipOperation::kDifference:
|
||||
SubtractCulling(rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::ClipRRect(const Rect& rect,
|
||||
Scalar corner_radius,
|
||||
Entity::ClipOperation clip_op) {
|
||||
ClipGeometry(Geometry::MakeRRect(rect, corner_radius), clip_op);
|
||||
switch (clip_op) {
|
||||
case Entity::ClipOperation::kIntersect:
|
||||
IntersectCulling(rect);
|
||||
break;
|
||||
case Entity::ClipOperation::kDifference:
|
||||
if (corner_radius <= 0) {
|
||||
SubtractCulling(rect);
|
||||
} else {
|
||||
// We subtract the inner "tall" and "wide" rectangle pieces
|
||||
// that fit inside the corners which cover the greatest area
|
||||
// without involving the curved corners
|
||||
// Since this is a subtract operation, we can subtract each
|
||||
// rectangle piece individually without fear of interference.
|
||||
if (corner_radius * 2 < rect.size.width) {
|
||||
SubtractCulling(Rect::MakeLTRB(
|
||||
rect.GetLeft() + corner_radius, rect.GetTop(),
|
||||
rect.GetRight() - corner_radius, rect.GetBottom()));
|
||||
}
|
||||
if (corner_radius * 2 < rect.size.height) {
|
||||
SubtractCulling(Rect::MakeLTRB(
|
||||
rect.GetLeft(), rect.GetTop() + corner_radius, //
|
||||
rect.GetRight(), rect.GetBottom() - corner_radius));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::ClipGeometry(std::unique_ptr<Geometry> geometry,
|
||||
@ -287,6 +347,31 @@ void Canvas::ClipGeometry(std::unique_ptr<Geometry> geometry,
|
||||
xformation_stack_.back().contains_clips = true;
|
||||
}
|
||||
|
||||
void Canvas::IntersectCulling(Rect clip_rect) {
|
||||
clip_rect = clip_rect.TransformBounds(GetCurrentTransformation());
|
||||
std::optional<Rect>& cull_rect = xformation_stack_.back().cull_rect;
|
||||
if (cull_rect.has_value()) {
|
||||
cull_rect = cull_rect
|
||||
.value() //
|
||||
.Intersection(clip_rect) //
|
||||
.value_or(Rect{});
|
||||
} else {
|
||||
cull_rect = clip_rect;
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::SubtractCulling(Rect clip_rect) {
|
||||
std::optional<Rect>& cull_rect = xformation_stack_.back().cull_rect;
|
||||
if (cull_rect.has_value()) {
|
||||
clip_rect = clip_rect.TransformBounds(GetCurrentTransformation());
|
||||
cull_rect = cull_rect
|
||||
.value() //
|
||||
.Cutout(clip_rect) //
|
||||
.value_or(Rect{});
|
||||
}
|
||||
// else (no cull) diff (any clip) is non-rectangular
|
||||
}
|
||||
|
||||
void Canvas::RestoreClip() {
|
||||
Entity entity;
|
||||
entity.SetTransformation(GetCurrentTransformation());
|
||||
@ -364,7 +449,7 @@ Picture Canvas::EndRecordingAsPicture() {
|
||||
picture.pass = std::move(base_pass_);
|
||||
|
||||
Reset();
|
||||
Initialize();
|
||||
Initialize(initial_cull_rect_);
|
||||
|
||||
return picture;
|
||||
}
|
||||
|
||||
@ -28,6 +28,15 @@ namespace impeller {
|
||||
|
||||
class Entity;
|
||||
|
||||
struct CanvasStackEntry {
|
||||
Matrix xformation;
|
||||
// |cull_rect| is conservative screen-space bounds of the clipped output area
|
||||
std::optional<Rect> cull_rect;
|
||||
size_t stencil_depth = 0u;
|
||||
bool is_subpass = false;
|
||||
bool contains_clips = false;
|
||||
};
|
||||
|
||||
class Canvas {
|
||||
public:
|
||||
struct DebugOptions {
|
||||
@ -40,6 +49,10 @@ class Canvas {
|
||||
|
||||
Canvas();
|
||||
|
||||
explicit Canvas(Rect cull_rect);
|
||||
|
||||
explicit Canvas(IRect cull_rect);
|
||||
|
||||
~Canvas();
|
||||
|
||||
void Save();
|
||||
@ -57,6 +70,8 @@ class Canvas {
|
||||
|
||||
const Matrix& GetCurrentTransformation() const;
|
||||
|
||||
const std::optional<Rect> GetCurrentLocalCullingBounds() const;
|
||||
|
||||
void ResetTransform();
|
||||
|
||||
void Transform(const Matrix& xformation);
|
||||
@ -135,8 +150,9 @@ class Canvas {
|
||||
EntityPass* current_pass_ = nullptr;
|
||||
std::deque<CanvasStackEntry> xformation_stack_;
|
||||
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
|
||||
std::optional<Rect> initial_cull_rect_;
|
||||
|
||||
void Initialize();
|
||||
void Initialize(std::optional<Rect> cull_rect);
|
||||
|
||||
void Reset();
|
||||
|
||||
@ -147,6 +163,9 @@ class Canvas {
|
||||
void ClipGeometry(std::unique_ptr<Geometry> geometry,
|
||||
Entity::ClipOperation clip_op);
|
||||
|
||||
void IntersectCulling(Rect clip_bounds);
|
||||
void SubtractCulling(Rect clip_bounds);
|
||||
|
||||
void Save(bool create_subpass,
|
||||
BlendMode = BlendMode::kSourceOver,
|
||||
std::optional<EntityPass::BackdropFilterProc> backdrop_filter =
|
||||
|
||||
337
engine/src/flutter/impeller/aiks/canvas_unittests.cc
Normal file
337
engine/src/flutter/impeller/aiks/canvas_unittests.cc
Normal file
@ -0,0 +1,337 @@
|
||||
// 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 "flutter/testing/testing.h"
|
||||
#include "impeller/aiks/canvas.h"
|
||||
#include "impeller/geometry/path_builder.h"
|
||||
|
||||
namespace impeller {
|
||||
namespace testing {
|
||||
|
||||
using AiksCanvasTest = ::testing::Test;
|
||||
|
||||
TEST(AiksCanvasTest, EmptyCullRect) {
|
||||
Canvas canvas;
|
||||
|
||||
ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, InitialCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), initial_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, TranslatedCullRect) {
|
||||
Rect initial_cull(5, 5, 10, 10);
|
||||
Rect translated_cull(0, 0, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.Translate(Vector3(5, 5, 0));
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), translated_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, ScaledCullRect) {
|
||||
Rect initial_cull(5, 5, 10, 10);
|
||||
Rect scaled_cull(10, 10, 20, 20);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.Scale(Vector2(0.5, 0.5));
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), scaled_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipIntersectAgainstEmptyCullRect) {
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas;
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kIntersect);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), rect_clip);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffAgainstEmptyCullRect) {
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas;
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipIntersectAgainstCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
Rect result_cull(5, 5, 5, 5);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kIntersect);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffAgainstNonCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
Rect result_cull(0, 0, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffAboveCullRect) {
|
||||
Rect initial_cull(5, 5, 10, 10);
|
||||
Rect rect_clip(0, 0, 20, 4);
|
||||
Rect result_cull(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffBelowCullRect) {
|
||||
Rect initial_cull(5, 5, 10, 10);
|
||||
Rect rect_clip(0, 16, 20, 4);
|
||||
Rect result_cull(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffLeftOfCullRect) {
|
||||
Rect initial_cull(5, 5, 10, 10);
|
||||
Rect rect_clip(0, 0, 4, 20);
|
||||
Rect result_cull(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffRightOfCullRect) {
|
||||
Rect initial_cull(5, 5, 10, 10);
|
||||
Rect rect_clip(16, 0, 4, 20);
|
||||
Rect result_cull(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffAgainstVCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(5, 0, 10, 10);
|
||||
Rect result_cull(0, 0, 5, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RectClipDiffAgainstHCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(0, 5, 10, 10);
|
||||
Rect result_cull(0, 0, 10, 5);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRect(rect_clip, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipIntersectAgainstEmptyCullRect) {
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas;
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kIntersect);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), rect_clip);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipDiffAgainstEmptyCullRect) {
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas;
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipIntersectAgainstCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
Rect result_cull(5, 5, 5, 5);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kIntersect);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipDiffAgainstNonCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
Rect result_cull(0, 0, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipDiffAgainstVPartiallyCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(5, 0, 10, 10);
|
||||
Rect result_cull(0, 0, 6, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipDiffAgainstVFullyCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(5, -2, 10, 14);
|
||||
Rect result_cull(0, 0, 5, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipDiffAgainstHPartiallyCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(0, 5, 10, 10);
|
||||
Rect result_cull(0, 0, 10, 6);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, RRectClipDiffAgainstHFullyCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
Rect rect_clip(-2, 5, 14, 10);
|
||||
Rect result_cull(0, 0, 10, 5);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipRRect(rect_clip, 1, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, PathClipIntersectAgainstEmptyCullRect) {
|
||||
PathBuilder builder;
|
||||
builder.AddRect({5, 5, 1, 1});
|
||||
builder.AddRect({5, 14, 1, 1});
|
||||
builder.AddRect({14, 5, 1, 1});
|
||||
builder.AddRect({14, 14, 1, 1});
|
||||
Path path = builder.TakePath();
|
||||
Rect rect_clip(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas;
|
||||
canvas.ClipPath(path, Entity::ClipOperation::kIntersect);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), rect_clip);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, PathClipDiffAgainstEmptyCullRect) {
|
||||
PathBuilder builder;
|
||||
builder.AddRect({5, 5, 1, 1});
|
||||
builder.AddRect({5, 14, 1, 1});
|
||||
builder.AddRect({14, 5, 1, 1});
|
||||
builder.AddRect({14, 14, 1, 1});
|
||||
Path path = builder.TakePath();
|
||||
|
||||
Canvas canvas;
|
||||
canvas.ClipPath(path, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_FALSE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, PathClipIntersectAgainstCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
PathBuilder builder;
|
||||
builder.AddRect({5, 5, 1, 1});
|
||||
builder.AddRect({5, 14, 1, 1});
|
||||
builder.AddRect({14, 5, 1, 1});
|
||||
builder.AddRect({14, 14, 1, 1});
|
||||
Path path = builder.TakePath();
|
||||
Rect result_cull(5, 5, 5, 5);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipPath(path, Entity::ClipOperation::kIntersect);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, PathClipDiffAgainstNonCoveredCullRect) {
|
||||
Rect initial_cull(0, 0, 10, 10);
|
||||
PathBuilder builder;
|
||||
builder.AddRect({5, 5, 1, 1});
|
||||
builder.AddRect({5, 14, 1, 1});
|
||||
builder.AddRect({14, 5, 1, 1});
|
||||
builder.AddRect({14, 14, 1, 1});
|
||||
Path path = builder.TakePath();
|
||||
Rect result_cull(0, 0, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipPath(path, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
TEST(AiksCanvasTest, PathClipDiffAgainstFullyCoveredCullRect) {
|
||||
Rect initial_cull(5, 5, 10, 10);
|
||||
PathBuilder builder;
|
||||
builder.AddRect({0, 0, 100, 100});
|
||||
Path path = builder.TakePath();
|
||||
// Diff clip of Paths is ignored due to complexity
|
||||
Rect result_cull(5, 5, 10, 10);
|
||||
|
||||
Canvas canvas(initial_cull);
|
||||
canvas.ClipPath(path, Entity::ClipOperation::kDifference);
|
||||
|
||||
ASSERT_TRUE(canvas.GetCurrentLocalCullingBounds().has_value());
|
||||
ASSERT_EQ(canvas.GetCurrentLocalCullingBounds().value(), result_cull);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
@ -43,6 +43,10 @@ namespace impeller {
|
||||
|
||||
DlDispatcher::DlDispatcher() = default;
|
||||
|
||||
DlDispatcher::DlDispatcher(Rect cull_rect) : canvas_(cull_rect) {}
|
||||
|
||||
DlDispatcher::DlDispatcher(IRect cull_rect) : canvas_(cull_rect) {}
|
||||
|
||||
DlDispatcher::~DlDispatcher() = default;
|
||||
|
||||
static BlendMode ToBlendMode(flutter::DlBlendMode mode) {
|
||||
@ -1053,7 +1057,24 @@ void DlDispatcher::drawDisplayList(
|
||||
canvas_.SaveLayer(save_paint);
|
||||
}
|
||||
|
||||
display_list->Dispatch(*this);
|
||||
if (display_list->has_rtree()) {
|
||||
// The canvas remembers the screen-space culling bounds clipped by
|
||||
// the surface and the history of clip calls. DisplayList can cull
|
||||
// the ops based on a rectangle expressed in its "destination bounds"
|
||||
// so we need the canvas to transform those into the current local
|
||||
// coordinate space into which the DisplayList will be rendered.
|
||||
auto cull_bounds = canvas_.GetCurrentLocalCullingBounds();
|
||||
if (cull_bounds.has_value()) {
|
||||
Rect cull_rect = cull_bounds.value();
|
||||
display_list->Dispatch(
|
||||
*this, SkRect::MakeLTRB(cull_rect.GetLeft(), cull_rect.GetTop(),
|
||||
cull_rect.GetRight(), cull_rect.GetBottom()));
|
||||
} else {
|
||||
display_list->Dispatch(*this);
|
||||
}
|
||||
} else {
|
||||
display_list->Dispatch(*this);
|
||||
}
|
||||
|
||||
// Restore all saved state back to what it was before we interpreted
|
||||
// the display_list
|
||||
|
||||
@ -15,6 +15,10 @@ class DlDispatcher final : public flutter::DlOpReceiver {
|
||||
public:
|
||||
DlDispatcher();
|
||||
|
||||
explicit DlDispatcher(Rect cull_rect);
|
||||
|
||||
explicit DlDispatcher(IRect cull_rect);
|
||||
|
||||
~DlDispatcher();
|
||||
|
||||
Picture EndRecordingAsPicture();
|
||||
|
||||
@ -234,11 +234,4 @@ class EntityPass {
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EntityPass);
|
||||
};
|
||||
|
||||
struct CanvasStackEntry {
|
||||
Matrix xformation;
|
||||
size_t stencil_depth = 0u;
|
||||
bool is_subpass = false;
|
||||
bool contains_clips = false;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -224,21 +224,21 @@ struct TRect {
|
||||
// Full cutout.
|
||||
return std::nullopt;
|
||||
}
|
||||
if (b_top <= a_top) {
|
||||
if (b_top <= a_top && b_bottom > a_top) {
|
||||
// Cuts off the top.
|
||||
return TRect::MakeLTRB(a_left, b_bottom, a_right, a_bottom);
|
||||
}
|
||||
if (b_bottom >= b_bottom) {
|
||||
if (b_bottom >= a_bottom && b_top < a_bottom) {
|
||||
// Cuts out the bottom.
|
||||
return TRect::MakeLTRB(a_left, a_top, a_right, b_top);
|
||||
}
|
||||
}
|
||||
if (b_top <= a_top && b_bottom >= a_bottom) {
|
||||
if (b_left <= a_left) {
|
||||
if (b_left <= a_left && b_right > a_left) {
|
||||
// Cuts out the left.
|
||||
return TRect::MakeLTRB(b_right, a_top, a_right, a_bottom);
|
||||
}
|
||||
if (b_right >= a_right) {
|
||||
if (b_right >= a_right && b_left < a_right) {
|
||||
// Cuts out the right.
|
||||
return TRect::MakeLTRB(a_left, a_top, b_left, a_bottom);
|
||||
}
|
||||
|
||||
@ -48,6 +48,9 @@ class SurfaceMTL final : public Surface {
|
||||
|
||||
id<MTLDrawable> drawable() const { return drawable_; }
|
||||
|
||||
// Returns a Rect defining the area of the surface in device pixels
|
||||
IRect coverage() const;
|
||||
|
||||
// |Surface|
|
||||
bool Present() const override;
|
||||
|
||||
|
||||
@ -148,6 +148,11 @@ bool SurfaceMTL::ShouldPerformPartialRepaint(std::optional<IRect> damage_rect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// |Surface|
|
||||
IRect SurfaceMTL::coverage() const {
|
||||
return IRect::MakeSize(resolve_texture_->GetSize());
|
||||
}
|
||||
|
||||
// |Surface|
|
||||
bool SurfaceMTL::Present() const {
|
||||
if (drawable_ == nil) {
|
||||
|
||||
@ -118,8 +118,10 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrame(const SkISiz
|
||||
return surface->Present();
|
||||
}
|
||||
|
||||
impeller::DlDispatcher impeller_dispatcher;
|
||||
display_list->Dispatch(impeller_dispatcher);
|
||||
impeller::IRect cull_rect = surface->coverage();
|
||||
SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.size.width, cull_rect.size.height);
|
||||
impeller::DlDispatcher impeller_dispatcher(cull_rect);
|
||||
display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
|
||||
auto picture = impeller_dispatcher.EndRecordingAsPicture();
|
||||
|
||||
return renderer->Render(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user