Better clipRect culling accuracy under scaling transforms (flutter/engine#53508)
The clipRect culling in DisplayListBuilder can sometimes fail to cull a clip due to round-off error in the math and this case happens to trigger when trying to cull against the initial frame bounds of a Pixel 6a with its uncommon 2.625 DPR as a scaling transform. An optimization in the culling tests for axis-aligned transforms (very common in Flutter) happens to do the right thing for this odd case, likely due to less math to incur round-off errors.
This commit is contained in:
parent
c77583f85e
commit
3bd63a65bb
@ -4420,7 +4420,9 @@ class ClipExpector : public virtual DlOpReceiver,
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void check(T shape, ClipOp clip_op, bool is_aa) {
|
void check(T shape, ClipOp clip_op, bool is_aa) {
|
||||||
ASSERT_LT(index_, clip_expectations_.size()) << label();
|
ASSERT_LT(index_, clip_expectations_.size())
|
||||||
|
<< label() << std::endl
|
||||||
|
<< "extra clip shape = " << shape;
|
||||||
auto expected = clip_expectations_[index_];
|
auto expected = clip_expectations_[index_];
|
||||||
EXPECT_EQ(expected.clip_op, clip_op) << label();
|
EXPECT_EQ(expected.clip_op, clip_op) << label();
|
||||||
EXPECT_EQ(expected.is_aa, is_aa) << label();
|
EXPECT_EQ(expected.is_aa, is_aa) << label();
|
||||||
@ -4443,6 +4445,30 @@ class ClipExpector : public virtual DlOpReceiver,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_F(DisplayListTest, ClipRectCullingPixel6a) {
|
||||||
|
// These particular values create bit errors if we use the path that
|
||||||
|
// tests for inclusion in local space, but work OK if we use a forward
|
||||||
|
// path that tests for inclusion in device space, due to the fact that
|
||||||
|
// the extra matrix inversion is just enough math to cause the transform
|
||||||
|
// to place the local space cull corners just outside the original rect.
|
||||||
|
// The test in device space only works under a simple scale, such as we
|
||||||
|
// use for DPR adjustments (and which are not always inversion friendly).
|
||||||
|
|
||||||
|
auto frame = SkRect::MakeLTRB(0.0f, 0.0f, 1080.0f, 2400.0f);
|
||||||
|
DlScalar DPR = 2.625f;
|
||||||
|
auto clip = SkRect::MakeLTRB(0.0f, 0.0f, 1080.0f / DPR, 2400.0f / DPR);
|
||||||
|
|
||||||
|
DisplayListBuilder cull_builder;
|
||||||
|
cull_builder.ClipRect(frame, ClipOp::kIntersect, false);
|
||||||
|
cull_builder.Scale(DPR, DPR);
|
||||||
|
cull_builder.ClipRect(clip, ClipOp::kIntersect, false);
|
||||||
|
auto cull_dl = cull_builder.Build();
|
||||||
|
|
||||||
|
CLIP_EXPECTOR(expector);
|
||||||
|
expector.addExpectation(frame, ClipOp::kIntersect, false);
|
||||||
|
cull_dl->Dispatch(expector);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DisplayListTest, ClipRectCulling) {
|
TEST_F(DisplayListTest, ClipRectCulling) {
|
||||||
auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
|
auto clip = SkRect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f);
|
||||||
|
|
||||||
|
@ -239,6 +239,12 @@ bool DisplayListMatrixClipState::rect_covers_cull(const DlRect& content) const {
|
|||||||
if (cull_rect_.IsEmpty()) {
|
if (cull_rect_.IsEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (matrix_.IsAligned2D()) {
|
||||||
|
// This transform-to-device calculation is faster and more accurate
|
||||||
|
// for rect-to-rect aligned transformations, but not accurate under
|
||||||
|
// (non-quadrant) rotations and skews.
|
||||||
|
return content.TransformAndClipBounds(matrix_).Contains(cull_rect_);
|
||||||
|
}
|
||||||
DlPoint corners[4];
|
DlPoint corners[4];
|
||||||
if (!getLocalCullCorners(corners)) {
|
if (!getLocalCullCorners(corners)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -779,6 +779,33 @@ TEST(DisplayListMatrixClipState, RectCoverage) {
|
|||||||
EXPECT_FALSE(state.rect_covers_cull(rect.Expand(0.0f, 0.0f, 0.0f, -0.1f)));
|
EXPECT_FALSE(state.rect_covers_cull(rect.Expand(0.0f, 0.0f, 0.0f, -0.1f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(DisplayListMatrixClipState, RectCoverageAccuracy) {
|
||||||
|
// These particular values create bit errors if we use the path that
|
||||||
|
// tests for inclusion in local space, but work OK if we use a forward
|
||||||
|
// path that tests for inclusion in device space, due to the fact that
|
||||||
|
// the extra matrix inversion is just enough math to cause the transform
|
||||||
|
// to place the local space cull corners just outside the original rect.
|
||||||
|
// The test in device space only works under a simple scale, such as we
|
||||||
|
// use for DPR adjustments (and which are not always inversion friendly).
|
||||||
|
|
||||||
|
DlRect cull = DlRect::MakeLTRB(0.0f, 0.0f, 1080.0f, 2400.0f);
|
||||||
|
DlScalar DPR = 2.625;
|
||||||
|
DlRect rect = DlRect::MakeLTRB(0.0f, 0.0f, 1080.0f / DPR, 2400.0f / DPR);
|
||||||
|
|
||||||
|
DisplayListMatrixClipState state(cull);
|
||||||
|
state.scale(DPR, DPR);
|
||||||
|
|
||||||
|
EXPECT_TRUE(state.rect_covers_cull(rect));
|
||||||
|
EXPECT_TRUE(state.rect_covers_cull(rect.Expand(0.1f, 0.0f, 0.0f, 0.0f)));
|
||||||
|
EXPECT_TRUE(state.rect_covers_cull(rect.Expand(0.0f, 0.1f, 0.0f, 0.0f)));
|
||||||
|
EXPECT_TRUE(state.rect_covers_cull(rect.Expand(0.0f, 0.0f, 0.1f, 0.0f)));
|
||||||
|
EXPECT_TRUE(state.rect_covers_cull(rect.Expand(0.0f, 0.0f, 0.0f, 0.1f)));
|
||||||
|
EXPECT_FALSE(state.rect_covers_cull(rect.Expand(-0.1f, 0.0f, 0.0f, 0.0f)));
|
||||||
|
EXPECT_FALSE(state.rect_covers_cull(rect.Expand(0.0f, -0.1f, 0.0f, 0.0f)));
|
||||||
|
EXPECT_FALSE(state.rect_covers_cull(rect.Expand(0.0f, 0.0f, -0.1f, 0.0f)));
|
||||||
|
EXPECT_FALSE(state.rect_covers_cull(rect.Expand(0.0f, 0.0f, 0.0f, -0.1f)));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(DisplayListMatrixClipState, RectCoverageUnderScale) {
|
TEST(DisplayListMatrixClipState, RectCoverageUnderScale) {
|
||||||
DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
|
DlRect rect = DlRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
|
||||||
DisplayListMatrixClipState state(rect);
|
DisplayListMatrixClipState state(rect);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user