Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Unified Diff: src/gpu/GrReducedClip.cpp

Issue 2160093002: Allow GrReducedClip to take non-integer query bounds (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix tests on ubuntu Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/GrReducedClip.h ('k') | src/gpu/gl/GrGLIRect.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/GrReducedClip.cpp
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 26b89369524f0cfe40610a6b7ad8a56efb0b83ea..2940f6a682a6db215667f0361dc18d34c9b1be02 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -7,14 +7,16 @@
#include "GrReducedClip.h"
+#include "GrClip.h"
+
typedef SkClipStack::Element Element;
-static void reduced_stack_walker(const SkClipStack& stack,
- const SkRect& queryBounds,
- GrReducedClip::ElementList* result,
- int32_t* resultGenID,
- GrReducedClip::InitialState* initialState,
- bool* requiresAA) {
+static GrReducedClip::InitialState reduced_stack_walker(const SkClipStack& stack,
+ const SkRect& queryBounds,
+ const SkIRect& clipIBounds,
+ GrReducedClip::ElementList* result,
+ int32_t* resultGenID,
+ bool* requiresAA) {
// walk backwards until we get to:
// a) the beginning
@@ -23,27 +25,32 @@ static void reduced_stack_walker(const SkClipStack& stack,
static const GrReducedClip::InitialState kUnknown_InitialState =
static_cast<GrReducedClip::InitialState>(-1);
- *initialState = kUnknown_InitialState;
+ GrReducedClip::InitialState initialState = kUnknown_InitialState;
// During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
// TODO: track these per saved clip so that we can consider them on the forward pass.
bool embiggens = false;
bool emsmallens = false;
+ // We use a slightly relaxed set of query bounds for element containment tests. This is to
+ // account for floating point rounding error that may have occurred during coord transforms.
+ SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
+ GrClip::kBoundsTolerance);
+
SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
int numAAElements = 0;
- while ((kUnknown_InitialState == *initialState)) {
+ while (kUnknown_InitialState == initialState) {
const Element* element = iter.prev();
if (nullptr == element) {
- *initialState = GrReducedClip::kAllIn_InitialState;
+ initialState = GrReducedClip::kAllIn_InitialState;
break;
}
if (SkClipStack::kEmptyGenID == element->getGenID()) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ initialState = GrReducedClip::kAllOut_InitialState;
break;
}
if (SkClipStack::kWideOpenGenID == element->getGenID()) {
- *initialState = GrReducedClip::kAllIn_InitialState;
+ initialState = GrReducedClip::kAllIn_InitialState;
break;
}
@@ -55,17 +62,17 @@ static void reduced_stack_walker(const SkClipStack& stack,
// check if the shape subtracted either contains the entire bounds (and makes
// the clip empty) or is outside the bounds and therefore can be skipped.
if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
+ if (element->contains(relaxedQueryBounds)) {
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
}
} else {
- if (element->contains(queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ if (element->contains(relaxedQueryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
skippable = true;
}
}
@@ -78,17 +85,17 @@ static void reduced_stack_walker(const SkClipStack& stack,
// be skipped or it is outside the entire bounds and therefore makes the clip
// empty.
if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ if (element->contains(relaxedQueryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
skippable = true;
}
} else {
- if (element->contains(queryBounds)) {
+ if (element->contains(relaxedQueryBounds)) {
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
}
}
@@ -101,17 +108,17 @@ static void reduced_stack_walker(const SkClipStack& stack,
// the bounds is entirely inside the clip. If the union-ed shape is outside the
// bounds then this op can be skipped.
if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
+ if (element->contains(relaxedQueryBounds)) {
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = GrReducedClip::kAllIn_InitialState;
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
+ initialState = GrReducedClip::kAllIn_InitialState;
skippable = true;
}
} else {
- if (element->contains(queryBounds)) {
- *initialState = GrReducedClip::kAllIn_InitialState;
+ if (element->contains(relaxedQueryBounds)) {
+ initialState = GrReducedClip::kAllIn_InitialState;
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
skippable = true;
}
}
@@ -125,15 +132,15 @@ static void reduced_stack_walker(const SkClipStack& stack,
// able to take advantage of this in the forward pass. If the xor-ed shape
// doesn't intersect the bounds then it can be skipped.
if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
+ if (element->contains(relaxedQueryBounds)) {
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
isFlip = true;
}
} else {
- if (element->contains(queryBounds)) {
+ if (element->contains(relaxedQueryBounds)) {
isFlip = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
skippable = true;
}
}
@@ -147,17 +154,17 @@ static void reduced_stack_walker(const SkClipStack& stack,
// the bounds then we know after this element is applied that the bounds will be
// all outside the current clip.B
if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ if (element->contains(relaxedQueryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
isFlip = true;
}
} else {
- if (element->contains(queryBounds)) {
+ if (element->contains(relaxedQueryBounds)) {
isFlip = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
}
}
@@ -165,30 +172,31 @@ static void reduced_stack_walker(const SkClipStack& stack,
emsmallens = embiggens = true;
}
break;
+
case SkRegion::kReplace_Op:
// Replace will always terminate our walk. We will either begin the forward walk
// at the replace op or detect here than the shape is either completely inside
// or completely outside the bounds. In this latter case it can be skipped by
// setting the correct value for initialState.
if (element->isInverseFilled()) {
- if (element->contains(queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ if (element->contains(relaxedQueryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = GrReducedClip::kAllIn_InitialState;
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
+ initialState = GrReducedClip::kAllIn_InitialState;
skippable = true;
}
} else {
- if (element->contains(queryBounds)) {
- *initialState = GrReducedClip::kAllIn_InitialState;
+ if (element->contains(relaxedQueryBounds)) {
+ initialState = GrReducedClip::kAllIn_InitialState;
skippable = true;
- } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
+ initialState = GrReducedClip::kAllOut_InitialState;
skippable = true;
}
}
if (!skippable) {
- *initialState = GrReducedClip::kAllOut_InitialState;
+ initialState = GrReducedClip::kAllOut_InitialState;
embiggens = emsmallens = true;
}
break;
@@ -206,7 +214,8 @@ static void reduced_stack_walker(const SkClipStack& stack,
if (isFlip) {
SkASSERT(SkRegion::kXOR_Op == element->getOp() ||
SkRegion::kReverseDifference_Op == element->getOp());
- result->addToHead(queryBounds, SkRegion::kReverseDifference_Op, false);
+ result->addToHead(SkRect::Make(clipIBounds), SkRegion::kReverseDifference_Op,
+ false);
} else {
Element* newElement = result->addToHead(*element);
if (newElement->isAA()) {
@@ -221,17 +230,18 @@ static void reduced_stack_walker(const SkClipStack& stack,
newElement->invertShapeFillType();
newElement->setOp(SkRegion::kDifference_Op);
if (isReplace) {
- SkASSERT(GrReducedClip::kAllOut_InitialState == *initialState);
- *initialState = GrReducedClip::kAllIn_InitialState;
+ SkASSERT(GrReducedClip::kAllOut_InitialState == initialState);
+ initialState = GrReducedClip::kAllIn_InitialState;
}
}
}
}
}
- if ((GrReducedClip::kAllOut_InitialState == *initialState && !embiggens) ||
- (GrReducedClip::kAllIn_InitialState == *initialState && !emsmallens)) {
+ if ((GrReducedClip::kAllOut_InitialState == initialState && !embiggens) ||
+ (GrReducedClip::kAllIn_InitialState == initialState && !emsmallens)) {
result->reset();
+ numAAElements = 0;
} else {
Element* element = result->headIter().get();
while (element) {
@@ -239,20 +249,20 @@ static void reduced_stack_walker(const SkClipStack& stack,
switch (element->getOp()) {
case SkRegion::kDifference_Op:
// subtracting from the empty set yields the empty set.
- skippable = GrReducedClip::kAllOut_InitialState == *initialState;
+ skippable = GrReducedClip::kAllOut_InitialState == initialState;
break;
case SkRegion::kIntersect_Op:
// intersecting with the empty set yields the empty set
- if (GrReducedClip::kAllOut_InitialState == *initialState) {
+ if (GrReducedClip::kAllOut_InitialState == initialState) {
skippable = true;
} else {
// We can clear to zero and then simply draw the clip element.
- *initialState = GrReducedClip::kAllOut_InitialState;
+ initialState = GrReducedClip::kAllOut_InitialState;
element->setOp(SkRegion::kReplace_Op);
}
break;
case SkRegion::kUnion_Op:
- if (GrReducedClip::kAllIn_InitialState == *initialState) {
+ if (GrReducedClip::kAllIn_InitialState == initialState) {
// unioning the infinite plane with anything is a no-op.
skippable = true;
} else {
@@ -261,23 +271,23 @@ static void reduced_stack_walker(const SkClipStack& stack,
}
break;
case SkRegion::kXOR_Op:
- if (GrReducedClip::kAllOut_InitialState == *initialState) {
+ if (GrReducedClip::kAllOut_InitialState == initialState) {
// xor could be changed to diff in the kAllIn case, not sure it's a win.
element->setOp(SkRegion::kReplace_Op);
}
break;
case SkRegion::kReverseDifference_Op:
- if (GrReducedClip::kAllIn_InitialState == *initialState) {
+ if (GrReducedClip::kAllIn_InitialState == initialState) {
// subtracting the whole plane will yield the empty set.
skippable = true;
- *initialState = GrReducedClip::kAllOut_InitialState;
+ initialState = GrReducedClip::kAllOut_InitialState;
} else {
// this picks up flips inserted in the backwards pass.
skippable = element->isInverseFilled() ?
- !SkRect::Intersects(element->getBounds(), queryBounds) :
- element->contains(queryBounds);
+ GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
+ element->contains(relaxedQueryBounds);
if (skippable) {
- *initialState = GrReducedClip::kAllIn_InitialState;
+ initialState = GrReducedClip::kAllIn_InitialState;
} else {
element->setOp(SkRegion::kReplace_Op);
}
@@ -305,12 +315,15 @@ static void reduced_stack_walker(const SkClipStack& stack,
*requiresAA = numAAElements > 0;
if (0 == result->count()) {
- if (*initialState == GrReducedClip::kAllIn_InitialState) {
+ if (initialState == GrReducedClip::kAllIn_InitialState) {
*resultGenID = SkClipStack::kWideOpenGenID;
} else {
*resultGenID = SkClipStack::kEmptyGenID;
}
}
+
+ SkASSERT(SkClipStack::kInvalidGenID != *resultGenID);
+ return initialState;
}
/*
@@ -320,15 +333,13 @@ for the case where the bounds are kInsideOut_BoundsType. We could restrict earli
based on later intersect operations, and perhaps remove intersect-rects. We could optionally
take a rect in case the caller knows a bound on what is to be drawn through this clip.
*/
-void GrReducedClip::ReduceClipStack(const SkClipStack& stack,
- const SkIRect& queryBounds,
- ElementList* result,
- int32_t* resultGenID,
- InitialState* initialState,
- SkIRect* tighterBounds,
- bool* requiresAA) {
- SkASSERT(tighterBounds);
- SkASSERT(requiresAA);
+GrReducedClip::InitialState GrReducedClip::ReduceClipStack(const SkClipStack& stack,
+ const SkRect& queryBounds,
+ ElementList* result,
+ int32_t* resultGenID,
+ SkIRect* clipIBounds,
+ bool* requiresAA) {
+ SkASSERT(!queryBounds.isEmpty());
result->reset();
// The clip established by the element list might be cached based on the last
@@ -336,85 +347,55 @@ void GrReducedClip::ReduceClipStack(const SkClipStack& stack,
// id that lead to the state. Make a conservative guess.
*resultGenID = stack.getTopmostGenID();
+ // TODO: instead devise a way of telling the caller to disregard some or all of the clip bounds.
+ *clipIBounds = GrClip::GetPixelIBounds(queryBounds);
+
if (stack.isWideOpen()) {
- *initialState = kAllIn_InitialState;
- return;
+ return kAllIn_InitialState;
}
-
- // We initially look at whether the bounds alone is sufficient. We also use the stack bounds to
- // attempt to compute the tighterBounds.
-
SkClipStack::BoundsType stackBoundsType;
SkRect stackBounds;
bool iior;
stack.getBounds(&stackBounds, &stackBoundsType, &iior);
- const SkIRect* bounds = &queryBounds;
-
- SkRect scalarQueryBounds = SkRect::Make(queryBounds);
+ if (stackBounds.isEmpty() || GrClip::IsOutsideClip(stackBounds, queryBounds)) {
+ bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
+ return insideOut ? kAllIn_InitialState : kAllOut_InitialState;
+ }
if (iior) {
+ // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds.
+ // This should only be true if aa/non-aa status matches among all elements.
SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
- SkRect isectRect;
- if (stackBounds.contains(scalarQueryBounds)) {
- *initialState = GrReducedClip::kAllIn_InitialState;
- *tighterBounds = queryBounds;
- *requiresAA = false;
- } else if (isectRect.intersect(stackBounds, scalarQueryBounds)) {
- // If the caller asked for tighter integer bounds we may be able to
- // return kAllIn and give the bounds with no elements
- isectRect.roundOut(tighterBounds);
- SkRect scalarTighterBounds = SkRect::Make(*tighterBounds);
- if (scalarTighterBounds == isectRect) {
- // the round-out didn't add any area outside the clip rect.
- *requiresAA = false;
- *initialState = GrReducedClip::kAllIn_InitialState;
- return;
- }
- *initialState = kAllOut_InitialState;
- // iior should only be true if aa/non-aa status matches among all elements.
- SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
- bool doAA = iter.prev()->isAA();
- result->addToHead(isectRect, SkRegion::kReplace_Op, doAA);
- *requiresAA = doAA;
- } else {
- *initialState = kAllOut_InitialState;
- *requiresAA = false;
+ SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
+ if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
+ // The clip is a non-aa rect. This is the one spot where we can actually implement the
+ // clip (using clipIBounds) rather than just telling the caller what it should be.
+ stackBounds.round(clipIBounds);
+ return kAllIn_InitialState;
}
- return;
- } else {
- if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
- if (!SkRect::Intersects(stackBounds, scalarQueryBounds)) {
- *initialState = kAllOut_InitialState;
- *requiresAA = false;
- return;
- }
- SkIRect stackIBounds;
- stackBounds.roundOut(&stackIBounds);
- if (!tighterBounds->intersect(queryBounds, stackIBounds)) {
- SkASSERT(0);
- tighterBounds->setEmpty();
- }
- bounds = tighterBounds;
- } else {
- if (stackBounds.contains(scalarQueryBounds)) {
- *initialState = kAllOut_InitialState;
- // We know that the bounding box contains all the pixels that are outside the clip,
- // but we don't know that *all* the pixels in the box are outside the clip. So
- // proceed to walking the stack.
- }
- *tighterBounds = queryBounds;
+ if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
+ return kAllIn_InitialState;
}
+
+ // Implement the clip with an AA rect element.
+ result->addToHead(stackBounds, SkRegion::kReplace_Op, true/*doAA*/);
+ *requiresAA = true;
+
+ SkAssertResult(clipIBounds->intersect(GrClip::GetPixelIBounds(stackBounds)));
+ return kAllOut_InitialState;
}
- SkRect scalarBounds = SkRect::Make(*bounds);
+ SkRect tighterQuery = queryBounds;
+ if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
+ // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This new
+ // clip will be enforced by the scissor through clipIBounds.)
+ SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
+ *clipIBounds = GrClip::GetPixelIBounds(tighterQuery);
+ }
// Now that we have determined the bounds to use and filtered out the trivial cases, call the
// helper that actually walks the stack.
- reduced_stack_walker(stack, scalarBounds, result, resultGenID, initialState, requiresAA);
-
- // The list that was computed in this function may be cached based on the gen id of the last
- // element.
- SkASSERT(SkClipStack::kInvalidGenID != *resultGenID);
+ return reduced_stack_walker(stack, tighterQuery, *clipIBounds, result, resultGenID, requiresAA);
}
« no previous file with comments | « src/gpu/GrReducedClip.h ('k') | src/gpu/gl/GrGLIRect.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698