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

[Impeller] add herustic for ignoring coverage limit w/ image filters. #55030

Merged
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
30 changes: 28 additions & 2 deletions impeller/entity/save_layer_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

namespace impeller {

namespace {
bool SizeDifferenceUnderThreshold(Size a, Size b, Scalar threshold) {
return (std::abs(a.width - b.width) / b.width) < threshold &&
(std::abs(a.height - b.height) / b.height) < threshold;
}
} // namespace

std::optional<Rect> ComputeSaveLayerCoverage(
const Rect& content_coverage,
const Matrix& effect_transform,
Expand Down Expand Up @@ -64,8 +71,27 @@ std::optional<Rect> ComputeSaveLayerCoverage(
return source_coverage_limit;
}

return coverage.TransformBounds(effect_transform)
.Intersection(source_coverage_limit.value());
// Trimming the content coverage by the coverage limit can reduce memory
// coverage. limit can reduce memory bandwith. But in cases where there are
Copy link
Contributor

Choose a reason for hiding this comment

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

double phrase.

// animated matrix filters, such as in the framework's zoom transition, the
// changing scale values continually change the source_coverage_limit.
// Intersecting the source_coverage_limit with the coverage may result in
// slightly different texture sizes each frame of the animation. This leads
// to non-optimal allocation patterns as differently sized textures cannot
// be reused. Hence the following herustic: If the coverage is within a
// semi-arbitrary percentage of the intersected coverage, then just use the
// transformed coverage. In other cases, use the intersection.
Copy link
Contributor

Choose a reason for hiding this comment

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

What about going further and using surfaces that are rounded up to a tile size or some other quantization size? Would we be able to reuse the surfaces?

Some thought would need to be taken with kClamp/repeat/mirror tile modes, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

OOh, that is a good idea.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can the clamp/repeat modes restrict themselves to a coordinate sample not on the edge?

Copy link
Member Author

Choose a reason for hiding this comment

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

We control the input UVs for all of our image filters, so should be able to easily (tm) restrict the input range.

auto transformed_coverage = coverage.TransformBounds(effect_transform);
auto intersected_coverage =
transformed_coverage.Intersection(source_coverage_limit.value());
if (intersected_coverage.has_value() &&
SizeDifferenceUnderThreshold(transformed_coverage.GetSize(),
intersected_coverage->GetSize(), 0.2)) {
// Returning the transformed coverage is always correct, it just may
// be larger than the clip area or onscreen texture.
return transformed_coverage;
}
return intersected_coverage;
}

// If the input coverage is maximum, just return the coverage limit that
Expand Down
56 changes: 56 additions & 0 deletions impeller/entity/save_layer_utils_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,62 @@ TEST(SaveLayerUtilsTest,
ASSERT_FALSE(coverage.has_value());
}

TEST(
SaveLayerUtilsTest,
CoverageLimitIgnoredIfIntersectedValueIsCloseToActualCoverageSmallerWithImageFilter) {
// Create an image filter that slightly shrinks the coverage limit
auto image_filter = FilterContents::MakeMatrixFilter(
FilterInput::Make(Rect()), Matrix::MakeScale({1.1, 1.1, 1}), {});

auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/image_filter //
);

ASSERT_TRUE(coverage.has_value());
// The transfomed coverage limit is ((0, 0), (90.9091, 90.9091)).
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 100, 100));
}

TEST(
SaveLayerUtilsTest,
CoverageLimitIgnoredIfIntersectedValueIsCloseToActualCoverageLargerWithImageFilter) {
// Create an image filter that slightly stretches the coverage limit. Even
// without the special logic for using the original content coverage, we
// verify that we don't introduce any artifacts from the intersection.
auto image_filter = FilterContents::MakeMatrixFilter(
FilterInput::Make(Rect()), Matrix::MakeScale({0.9, 0.9, 1}), {});

auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/image_filter //
);

ASSERT_TRUE(coverage.has_value());
// The transfomed coverage limit is ((0, 0), (111.111, 111.111)).
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 100, 100));
}

TEST(SaveLayerUtilsTest,
CoverageLimitRespectedIfSubstantiallyDifferentFromContentCoverge) {
auto image_filter = FilterContents::MakeMatrixFilter(
FilterInput::Make(Rect()), Matrix::MakeScale({2, 2, 1}), {});

auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(0, 0, 1000, 1000), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/image_filter //
);

ASSERT_TRUE(coverage.has_value());
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 50, 50));
}

} // namespace testing
} // namespace impeller

Expand Down