Skip to content

Commit

Permalink
Implement Canvas::SaveLayer with bounds.
Browse files Browse the repository at this point in the history
  • Loading branch information
chinmaygarde authored and dnfield committed Apr 27, 2022
1 parent 6d87108 commit 18399a5
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 4 deletions.
54 changes: 54 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,59 @@ TEST_F(AiksTest, CanPerformSkew) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_F(AiksTest, CanPerformSaveLayerWithBounds) {
Canvas canvas;

Paint red;
red.color = Color::Red();

Paint green;
green.color = Color::Green();

Paint blue;
blue.color = Color::Blue();

Paint save;
save.color = Color::Black();

canvas.SaveLayer(save, Rect{0, 0, 50, 50});

canvas.DrawRect({0, 0, 100, 100}, red);
canvas.DrawRect({10, 10, 100, 100}, green);
canvas.DrawRect({20, 20, 100, 100}, blue);

canvas.Restore();

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_F(
AiksTest,
DISABLED_CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) {
Canvas canvas;

Paint red;
red.color = Color::Red();

Paint green;
green.color = Color::Green();

Paint blue;
blue.color = Color::Blue();

Paint save;
save.color = Color::Black().WithAlpha(0.5);

canvas.SaveLayer(save, Rect{0, 0, 100000, 100000});

canvas.DrawRect({0, 0, 100, 100}, red);
canvas.DrawRect({10, 10, 100, 100}, green);
canvas.DrawRect({20, 20, 100, 100}, blue);

canvas.Restore();

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

} // namespace testing
} // namespace impeller
12 changes: 11 additions & 1 deletion impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,18 @@ void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) {

void Canvas::SaveLayer(Paint paint, std::optional<Rect> bounds) {
GetCurrentPass().SetDelegate(
std::make_unique<PaintPassDelegate>(std::move(paint)));
std::make_unique<PaintPassDelegate>(std::move(paint), bounds));

Save(true);

if (bounds.has_value()) {
// Render target switches due to a save layer can be elided. In such cases
// where passes are collapsed into their parent, the clipping effect to
// the size of the render target that would have been allocated will be
// absent. Explicitly add back a clip to reproduce that behavior. Since
// clips never require a render target switch, this is a cheap operation.
ClipPath(PathBuilder{}.AddRect(bounds.value()).CreatePath());
}
}

void Canvas::ClipPath(Path path) {
Expand Down
8 changes: 7 additions & 1 deletion impeller/aiks/paint_pass_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@

namespace impeller {

PaintPassDelegate::PaintPassDelegate(Paint paint) : paint_(std::move(paint)) {}
PaintPassDelegate::PaintPassDelegate(Paint paint, std::optional<Rect> coverage)
: paint_(std::move(paint)), coverage_(std::move(coverage)) {}

// |EntityPassDelgate|
PaintPassDelegate::~PaintPassDelegate() = default;

// |EntityPassDelgate|
std::optional<Rect> PaintPassDelegate::GetCoverageRect() {
return coverage_;
}

// |EntityPassDelgate|
bool PaintPassDelegate::CanElide() {
return paint_.color.IsTransparent();
Expand Down
8 changes: 7 additions & 1 deletion impeller/aiks/paint_pass_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#pragma once

#include <optional>

#include "flutter/fml/macros.h"
#include "impeller/aiks/paint.h"
#include "impeller/entity/entity_pass_delegate.h"
Expand All @@ -12,11 +14,14 @@ namespace impeller {

class PaintPassDelegate final : public EntityPassDelegate {
public:
PaintPassDelegate(Paint paint);
PaintPassDelegate(Paint paint, std::optional<Rect> coverage);

// |EntityPassDelgate|
~PaintPassDelegate() override;

// |EntityPassDelegate|
std::optional<Rect> GetCoverageRect() override;

// |EntityPassDelgate|
bool CanElide() override;

Expand All @@ -29,6 +34,7 @@ class PaintPassDelegate final : public EntityPassDelegate {

private:
const Paint paint_;
const std::optional<Rect> coverage_;

FML_DISALLOW_COPY_AND_ASSIGN(PaintPassDelegate);
};
Expand Down
14 changes: 13 additions & 1 deletion impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ const EntityPass::Subpasses& EntityPass::GetSubpasses() const {
return subpasses_;
}

Rect EntityPass::GetSubpassCoverage(const EntityPass& subpass) const {
auto subpass_coverage = subpass.GetCoverageRect();
auto delegate_coverage =
delegate_->GetCoverageRect().value_or(subpass_coverage);
Rect coverage;
coverage.origin = subpass_coverage.origin;
// TODO(csg): This must still be restricted to the max texture size. Or,
// decide if this must be done by the allocator.
coverage.size = subpass_coverage.size.Min(delegate_coverage.size);
return coverage;
}

EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
if (!pass) {
return nullptr;
Expand Down Expand Up @@ -103,7 +115,7 @@ bool EntityPass::Render(ContentRenderer& renderer,
continue;
}

const auto subpass_coverage = subpass->GetCoverageRect();
const auto subpass_coverage = GetSubpassCoverage(*subpass);

if (subpass_coverage.IsEmpty()) {
// It is not an error to have an empty subpass. But subpasses that can't
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/entity_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class EntityPass {
std::unique_ptr<EntityPassDelegate> delegate_ =
EntityPassDelegate::MakeDefault();

Rect GetSubpassCoverage(const EntityPass& subpass) const;

FML_DISALLOW_COPY_AND_ASSIGN(EntityPass);
};

Expand Down
7 changes: 7 additions & 0 deletions impeller/entity/entity_pass_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ class DefaultEntityPassDelegate final : public EntityPassDelegate {
public:
DefaultEntityPassDelegate() = default;

// |EntityPassDelegate|
~DefaultEntityPassDelegate() override = default;

// |EntityPassDelegate|
std::optional<Rect> GetCoverageRect() override { return std::nullopt; }

// |EntityPassDelegate|
bool CanElide() override { return false; }

// |EntityPassDelegate|
bool CanCollapseIntoParentPass() override { return true; }

// |EntityPassDelegate|
std::shared_ptr<Contents> CreateContentsForSubpassTarget(
std::shared_ptr<Texture> target) override {
// Not possible since this pass always collapses into its parent.
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/entity_pass_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class EntityPassDelegate {

EntityPassDelegate();

virtual std::optional<Rect> GetCoverageRect() = 0;

virtual ~EntityPassDelegate();

virtual bool CanElide() = 0;
Expand Down
4 changes: 4 additions & 0 deletions impeller/geometry/size.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ struct TSize {
return {width - s.width, height - s.height};
}

constexpr TSize Min(const TSize& s) const { return Intersection(s); }

constexpr TSize Max(const TSize& s) const { return Union(s); }

constexpr TSize Union(const TSize& o) const {
return {
std::max(width, o.width),
Expand Down

0 comments on commit 18399a5

Please sign in to comment.