[Impeller] simplify render target size rounding up heuristics. (flutter/engine#57043)

Remove the over-engineered "slide around round up" herusistic for the same heuristic used by the image filters: if the coverage size is within 30% of the cull rect size, just use the cull rect size.

This stabilizes allocations for the new android slide in page transition, but in a less complicated way than the old herustic.
This commit is contained in:
Jonah Williams 2024-12-09 17:16:23 -08:00 committed by GitHub
parent 2695e35d9d
commit b425a87fd3
2 changed files with 53 additions and 85 deletions

View File

@ -12,6 +12,8 @@ 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;
}
static constexpr Scalar kDefaultSizeThreshold = 0.3;
} // namespace
std::optional<Rect> ComputeSaveLayerCoverage(
@ -84,7 +86,8 @@ std::optional<Rect> ComputeSaveLayerCoverage(
transformed_coverage.Intersection(source_coverage_limit.value());
if (intersected_coverage.has_value() &&
SizeDifferenceUnderThreshold(transformed_coverage.GetSize(),
intersected_coverage->GetSize(), 0.2)) {
intersected_coverage->GetSize(),
kDefaultSizeThreshold)) {
// Returning the transformed coverage is always correct, it just may
// be larger than the clip area or onscreen texture.
return transformed_coverage;
@ -113,35 +116,16 @@ std::optional<Rect> ComputeSaveLayerCoverage(
if (!intersection.has_value()) {
return std::nullopt;
}
// Sometimes a saveLayer is only slightly shifted outside of the cull rect,
// but is being animated in. This is common for the Android slide in page
// transitions. In these cases, computing a cull that is too tight can cause
// thrashing of the texture cache. Instead, we try to determine the
// intersection using only the sizing by shifting the coverage rect into the
// cull rect origin.
Point delta = coverage_limit.GetOrigin() - transformed_coverage.GetOrigin();
// This herustic is limited to perfectly vertical or horizontal transitions
// that slide in, limited to a fixed threshold of ~30%. This value is based on
// the Android slide in page transition which experimental has threshold
// values of up to 28%.
static constexpr Scalar kThresholdLimit = 0.3;
if (ScalarNearlyEqual(delta.y, 0) || ScalarNearlyEqual(delta.x, 0)) {
Scalar threshold = std::max(std::abs(delta.x / coverage_limit.GetWidth()),
std::abs(delta.y / coverage_limit.GetHeight()));
if (threshold < kThresholdLimit) {
std::optional<Rect> shifted_intersected_value =
transformed_coverage.Shift(delta).Intersection(coverage_limit);
if (shifted_intersected_value.has_value()) {
return shifted_intersected_value.value().Shift(-delta);
}
}
// The the resulting coverage rect is nearly the same as the coverage_limit,
// round up to the coverage_limit.
Rect intersect_rect = intersection.value();
if (SizeDifferenceUnderThreshold(intersect_rect.GetSize(),
coverage_limit.GetSize(),
kDefaultSizeThreshold)) {
return coverage_limit;
}
return intersection;
return intersect_rect;
}
} // namespace impeller

View File

@ -280,77 +280,61 @@ TEST(SaveLayerUtilsTest,
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 50, 50));
}
TEST(SaveLayerUtilsTest,
PartiallyIntersectingCoverageIgnoresOriginWithSlideSemanticsX) {
TEST(SaveLayerUtilsTest, RoundUpCoverageWhenCloseToCoverageLimit) {
// X varies, translation is performed on coverage.
auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(5, 0, 210, 210), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
/*content_coverage=*/Rect::MakeLTRB(0, 0, 90, 90), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
);
ASSERT_TRUE(coverage.has_value());
// Size that matches coverage limit
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(5, 0, 105, 100));
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 100, 100));
}
TEST(SaveLayerUtilsTest, DontRoundUpCoverageWhenNotCloseToCoverageLimitWidth) {
// X varies, translation is performed on coverage.
auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(0, 0, 50, 90), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
);
ASSERT_TRUE(coverage.has_value());
// Size that matches coverage limit
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 50, 90));
}
TEST(SaveLayerUtilsTest, DontRoundUpCoverageWhenNotCloseToCoverageLimitHeight) {
// X varies, translation is performed on coverage.
auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(0, 0, 90, 50), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
);
ASSERT_TRUE(coverage.has_value());
// Size that matches coverage limit
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 90, 50));
}
TEST(SaveLayerUtilsTest,
PartiallyIntersectingCoverageIgnoresOriginWithSlideSemanticsY) {
// Y varies, translation is performed on coverage.
DontRoundUpCoverageWhenNotCloseToCoverageLimitWidthHeight) {
// X varies, translation is performed on coverage.
auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(0, 5, 210, 210), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
/*content_coverage=*/Rect::MakeLTRB(0, 0, 50, 50), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
);
ASSERT_TRUE(coverage.has_value());
// Size that matches coverage limit
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 5, 100, 105));
}
TEST(SaveLayerUtilsTest, PartiallyIntersectingCoverageIgnoresOriginBothXAndY) {
// Both X and Y vary, no transation is performed.
auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(5, 5, 210, 210), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
);
ASSERT_TRUE(coverage.has_value());
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(5, 5, 100, 100));
}
TEST(SaveLayerUtilsTest, PartiallyIntersectingCoverageWithOveriszedThresholdY) {
// Y varies, translation is not performed on coverage because it is too far
// offscreen.
auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(0, 80, 200, 200), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
);
ASSERT_TRUE(coverage.has_value());
// Size that matches coverage limit
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 80, 100, 100));
}
TEST(SaveLayerUtilsTest, PartiallyIntersectingCoverageWithOveriszedThresholdX) {
// X varies, translation is not performed on coverage because it is too far
// offscreen.
auto coverage = ComputeSaveLayerCoverage(
/*content_coverage=*/Rect::MakeLTRB(80, 0, 200, 200), //
/*effect_transform=*/{}, //
/*coverage_limit=*/Rect::MakeLTRB(0, 0, 100, 100), //
/*image_filter=*/nullptr //
);
ASSERT_TRUE(coverage.has_value());
// Size that matches coverage limit
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(80, 0, 100, 100));
EXPECT_EQ(coverage.value(), Rect::MakeLTRB(0, 0, 50, 50));
}
} // namespace testing