Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] round up subpass coverage when it is close to (and smaller) than root pass size. #49925

Merged
merged 9 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3867,6 +3867,54 @@ TEST_P(AiksTest, GaussianBlurMipMapImageFilter) {
#endif
}

TEST_P(AiksTest, SaveLayersCloseToRootPassSizeAreScaledUp) {
Canvas canvas;
// Create a subpass with no bounds hint and an entity coverage of (95, 95).
canvas.SaveLayer({
.color = Color::Black().WithAlpha(0.5),
});
canvas.DrawRect(Rect::MakeLTRB(0, 0, 10, 10), {.color = Color::Red()});
canvas.DrawRect(Rect::MakeLTRB(0, 0, 95, 95), {.color = Color::Blue()});
canvas.Restore();

Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<RenderTargetCache> cache =
std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
AiksContext aiks_context(GetContext(), nullptr, cache);
picture.ToImage(aiks_context, {100, 100});

for (auto it = cache->GetTextureDataBegin(); it != cache->GetTextureDataEnd();
++it) {
EXPECT_EQ(it->texture->GetTextureDescriptor().size, ISize(100, 100));
}
}

TEST_P(AiksTest, SaveLayersCloseToRootPassSizeAreNotScaledUpPastBoundsHint) {
Canvas canvas;
canvas.SaveLayer(
{
.color = Color::Black().WithAlpha(0.5),
},
Rect::MakeSize(ISize(95, 95)));
canvas.DrawRect(Rect::MakeLTRB(0, 0, 100, 100), {.color = Color::Red()});
canvas.DrawRect(Rect::MakeLTRB(50, 50, 150, 150), {.color = Color::Blue()});
canvas.Restore();

Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<RenderTargetCache> cache =
std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
AiksContext aiks_context(GetContext(), nullptr, cache);
picture.ToImage(aiks_context, {100, 100});

// We expect a single 100x100 texture and the rest should be 95x95.
EXPECT_EQ(cache->GetTextureDataBegin()->texture->GetTextureDescriptor().size,
ISize(100, 100));
for (auto it = ++cache->GetTextureDataBegin();
it != cache->GetTextureDataEnd(); ++it) {
EXPECT_EQ(it->texture->GetTextureDescriptor().size, ISize(95, 95));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, one if these is 100,100, need to make the expection more specific.

}
}

TEST_P(AiksTest, ImageColorSourceEffectTransform) {
// Compare with https://fiddle.skia.org/c/6cdc5aefb291fda3833b806ca347a885

Expand Down
55 changes: 51 additions & 4 deletions impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
#include "impeller/entity/entity.h"
#include "impeller/entity/inline_pass_context.h"
#include "impeller/geometry/color.h"
#include "impeller/geometry/matrix.h"
#include "impeller/geometry/rect.h"
#include "impeller/geometry/size.h"
#include "impeller/renderer/command_buffer.h"

#ifdef IMPELLER_DEBUG
Expand Down Expand Up @@ -67,6 +69,13 @@ std::optional<Rect> EntityPass::GetBoundsLimit() const {
return bounds_limit_;
}

std::optional<Rect> EntityPass::GetTransformedBoundsLimit() const {
if (bounds_limit_.has_value()) {
return bounds_limit_->TransformBounds(transform_);
}
return std::nullopt;
}

void EntityPass::AddEntity(Entity entity) {
if (entity.GetBlendMode() == BlendMode::kSourceOver &&
entity.GetContents()->IsOpaque()) {
Expand Down Expand Up @@ -190,12 +199,11 @@ std::optional<Rect> EntityPass::GetSubpassCoverage(
return std::nullopt;
}

if (!subpass.bounds_limit_.has_value()) {
auto user_bounds_coverage = subpass.GetTransformedBoundsLimit();
if (!user_bounds_coverage.has_value()) {
return entities_coverage;
}
auto user_bounds_coverage =
subpass.bounds_limit_->TransformBounds(subpass.transform_);
return entities_coverage->Intersection(user_bounds_coverage);
return entities_coverage->Intersection(user_bounds_coverage.value());
}

EntityPass* EntityPass::GetSuperpass() const {
Expand Down Expand Up @@ -485,6 +493,43 @@ bool EntityPass::Render(ContentContext& renderer,
clip_coverage_stack); // clip_coverage_stack
}

// When a subpass size is close to, but still smaller than the root pass
// size and smaller than the bounds hint, we may scale it up to the root
// pass size. This will improve performance by improving the efficiency of
// the render target cache, as only textures with exactly the same sizes +
// descriptors can be recycled.
static ISize MaybeRoundUpTextureSize(ISize subpass_size,
ISize root_pass_size,
std::optional<Rect> bounds_limit) {
// If the subpass is already bigger than the root pass size,
// return the existing subpass size.
if (subpass_size.width > root_pass_size.width ||
subpass_size.height > root_pass_size.height) {
return subpass_size;
}

// If there is a bounds limit and it is tigher than the root pass size,
// return the existing subpass size. This case could be removed if we
// conditionally inserted clips/scissor instead.
if (bounds_limit.has_value()) {
auto bounds_size = bounds_limit->GetSize();
if (bounds_size.width < root_pass_size.width ||
bounds_size.height < root_pass_size.height) {
return subpass_size;
}
}

// If the subpass size is within 10% of the root pass size, round up
// to the root pass size.
if (root_pass_size.width - subpass_size.width <=
(0.1 * root_pass_size.width) &&
root_pass_size.height - subpass_size.height <=
(0.1 * root_pass_size.height)) {
return root_pass_size;
}
return subpass_size;
}

EntityPass::EntityResult EntityPass::GetEntityForElement(
const EntityPass::Element& element,
ContentContext& renderer,
Expand Down Expand Up @@ -618,6 +663,8 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
return EntityPass::EntityResult::Skip();
}

subpass_size = MaybeRoundUpTextureSize(
subpass_size, root_pass_size, subpass->GetTransformedBoundsLimit());
auto subpass_target = CreateRenderTarget(
renderer, // renderer
subpass_size, // size
Expand Down
3 changes: 3 additions & 0 deletions impeller/entity/entity_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ class EntityPass {
required_mip_count_ = mip_count;
}

/// @brief Return the local bounds transformed intro screen coordinate space.
std::optional<Rect> GetTransformedBoundsLimit() const;

//----------------------------------------------------------------------------
/// @brief Computes the coverage of a given subpass. This is used to
/// determine the texture size of a given subpass before it's rendered
Expand Down