Use preservesAxisAlignment instead of scaleTranslate for clip simplification

This lets more matrix types pre-convert rects and rrects to device space.
Since the clip geometry isn't itself shaded, we can apply the matrix
without worrying about preserving local vs. device coordinates.

Bug: skia:10730
Change-Id: I61ae3e13eec66f0e5eb83a6504dcb8004620b151
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/320222
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2020-09-28 12:03:01 -04:00 committed by Skia Commit-Bot
parent 0dec9927af
commit d30e9efdab
2 changed files with 41 additions and 3 deletions

View File

@ -125,7 +125,7 @@ static bool shape_contains_rect(
if (!mixedAAMode && aToDevice == bToDevice) { if (!mixedAAMode && aToDevice == bToDevice) {
// A and B are in the same coordinate space, so don't bother mapping // A and B are in the same coordinate space, so don't bother mapping
return a.conservativeContains(b); return a.conservativeContains(b);
} else if (bToDevice.isIdentity() && aToDevice.isScaleTranslate()) { } else if (bToDevice.isIdentity() && aToDevice.preservesAxisAlignment()) {
// Optimize the common case of draws (B, with identity matrix) and axis-aligned shapes, // Optimize the common case of draws (B, with identity matrix) and axis-aligned shapes,
// instead of checking the four corners separately. // instead of checking the four corners separately.
SkRect bInA = b; SkRect bInA = b;
@ -543,7 +543,7 @@ void GrClipStack::RawElement::simplify(const SkIRect& deviceBounds, bool forceAA
// Except for axis-aligned clip rects, upgrade to AA when forced. We skip axis-aligned clip // Except for axis-aligned clip rects, upgrade to AA when forced. We skip axis-aligned clip
// rects because a non-AA axis aligned rect can always be set as just a scissor test or window // rects because a non-AA axis aligned rect can always be set as just a scissor test or window
// rect, avoiding an expensive stencil mask generation. // rect, avoiding an expensive stencil mask generation.
if (forceAA && !(fShape.isRect() && fLocalToDevice.isScaleTranslate())) { if (forceAA && !(fShape.isRect() && fLocalToDevice.preservesAxisAlignment())) {
fAA = GrAA::kYes; fAA = GrAA::kYes;
} }
@ -551,7 +551,7 @@ void GrClipStack::RawElement::simplify(const SkIRect& deviceBounds, bool forceAA
// mapped bounds of the shape. // mapped bounds of the shape.
fOuterBounds = GrClip::GetPixelIBounds(outer, fAA, BoundsType::kExterior); fOuterBounds = GrClip::GetPixelIBounds(outer, fAA, BoundsType::kExterior);
if (fLocalToDevice.isScaleTranslate()) { if (fLocalToDevice.preservesAxisAlignment()) {
if (fShape.isRect()) { if (fShape.isRect()) {
// The actual geometry can be updated to the device-intersected bounds and we can // The actual geometry can be updated to the device-intersected bounds and we can
// know the inner bounds // know the inner bounds

View File

@ -1108,6 +1108,7 @@ DEF_TEST(GrClipStack_DeviceRRect, r) {
DEF_TEST(GrClipStack_ScaleTranslate, r) { DEF_TEST(GrClipStack_ScaleTranslate, r) {
SkMatrix lm = SkMatrix::Scale(2.f, 4.f); SkMatrix lm = SkMatrix::Scale(2.f, 4.f);
lm.postTranslate(15.5f, 14.3f); lm.postTranslate(15.5f, 14.3f);
SkASSERT(lm.preservesAxisAlignment() && lm.isScaleTranslate());
// Rect -> matrix is applied up front // Rect -> matrix is applied up front
SkRect rect = {0.f, 0.f, 10.f, 10.f}; SkRect rect = {0.f, 0.f, 10.f, 10.f};
@ -1140,6 +1141,43 @@ DEF_TEST(GrClipStack_ScaleTranslate, r) {
.finishTest()); .finishTest());
} }
// Tests that rect-stays-rect matrices that are not scale+translate matrices are pre-applied.
DEF_TEST(GrClipStack_PreserveAxisAlignment, r) {
SkMatrix lm = SkMatrix::RotateDeg(90.f);
lm.postTranslate(15.5f, 14.3f);
SkASSERT(lm.preservesAxisAlignment() && !lm.isScaleTranslate());
// Rect -> matrix is applied up front
SkRect rect = {0.f, 0.f, 10.f, 10.f};
run_test_case(r, TestCase::Build("r90+rect", kDeviceBounds)
.actual().rect(rect, lm, GrAA::kYes, SkClipOp::kIntersect)
.finishElements()
.expect().rect(lm.mapRect(rect), GrAA::kYes, SkClipOp::kIntersect)
.finishElements()
.state(GrClipStack::ClipState::kDeviceRect)
.finishTest());
// RRect -> matrix is applied up front
SkRRect localRRect = SkRRect::MakeRectXY(rect, 2.f, 2.f);
SkRRect deviceRRect;
SkAssertResult(localRRect.transform(lm, &deviceRRect));
run_test_case(r, TestCase::Build("r90+rrect", kDeviceBounds)
.actual().rrect(localRRect, lm, GrAA::kYes, SkClipOp::kIntersect)
.finishElements()
.expect().rrect(deviceRRect, GrAA::kYes, SkClipOp::kIntersect)
.finishElements()
.state(GrClipStack::ClipState::kDeviceRRect)
.finishTest());
// Path -> matrix is NOT applied
run_test_case(r, TestCase::Build("r90+path", kDeviceBounds)
.actual().intersect().localToDevice(lm).path(make_octagon(rect))
.finishElements()
.expectActual()
.state(GrClipStack::ClipState::kComplex)
.finishTest());
}
// Tests that a convex path element can contain a rect or round rect, allowing the stack to be // Tests that a convex path element can contain a rect or round rect, allowing the stack to be
// simplified // simplified
DEF_TEST(GrClipStack_ConvexPathContains, r) { DEF_TEST(GrClipStack_ConvexPathContains, r) {