diff options
author | Brian Salomon <bsalomon@google.com> | 2018-07-09 10:31:47 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-07-09 14:54:16 +0000 |
commit | 1354048c8fa885b83e414532c011d710590d6b46 (patch) | |
tree | e3fd906497340adbd73e5a1bbebc50c4f4ef4d15 | |
parent | 373224c9ab3ae3f13c45f61521e01c282016fde0 (diff) |
Add genIDs from all contributing elements to GrReducedClip's mask key.
Change-Id: I3fed124ba3fefd1ef82acdb4ace9531d0c89ad8b
Reviewed-on: https://skia-review.googlesource.com/138586
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
-rw-r--r-- | src/gpu/GrAppliedClip.h | 17 | ||||
-rw-r--r-- | src/gpu/GrClipStackClip.cpp | 84 | ||||
-rw-r--r-- | src/gpu/GrClipStackClip.h | 3 | ||||
-rw-r--r-- | src/gpu/GrReducedClip.cpp | 200 | ||||
-rw-r--r-- | src/gpu/GrReducedClip.h | 42 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContextPriv.h | 21 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetOpList.cpp | 3 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetOpList.h | 9 | ||||
-rw-r--r-- | src/gpu/GrStencilClip.h | 2 | ||||
-rw-r--r-- | src/gpu/ops/GrStencilAndCoverPathRenderer.cpp | 2 | ||||
-rw-r--r-- | tests/ClipStackTest.cpp | 76 |
11 files changed, 244 insertions, 215 deletions
diff --git a/src/gpu/GrAppliedClip.h b/src/gpu/GrAppliedClip.h index bfe3383e16..eb136eaccb 100644 --- a/src/gpu/GrAppliedClip.h +++ b/src/gpu/GrAppliedClip.h @@ -27,8 +27,7 @@ public: const GrScissorState& scissorState() const { return fScissorState; } const GrWindowRectsState& windowRectsState() const { return fWindowRectsState; } - uint32_t stencilStackID() const { return fStencilStackID; } - bool hasStencilClip() const { return SkClipStack::kInvalidGenID != fStencilStackID; } + bool hasStencilClip() const { return fHasStencilClip; } /** * Intersects the applied clip with the provided rect. Returns false if the draw became empty. @@ -49,9 +48,9 @@ public: fWindowRectsState.set(windows, mode); } - void addStencilClip(uint32_t stencilStackID) { - SkASSERT(SkClipStack::kInvalidGenID == fStencilStackID); - fStencilStackID = stencilStackID; + void addStencilClip() { + SkASSERT(!fHasStencilClip); + fHasStencilClip = true; } bool doesClip() const { @@ -59,16 +58,15 @@ public: } bool operator==(const GrAppliedHardClip& that) const { - return fScissorState == that.fScissorState && - fWindowRectsState == that.fWindowRectsState && - fStencilStackID == that.fStencilStackID; + return fScissorState == that.fScissorState && fWindowRectsState == that.fWindowRectsState && + fHasStencilClip == that.fHasStencilClip; } bool operator!=(const GrAppliedHardClip& that) const { return !(*this == that); } private: GrScissorState fScissorState; GrWindowRectsState fWindowRectsState; - uint32_t fStencilStackID = SkClipStack::kInvalidGenID; + bool fHasStencilClip = false; }; /** @@ -82,7 +80,6 @@ public: const GrScissorState& scissorState() const { return fHardClip.scissorState(); } const GrWindowRectsState& windowRectsState() const { return fHardClip.windowRectsState(); } - uint32_t stencilStackID() const { return fHardClip.stencilStackID(); } bool hasStencilClip() const { return fHardClip.hasStencilClip(); } int numClipCoverageFragmentProcessors() const { return fClipCoverageFPs.count(); } const GrFragmentProcessor* clipCoverageFragmentProcessor(int i) const { diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp index 68c18be5e8..dea60ff3b4 100644 --- a/src/gpu/GrClipStackClip.cpp +++ b/src/gpu/GrClipStackClip.cpp @@ -27,11 +27,10 @@ #include "effects/GrRRectEffect.h" #include "effects/GrTextureDomain.h" -typedef SkClipStack::Element Element; -typedef GrReducedClip::InitialState InitialState; -typedef GrReducedClip::ElementList ElementList; - -const char GrClipStackClip::kMaskTestTag[] = "clip_mask"; +using MaskElement = GrReducedClip::MaskElement; +using Element = SkClipStack::Element; +using InitialState = GrReducedClip::InitialState; +using ElementList = GrReducedClip::ElementList; bool GrClipStackClip::quickContains(const SkRect& rect) const { if (!fStack || fStack->isWideOpen()) { @@ -91,10 +90,10 @@ bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context, bool hasUserStencilSettings, const GrRenderTargetContext* renderTargetContext, const SkMatrix& viewMatrix, - const Element* element, + const Element& element, GrPathRenderer** prOut, bool needsStencil) { - if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { + if (Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) { // rects can always be drawn directly w/o using the software path // TODO: skip rrects once we're drawing them directly. if (prOut) { @@ -103,11 +102,11 @@ bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context, return false; } else { // We shouldn't get here with an empty clip element. - SkASSERT(Element::DeviceSpaceType::kEmpty != element->getDeviceSpaceType()); + SkASSERT(Element::DeviceSpaceType::kEmpty != element.getDeviceSpaceType()); // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer SkPath path; - element->asDeviceSpacePath(&path); + element.asDeviceSpacePath(&path); if (path.isInverseFillType()) { path.toggleInverseFillType(); } @@ -122,7 +121,7 @@ bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context, canDrawArgs.fClipConservativeBounds = &scissorRect; canDrawArgs.fViewMatrix = &viewMatrix; canDrawArgs.fShape = &shape; - canDrawArgs.fAAType = GrChooseAAType(GrAA(element->isAA()), + canDrawArgs.fAAType = GrChooseAAType(GrAA(element.isAA()), renderTargetContext->fsaaType(), GrAllowMixedSamples::kYes, *context->contextPriv().caps()); @@ -162,10 +161,10 @@ bool GrClipStackClip::UseSWOnlyPath(GrContext* context, translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top())); for (ElementList::Iter iter(reducedClip.maskElements()); iter.get(); iter.next()) { - const Element* element = iter.get(); + const Element& element = iter.get()->fElement; - SkClipOp op = element->getOp(); - bool invert = element->isInverseFilled(); + SkClipOp op = element.getOp(); + bool invert = element.isInverseFilled(); bool needsStencil = invert || kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op; @@ -281,38 +280,25 @@ bool GrClipStackClip::applyClipMask(GrContext* context, GrRenderTargetContext* r renderTargetContext->setNeedsStencil(); - // This relies on the property that a reduced sub-rect of the last clip will contain all the - // relevant window rectangles that were in the last clip. This subtle requirement will go away - // after clipping is overhauled. - if (renderTargetContext->priv().mustRenderClip(reducedClip.maskGenID(), reducedClip.scissor(), - reducedClip.numAnalyticFPs())) { + if (!renderTargetContext->priv().lastStencilClipKey().isValid() || + !reducedClip.maskUniqueKey().isValid() || + renderTargetContext->priv().lastStencilClipKey() != reducedClip.maskUniqueKey()) { reducedClip.drawStencilClipMask(context, renderTargetContext); - renderTargetContext->priv().setLastClip(reducedClip.maskGenID(), reducedClip.scissor(), - reducedClip.numAnalyticFPs()); + renderTargetContext->priv().setLastStencilClipKey(reducedClip.maskUniqueKey()); } - // GrAppliedClip doesn't need to figure numAnalyticFPs into its key (used by operator==) because - // it verifies the FPs are also equal. - out->hardClip().addStencilClip(reducedClip.maskGenID()); + // The enables the stencil test against the clip bit in the applied clip. + out->hardClip().addStencilClip(); return true; } //////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha -static void create_clip_mask_key(uint32_t clipGenID, const SkIRect& bounds, int numAnalyticFPs, - GrUniqueKey* key) { - static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); - GrUniqueKey::Builder builder(key, kDomain, 4, GrClipStackClip::kMaskTestTag); - builder[0] = clipGenID; - // SkToS16 because image filters outset layers to a size indicated by the filter, which can - // sometimes result in negative coordinates from device space. - builder[1] = SkToS16(bounds.fLeft) | (SkToS16(bounds.fRight) << 16); - builder[2] = SkToS16(bounds.fTop) | (SkToS16(bounds.fBottom) << 16); - builder[3] = numAnalyticFPs; -} - static void add_invalidate_on_pop_message(const SkClipStack& stack, uint32_t clipGenID, const GrUniqueKey& clipMaskKey) { + if (clipGenID == SK_InvalidGenID || !clipMaskKey.isValid()) { + return; + } SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); while (const Element* element = iter.prev()) { if (element->getGenID() == clipGenID) { @@ -328,10 +314,7 @@ static void add_invalidate_on_pop_message(const SkClipStack& stack, uint32_t cli sk_sp<GrTextureProxy> GrClipStackClip::createAlphaClipMask(GrContext* context, const GrReducedClip& reducedClip) const { GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); - GrUniqueKey key; - create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), - reducedClip.numAnalyticFPs(), &key); - + const GrUniqueKey& key = reducedClip.maskUniqueKey(); sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey( key, kTopLeft_GrSurfaceOrigin)); if (proxy) { @@ -362,7 +345,7 @@ sk_sp<GrTextureProxy> GrClipStackClip::createAlphaClipMask(GrContext* context, SkASSERT(result->origin() == kTopLeft_GrSurfaceOrigin); proxyProvider->assignUniqueKeyToProxy(key, result.get()); - add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key); + add_invalidate_on_pop_message(*fStack, reducedClip.topMaskElementID(), key); return result; } @@ -406,9 +389,9 @@ static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const Elem helper.clear(InitialState::kAllIn == initialState ? 0xFF : 0x00); for (ElementList::Iter iter(elements); iter.get(); iter.next()) { - const Element* element = iter.get(); - SkClipOp op = element->getOp(); - GrAA aa = GrAA(element->isAA()); + const Element& element = iter.get()->fElement; + SkClipOp op = element.getOp(); + GrAA aa = GrAA(element.isAA()); if (kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op) { // Intersect and reverse difference require modifying pixels outside of the geometry @@ -421,7 +404,7 @@ static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const Elem helper.drawRect(temp, translate, SkRegion::kXOR_Op, GrAA::kNo, 0xFF); } SkPath clipPath; - element->asDeviceSpacePath(&clipPath); + element.asDeviceSpacePath(&clipPath); clipPath.toggleInverseFillType(); GrShape shape(clipPath, GrStyle::SimpleFill()); helper.drawShape(shape, translate, SkRegion::kReplace_Op, aa, 0x00); @@ -430,11 +413,11 @@ static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const Elem // The other ops (union, xor, diff) only affect pixels inside // the geometry so they can just be drawn normally - if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { - helper.drawRect(element->getDeviceSpaceRect(), translate, (SkRegion::Op)op, aa, 0xFF); + if (Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) { + helper.drawRect(element.getDeviceSpaceRect(), translate, (SkRegion::Op)op, aa, 0xFF); } else { SkPath path; - element->asDeviceSpacePath(&path); + element.asDeviceSpacePath(&path); GrShape shape(path, GrStyle::SimpleFill()); helper.drawShape(shape, translate, (SkRegion::Op)op, aa, 0xFF); } @@ -444,9 +427,7 @@ static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const Elem sk_sp<GrTextureProxy> GrClipStackClip::createSoftwareClipMask( GrContext* context, const GrReducedClip& reducedClip, GrRenderTargetContext* renderTargetContext) const { - GrUniqueKey key; - create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), - reducedClip.numAnalyticFPs(), &key); + const GrUniqueKey& key = reducedClip.maskUniqueKey(); GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); @@ -503,6 +484,7 @@ sk_sp<GrTextureProxy> GrClipStackClip::createSoftwareClipMask( SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); proxyProvider->assignUniqueKeyToProxy(key, proxy.get()); - add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key); + add_invalidate_on_pop_message(*fStack, reducedClip.topMaskElementID(), key); + return proxy; } diff --git a/src/gpu/GrClipStackClip.h b/src/gpu/GrClipStackClip.h index aeb983468c..d4075af4c7 100644 --- a/src/gpu/GrClipStackClip.h +++ b/src/gpu/GrClipStackClip.h @@ -34,7 +34,6 @@ public: bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA* aa) const override; sk_sp<GrTextureProxy> testingOnly_createClipMask(GrContext*) const; - static const char kMaskTestTag[]; private: static bool PathNeedsSWRenderer(GrContext* context, @@ -42,7 +41,7 @@ private: bool hasUserStencilSettings, const GrRenderTargetContext*, const SkMatrix& viewMatrix, - const SkClipStack::Element* element, + const SkClipStack::Element& element, GrPathRenderer** prOut, bool needsStencil); diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp index 2377846153..de3d27fb56 100644 --- a/src/gpu/GrReducedClip.cpp +++ b/src/gpu/GrReducedClip.cpp @@ -26,6 +26,8 @@ #include "effects/GrConvexPolyEffect.h" #include "effects/GrRRectEffect.h" +constexpr char GrReducedClip::kMaskTestTag[]; + /** * There are plenty of optimizations that could be added here. Maybe flips could be folded into * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps @@ -89,8 +91,6 @@ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds fHasScissor = true; fAAClipRect = stackBounds; - fAAClipRectGenID = stack.getTopmostGenID(); - SkASSERT(SK_InvalidGenID != fAAClipRectGenID); fInitialState = InitialState::kAllIn; } else { @@ -113,18 +113,84 @@ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds this->walkStack(stack, tighterQuery); } - if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect? - ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, GrAA::kYes)) { - if (fMaskElements.isEmpty()) { - // Use a replace since it is faster than intersect. - fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/); - fInitialState = InitialState::kAllOut; + // Is there an AA clip rect? + if (SK_InvalidGenID != fAAClipRectGenID || iior) { + if (ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, GrAA::kYes)) { + if (fMaskElements.isEmpty()) { + // Use a replace since it is faster than intersect. + fMaskElements.addToHead(fAAClipRect, kReplace_SkClipOp, true); + fInitialState = InitialState::kAllOut; + } else { + fMaskElements.addToTail(fAAClipRect, kIntersect_SkClipOp, true); + } + fMaskRequiresAA = true; } else { - fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/); + // We didn't include the aa clip rect in the mask elements. Clear this so we don't + // include the rect in the mask key or report it as the top-most mask genID. + fAAClipRectGenID = SK_InvalidGenID; + } + } +} + +const GrUniqueKey& GrReducedClip::maskUniqueKey() const { + if (fMaskUniqueKey.isValid() || !fMaskElements.count()) { + SkASSERT(SkToBool(fMaskElements.count()) == fMaskUniqueKey.isValid()); + return fMaskUniqueKey; + } + int size = fMaskElements.count(); + if (fAAClipRectGenID != SK_InvalidGenID) { + // 4 for the rect edges, -1 for the rect element in fMaskElmeents which has genID 0. + size += 3; + } + size += 4; // scissor + + static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); + GrUniqueKey::Builder builder(&fMaskUniqueKey, kDomain, size, kMaskTestTag); + int i = 0; + for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) { + if (iter.get()->fOriginalGenID != SK_InvalidGenID) { + builder[i++] = iter.get()->fOriginalGenID; + } + } + if (fAAClipRectGenID != SK_InvalidGenID) { + GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(float)); + builder[i++] = static_cast<uint32_t>(SkFloat2Bits(fAAClipRect.fLeft)); + builder[i++] = static_cast<uint32_t>(SkFloat2Bits(fAAClipRect.fTop)); + builder[i++] = static_cast<uint32_t>(SkFloat2Bits(fAAClipRect.fRight)); + builder[i++] = static_cast<uint32_t>(SkFloat2Bits(fAAClipRect.fBottom)); + } + builder[i++] = static_cast<uint32_t>(fScissor.fLeft); + builder[i++] = static_cast<uint32_t>(fScissor.fTop); + builder[i++] = static_cast<uint32_t>(fScissor.fRight); + builder[i++] = static_cast<uint32_t>(fScissor.fBottom); + builder.finish(); + + return fMaskUniqueKey; +} + +uint32_t GrReducedClip::topMaskElementID() const { +#ifdef SK_DEBUG + // We expect all mask elements to have a valid gen ID with the exception of the + // unified AA rect (if present and part of the mask). + bool foundInvalidGenID = false; + for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) { + if (iter.get()->fOriginalGenID == SK_InvalidGenID) { + SkASSERT(!foundInvalidGenID); + // In the intersection-of-rect cases we don't know or store the top-most gen ID that + // contributed to the AA rect. In that case there is only one element. + SkASSERT(fAAClipRectGenID != SK_InvalidGenID || fMaskElements.count() == 1); + foundInvalidGenID = true; } - fMaskRequiresAA = true; - fMaskGenID = fAAClipRectGenID; } +#endif + if (!fMaskElements.count()) { + return SK_InvalidGenID; + } + auto topGenID = fMaskElements.tail()->fOriginalGenID; + if (topGenID == SK_InvalidGenID) { + return fAAClipRectGenID; + } + return topGenID; } void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) { @@ -155,7 +221,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); int numAAElements = 0; while (InitialTriState::kUnknown == initialTriState) { - const Element* element = iter.prev(); + const SkClipStack::Element* element = iter.prev(); if (nullptr == element) { initialTriState = InitialTriState::kAllIn; break; @@ -362,19 +428,14 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound break; } if (!skippable) { - if (fMaskElements.isEmpty()) { - // This will be the last element. Record the stricter genID. - fMaskGenID = element->getGenID(); - } - // if it is a flip, change it to a bounds-filling rect if (isFlip) { SkASSERT(kXOR_SkClipOp == element->getOp() || kReverseDifference_SkClipOp == element->getOp()); - fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(), - kReverseDifference_SkClipOp, false); + fMaskElements.addToHead(SkRect::Make(fScissor), kReverseDifference_SkClipOp, false, + element->getGenID()); } else { - Element* newElement = fMaskElements.addToHead(*element); + SkClipStack::Element* newElement = &fMaskElements.addToHead(*element)->fElement; if (newElement->isAA()) { ++numAAElements; } @@ -400,8 +461,8 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound fMaskElements.reset(); numAAElements = 0; } else { - Element* element = fMaskElements.headIter().get(); - while (element) { + SkClipStack::Element* element = &fMaskElements.headIter().get()->fElement; + while (true) { bool skippable = false; switch (element->getOp()) { case kDifference_SkClipOp: @@ -458,14 +519,18 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound SkDEBUGFAIL("Unexpected op."); break; } - if (!skippable) { - break; - } else { + if (skippable) { if (element->isAA()) { --numAAElements; } fMaskElements.popHead(); - element = fMaskElements.headIter().get(); + if (fMaskElements.count()) { + element = &fMaskElements.headIter().get()->fElement; + } else { + break; + } + } else { + break; } } } @@ -475,7 +540,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState); } -GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) { +GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const SkClipStack::Element* element) { SkIRect elementIBounds; if (!element->isAA()) { element->getBounds().round(&elementIBounds); @@ -489,21 +554,16 @@ GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* elemen } switch (element->getDeviceSpaceType()) { - case Element::DeviceSpaceType::kEmpty: + case SkClipStack::Element::DeviceSpaceType::kEmpty: return ClipResult::kMadeEmpty; - case Element::DeviceSpaceType::kRect: + case SkClipStack::Element::DeviceSpaceType::kRect: SkASSERT(element->getBounds() == element->getDeviceSpaceRect()); SkASSERT(!element->isInverseFilled()); if (element->isAA()) { - if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet? + if (fAAClipRectGenID == SK_InvalidGenID) { // No AA clip rect yet? fAAClipRect = element->getDeviceSpaceRect(); - // fAAClipRectGenID is the value we should use for fMaskGenID if we end up - // moving the AA clip rect into the mask. The mask GenID is simply the topmost - // element's GenID. And since we walk the stack backwards, this means it's just - // the first element we don't skip during our walk. - fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID; - SkASSERT(SK_InvalidGenID != fAAClipRectGenID); + fAAClipRectGenID = element->getGenID(); } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) { this->makeEmpty(); return ClipResult::kMadeEmpty; @@ -511,12 +571,12 @@ GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* elemen } return ClipResult::kClipped; - case Element::DeviceSpaceType::kRRect: + case SkClipStack::Element::DeviceSpaceType::kRRect: SkASSERT(!element->isInverseFilled()); return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo, GrAA(element->isAA())); - case Element::DeviceSpaceType::kPath: + case SkClipStack::Element::DeviceSpaceType::kPath: return this->addAnalyticFP(element->getDeviceSpacePath(), Invert(element->isInverseFilled()), GrAA(element->isAA())); } @@ -525,12 +585,12 @@ GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* elemen return ClipResult::kNotClipped; } -GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) { +GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const SkClipStack::Element* element) { switch (element->getDeviceSpaceType()) { - case Element::DeviceSpaceType::kEmpty: + case SkClipStack::Element::DeviceSpaceType::kEmpty: return ClipResult::kMadeEmpty; - case Element::DeviceSpaceType::kRect: + case SkClipStack::Element::DeviceSpaceType::kRect: SkASSERT(!element->isInverseFilled()); if (fWindowRects.count() < fMaxWindowRectangles) { // Clip out the inside of every rect. We won't be able to entirely skip the AA ones, @@ -543,7 +603,7 @@ GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* eleme return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes, GrAA(element->isAA())); - case Element::DeviceSpaceType::kRRect: { + case SkClipStack::Element::DeviceSpaceType::kRRect: { SkASSERT(!element->isInverseFilled()); const SkRRect& clipRRect = element->getDeviceSpaceRRect(); ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes, @@ -583,7 +643,7 @@ GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* eleme return clipResult; } - case Element::DeviceSpaceType::kPath: + case SkClipStack::Element::DeviceSpaceType::kPath: return this->addAnalyticFP(element->getDeviceSpacePath(), Invert(!element->isInverseFilled()), GrAA(element->isAA())); } @@ -681,26 +741,26 @@ static bool stencil_element(GrRenderTargetContext* rtc, const GrFixedClip& clip, const GrUserStencilSettings* ss, const SkMatrix& viewMatrix, - const SkClipStack::Element* element) { - GrAA aa = GrAA(element->isAA()); - switch (element->getDeviceSpaceType()) { + const SkClipStack::Element& element) { + GrAA aa = GrAA(element.isAA()); + switch (element.getDeviceSpaceType()) { case SkClipStack::Element::DeviceSpaceType::kEmpty: SkDEBUGFAIL("Should never get here with an empty element."); break; case SkClipStack::Element::DeviceSpaceType::kRect: - return rtc->priv().drawAndStencilRect(clip, ss, (SkRegion::Op)element->getOp(), - element->isInverseFilled(), aa, viewMatrix, - element->getDeviceSpaceRect()); + return rtc->priv().drawAndStencilRect(clip, ss, (SkRegion::Op)element.getOp(), + element.isInverseFilled(), aa, viewMatrix, + element.getDeviceSpaceRect()); break; default: { SkPath path; - element->asDeviceSpacePath(&path); + element.asDeviceSpacePath(&path); if (path.isInverseFillType()) { path.toggleInverseFillType(); } - return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(), - element->isInverseFilled(), aa, viewMatrix, path); + return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element.getOp(), + element.isInverseFilled(), aa, viewMatrix, path); break; } } @@ -713,18 +773,18 @@ static void draw_element(GrRenderTargetContext* rtc, GrPaint&& paint, GrAA aa, const SkMatrix& viewMatrix, - const SkClipStack::Element* element) { + const SkClipStack::Element& element) { // TODO: Draw rrects directly here. - switch (element->getDeviceSpaceType()) { + switch (element.getDeviceSpaceType()) { case SkClipStack::Element::DeviceSpaceType::kEmpty: SkDEBUGFAIL("Should never get here with an empty element."); break; case SkClipStack::Element::DeviceSpaceType::kRect: - rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect()); + rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element.getDeviceSpaceRect()); break; default: { SkPath path; - element->asDeviceSpacePath(&path); + element.asDeviceSpacePath(&path); if (path.isInverseFillType()) { path.toggleInverseFillType(); } @@ -756,10 +816,10 @@ bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const { // walk through each clip element and perform its set op for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) { - const Element* element = iter.get(); - SkRegion::Op op = (SkRegion::Op)element->getOp(); - GrAA aa = GrAA(element->isAA()); - bool invert = element->isInverseFilled(); + const SkClipStack::Element& element = iter.get()->fElement; + SkRegion::Op op = (SkRegion::Op)element.getOp(); + GrAA aa = GrAA(element.isAA()); + bool invert = element.isInverseFilled(); if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. @@ -808,7 +868,7 @@ bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const { bool GrReducedClip::drawStencilClipMask(GrContext* context, GrRenderTargetContext* renderTargetContext) const { // We set the current clip to the bounds so that our recursive draws are scissored to them. - GrStencilClip stencilClip(fScissor, this->maskGenID()); + GrStencilClip stencilClip(fScissor, /*enable stencil clip test*/ true); if (!fWindowRects.empty()) { stencilClip.fixedClip().setWindowRectangles(fWindowRects, @@ -820,9 +880,9 @@ bool GrReducedClip::drawStencilClipMask(GrContext* context, // walk through each clip element and perform its set op with the existing clip. for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) { - const Element* element = iter.get(); + const SkClipStack::Element& element = iter.get()->fElement; GrAAType aaType = GrAAType::kNone; - if (element->isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) { + if (element.isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) { aaType = GrAAType::kMSAA; } @@ -832,15 +892,15 @@ bool GrReducedClip::drawStencilClipMask(GrContext* context, // stencil with arbitrary stencil settings. GrPathRenderer::StencilSupport stencilSupport; - SkRegion::Op op = (SkRegion::Op)element->getOp(); + SkRegion::Op op = (SkRegion::Op)element.getOp(); GrPathRenderer* pr = nullptr; SkPath clipPath; - if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { + if (SkClipStack::Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) { stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; fillInverted = false; } else { - element->asDeviceSpacePath(&clipPath); + element.asDeviceSpacePath(&clipPath); fillInverted = clipPath.isInverseFillType(); if (fillInverted) { clipPath.toggleInverseFillType(); @@ -884,10 +944,10 @@ bool GrReducedClip::drawStencilClipMask(GrContext* context, GrUserStencilOp::kIncMaybeClamp, 0xffff>() ); - if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { + if (SkClipStack::Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) { renderTargetContext->priv().stencilRect(stencilClip.fixedClip(), &kDrawToStencil, aaType, SkMatrix::I(), - element->getDeviceSpaceRect()); + element.getDeviceSpaceRect()); } else { if (!clipPath.isEmpty()) { GrShape shape(clipPath, GrStyle::SimpleFill()); @@ -925,10 +985,10 @@ bool GrReducedClip::drawStencilClipMask(GrContext* context, // element directly or a bounding rect of the entire clip. for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) { if (drawDirectToClip) { - if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { + if (SkClipStack::Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) { renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, SkMatrix::I(), - element->getDeviceSpaceRect()); + element.getDeviceSpaceRect()); } else { GrShape shape(clipPath, GrStyle::SimpleFill()); GrPaint paint; diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h index fcdc66c843..b8447555b0 100644 --- a/src/gpu/GrReducedClip.h +++ b/src/gpu/GrReducedClip.h @@ -23,8 +23,22 @@ class GrRenderTargetContext; */ class SK_API GrReducedClip { public: - using Element = SkClipStack::Element; - using ElementList = SkTLList<SkClipStack::Element, 16>; + /** + * Elements that contribute to the mask are not always directly copied from SkClipStack and + * may instead be synthesized by GrReducedClip. Therefore, we store the "original" gen ID of + * the element that corresponds to the MaskElement. If the MaskElement does not correspond to + * a single SkClipStack element then this ID will be SK_InvalidGenID. + */ + struct MaskElement { + MaskElement(const SkClipStack::Element& element) + : fElement(element), fOriginalGenID(element.getGenID()) {} + MaskElement(const SkRect& rect, SkClipOp op, bool doAA, uint32_t genID = SK_InvalidGenID) + : fElement(rect, SkMatrix::I(), op, doAA), fOriginalGenID(genID) {} + SkClipStack::Element fElement; + uint32_t fOriginalGenID; + }; + + using ElementList = SkTLList<MaskElement, 16>; GrReducedClip(const SkClipStack&, const SkRect& queryBounds, const GrCaps* caps, int maxWindowRectangles = 0, int maxAnalyticFPs = 0, int maxCCPRClipPaths = 0); @@ -65,17 +79,13 @@ public: */ const ElementList& maskElements() const { return fMaskElements; } + const GrUniqueKey& maskUniqueKey() const; + /** - * If maskElements() are nonempty, uniquely identifies the region of the clip mask that falls - * inside of scissor(). - * - * NOTE: since clip elements might fall outside the query bounds, different regions of the same - * clip stack might have more or less restrictive IDs. - * - * FIXME: this prevents us from reusing a sub-rect of a perfectly good mask when that rect has - * been assigned a less restrictive ID. + * Returns the unique ID of the top most element that contributes to the mask. If this element + * is popped then any cached mask with maskUniqueKey() can be purged. */ - uint32_t maskGenID() const { SkASSERT(!fMaskElements.isEmpty()); return fMaskGenID; } + uint32_t topMaskElementID() const; /** * Indicates whether antialiasing is required to process any of the mask elements. @@ -100,6 +110,8 @@ public: uint32_t opListID, int rtWidth, int rtHeight); + static constexpr char kMaskTestTag[] = "clip_mask"; + private: void walkStack(const SkClipStack&, const SkRect& queryBounds); @@ -111,11 +123,11 @@ private: // Intersects the clip with the element's interior, regardless of inverse fill type. // NOTE: do not call for elements followed by ops that can grow the clip. - ClipResult clipInsideElement(const Element*); + ClipResult clipInsideElement(const SkClipStack::Element*); // Intersects the clip with the element's exterior, regardless of inverse fill type. // NOTE: do not call for elements followed by ops that can grow the clip. - ClipResult clipOutsideElement(const Element*); + ClipResult clipOutsideElement(const SkClipStack::Element*); void addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA); @@ -140,11 +152,11 @@ private: SkIRect fScissor; bool fHasScissor; SkRect fAAClipRect; - uint32_t fAAClipRectGenID; // GenID the mask will have if includes the AA clip rect. + uint32_t fAAClipRectGenID; // top most GenID that contributed to fAAClipRect. GrWindowRectangles fWindowRects; ElementList fMaskElements; - uint32_t fMaskGenID; bool fMaskRequiresAA; + mutable GrUniqueKey fMaskUniqueKey; SkSTArray<4, std::unique_ptr<GrFragmentProcessor>> fAnalyticFPs; SkSTArray<4, SkPath> fCCPRClipPaths; // Will convert to FPs once we have an opList ID for CCPR. }; diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h index 52f5497603..94d52debcf 100644 --- a/src/gpu/GrRenderTargetContextPriv.h +++ b/src/gpu/GrRenderTargetContextPriv.h @@ -23,24 +23,11 @@ struct GrUserStencilSettings; additional data members or virtual methods. */ class GrRenderTargetContextPriv { public: - // called to note the last clip drawn to the stencil buffer. - // TODO: remove after clipping overhaul. - void setLastClip(uint32_t clipStackGenID, const SkIRect& devClipBounds, - int numClipAnalyticFPs) { - GrRenderTargetOpList* opList = fRenderTargetContext->getRTOpList(); - opList->fLastClipStackGenID = clipStackGenID; - opList->fLastDevClipBounds = devClipBounds; - opList->fLastClipNumAnalyticFPs = numClipAnalyticFPs; + const GrUniqueKey& lastStencilClipKey() const { + return fRenderTargetContext->getRTOpList()->lastStencilClipKey(); } - - // called to determine if we have to render the clip into SB. - // TODO: remove after clipping overhaul. - bool mustRenderClip(uint32_t clipStackGenID, const SkIRect& devClipBounds, - int numClipAnalyticFPs) const { - GrRenderTargetOpList* opList = fRenderTargetContext->getRTOpList(); - return opList->fLastClipStackGenID != clipStackGenID || - !opList->fLastDevClipBounds.contains(devClipBounds) || - opList->fLastClipNumAnalyticFPs != numClipAnalyticFPs; + void setLastStencilClipKey(const GrUniqueKey& key) { + fRenderTargetContext->getRTOpList()->setLastStencilClipKey(key); } using CanClearFullscreen = GrRenderTargetContext::CanClearFullscreen; diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp index 423b71ca10..b3f31874d9 100644 --- a/src/gpu/GrRenderTargetOpList.cpp +++ b/src/gpu/GrRenderTargetOpList.cpp @@ -30,7 +30,6 @@ GrRenderTargetOpList::GrRenderTargetOpList(GrResourceProvider* resourceProvider, GrRenderTargetProxy* proxy, GrAuditTrail* auditTrail) : INHERITED(resourceProvider, std::move(opMemoryPool), proxy, auditTrail) - , fLastClipStackGenID(SK_InvalidUniqueID) SkDEBUGCODE(, fNumClips(0)) { } @@ -202,7 +201,7 @@ bool GrRenderTargetOpList::onExecute(GrOpFlushState* flushState) { } void GrRenderTargetOpList::endFlush() { - fLastClipStackGenID = SK_InvalidUniqueID; + fLastStencilClipKey.reset(); this->deleteOps(); fClipAllocator.reset(); INHERITED::endFlush(); diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h index 8629a7cb1f..0c7d983b29 100644 --- a/src/gpu/GrRenderTargetOpList.h +++ b/src/gpu/GrRenderTargetOpList.h @@ -120,9 +120,10 @@ public: SkDEBUGCODE(int numClips() const override { return fNumClips; }) SkDEBUGCODE(void visitProxies_debugOnly(const GrOp::VisitProxyFunc&) const;) -private: - friend class GrRenderTargetContextPriv; // for stencil clip state. TODO: this is invasive + const GrUniqueKey& lastStencilClipKey() const { return fLastStencilClipKey; } + void setLastStencilClipKey(const GrUniqueKey& key) { fLastStencilClipKey = key; } +private: void deleteOps(); struct RecordedOp { @@ -172,9 +173,7 @@ private: bool combineIfPossible(const RecordedOp& a, GrOp* b, const GrAppliedClip* bClip, const DstProxy* bDstTexture, const GrCaps&); - uint32_t fLastClipStackGenID; - SkIRect fLastDevClipBounds; - int fLastClipNumAnalyticFPs; + GrUniqueKey fLastStencilClipKey; // For ops/opList we have mean: 5 stdDev: 28 SkSTArray<5, RecordedOp, true> fRecordedOps; diff --git a/src/gpu/GrStencilClip.h b/src/gpu/GrStencilClip.h index 2bbf3b44de..b0cd4e41ba 100644 --- a/src/gpu/GrStencilClip.h +++ b/src/gpu/GrStencilClip.h @@ -44,7 +44,7 @@ public: return false; } if (this->hasStencilClip()) { - out->addStencilClip(fStencilStackID); + out->addStencilClip(); } return true; } diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp index f395927b63..1f048a9883 100644 --- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp +++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp @@ -108,7 +108,7 @@ bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { GrAATypeIsHW(args.fAAType), true, &appliedClip, &devBounds)) { return true; } - GrStencilClip stencilClip(appliedClip.stencilStackID()); + GrStencilClip stencilClip(true); if (appliedClip.scissorState().enabled()) { stencilClip.fixedClip().setScissor(appliedClip.scissorState().rect()); } diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp index 498e60d45a..60a8626c57 100644 --- a/tests/ClipStackTest.cpp +++ b/tests/ClipStackTest.cpp @@ -944,6 +944,18 @@ static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* } } +// When the entire clip is an intersection of rectangles we can wind up with a mask with one +// AA rect element that doesn't have a known top-most contributing element. +static bool mask_allowed_no_top_most_gen_id(const GrReducedClip& reduced) { + return 1 == reduced.maskElements().count() && + reduced.maskElements().head()->fElement.getDeviceSpaceType() == + SkClipStack::Element::DeviceSpaceType::kRect && + reduced.maskElements().head()->fElement.isAA() && + !reduced.maskElements().head()->fElement.isInverseFilled() && + reduced.maskElements().head()->fElement.getOp() == + static_cast<SkClipOp>(SkRegion::kReplace_Op); +} + static void test_reduced_clip_stack(skiatest::Reporter* reporter) { // We construct random clip stacks, reduce them, and then rasterize both versions to verify that // they are equal. @@ -1043,41 +1055,35 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) { auto context = GrContext::MakeMock(nullptr); const GrCaps* caps = context->contextPriv().caps(); - // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID - // will be kInvalidGenID if left uninitialized. - SkAlignedSTStorage<1, GrReducedClip> storage; - memset(storage.get(), 0, sizeof(GrReducedClip)); - GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID); - // Get the reduced version of the stack. SkRect queryBounds = kBounds; queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2); - const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps); + const GrReducedClip reduced(stack, queryBounds, caps); - REPORTER_ASSERT(reporter, - reduced->maskElements().isEmpty() || - SkClipStack::kInvalidGenID != reduced->maskGenID(), - testCase.c_str()); + if (!reduced.maskElements().isEmpty() && !mask_allowed_no_top_most_gen_id(reduced)) { + REPORTER_ASSERT(reporter, SK_InvalidGenID != reduced.topMaskElementID(), + testCase.c_str()); + } - if (!reduced->maskElements().isEmpty()) { - REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str()); + if (!reduced.maskElements().isEmpty()) { + REPORTER_ASSERT(reporter, reduced.hasScissor(), testCase.c_str()); SkRect stackBounds; SkClipStack::BoundsType stackBoundsType; stack.getBounds(&stackBounds, &stackBoundsType); - REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str()); + REPORTER_ASSERT(reporter, reduced.maskRequiresAA() == doAA, testCase.c_str()); } // Build a new clip stack based on the reduced clip elements SkClipStack reducedStack; - if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) { + if (GrReducedClip::InitialState::kAllOut == reduced.initialState()) { // whether the result is bounded or not, the whole plane should start outside the clip. reducedStack.clipEmpty(); } - for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) { - add_elem_to_stack(*iter.get(), &reducedStack); + for (ElementList::Iter iter(reduced.maskElements()); iter.get(); iter.next()) { + add_elem_to_stack(iter.get()->fElement, &reducedStack); } - SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds; + SkIRect scissor = reduced.hasScissor() ? reduced.scissor() : kIBounds; // GrReducedClipStack assumes that the final result is clipped to the returned bounds reducedStack.clipDevRect(scissor, kIntersect_SkClipOp); @@ -1091,8 +1097,6 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) { set_region_to_stack(reducedStack, scissor, &reducedRegion); REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str()); - - reduced->~GrReducedClip(); } } @@ -1102,7 +1106,7 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) { #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden"))) #endif -static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) { +static void test_reduced_clip_stack_mask_key(skiatest::Reporter* reporter) { { SkClipStack stack; stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp, @@ -1114,16 +1118,9 @@ static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) { auto context = GrContext::MakeMock(nullptr); const GrCaps* caps = context->contextPriv().caps(); - SkAlignedSTStorage<1, GrReducedClip> storage; - memset(storage.get(), 0, sizeof(GrReducedClip)); - GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID); - const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps); - - REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1); - // Clips will be cached based on the generation id. Make sure the gen id is valid. - REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID()); - - reduced->~GrReducedClip(); + GrReducedClip reduced(stack, bounds, caps); + REPORTER_ASSERT(reporter, reduced.maskElements().count() == 1); + REPORTER_ASSERT(reporter, reduced.maskUniqueKey().isValid()); } { SkClipStack stack; @@ -1162,7 +1159,7 @@ static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) { static const struct SUPPRESS_VISIBILITY_WARNING { SkRect testBounds; int reducedClipCount; - uint32_t reducedGenID; + uint32_t topStackGenID; InitialState initialState; SkIRect clipIRect; // parameter. @@ -1207,17 +1204,14 @@ static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) { const GrReducedClip reduced(stack, testCases[i].testBounds, caps); REPORTER_ASSERT(reporter, reduced.maskElements().count() == testCases[i].reducedClipCount); - SkASSERT(reduced.maskElements().count() == testCases[i].reducedClipCount); - if (reduced.maskElements().count()) { - REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID); - SkASSERT(reduced.maskGenID() == testCases[i].reducedGenID); + if (reduced.maskElements().count() && !mask_allowed_no_top_most_gen_id(reduced)) { + REPORTER_ASSERT(reporter, reduced.topMaskElementID() == testCases[i].topStackGenID); + REPORTER_ASSERT(reporter, reduced.maskUniqueKey().isValid()); } + REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState); - SkASSERT(reduced.initialState() == testCases[i].initialState); REPORTER_ASSERT(reporter, reduced.hasScissor()); - SkASSERT(reduced.hasScissor()); REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect); - SkASSERT(reduced.scissor() == testCases[i].clipIRect); } } } @@ -1474,7 +1468,7 @@ DEF_TEST(ClipStack, reporter) { test_invfill_diff_bug(reporter); test_reduced_clip_stack(reporter); - test_reduced_clip_stack_genid(reporter); + test_reduced_clip_stack_mask_key(reporter); test_reduced_clip_stack_no_aa_crash(reporter); test_reduced_clip_stack_aa(reporter); test_tiny_query_bounds_assertion_bug(reporter); @@ -1499,7 +1493,7 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) { path.addCircle(15, 15, 8); path.setFillType(SkPath::kEvenOdd_FillType); - static const char* kTag = GrClipStackClip::kMaskTestTag; + static const char* kTag = GrReducedClip::kMaskTestTag; GrResourceCache* cache = context->contextPriv().getResourceCache(); static constexpr int kN = 5; |