aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2018-07-09 18:23:58 +0000
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-07-09 18:24:08 +0000
commitc3833b4c152af3b6fa2a4c4ba7b1da02acd1af80 (patch)
tree87183da161e40d0b2fb86fdbe12a8570bfb54d40
parentd48897b576e9ee7df445606243d14bcb1d0b95df (diff)
Revert "Add genIDs from all contributing elements to GrReducedClip's mask key."
This reverts commit 1354048c8fa885b83e414532c011d710590d6b46. Reason for revert: tsan/valgrind failures Original change's description: > 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> TBR=bsalomon@google.com,robertphillips@google.com,csmartdalton@google.com Change-Id: Ia5bc098309cd02baf46f03d8ff17fabbd383481e No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://skia-review.googlesource.com/139920 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
-rw-r--r--src/gpu/GrAppliedClip.h17
-rw-r--r--src/gpu/GrClipStackClip.cpp84
-rw-r--r--src/gpu/GrClipStackClip.h3
-rw-r--r--src/gpu/GrReducedClip.cpp200
-rw-r--r--src/gpu/GrReducedClip.h42
-rw-r--r--src/gpu/GrRenderTargetContextPriv.h21
-rw-r--r--src/gpu/GrRenderTargetOpList.cpp3
-rw-r--r--src/gpu/GrRenderTargetOpList.h9
-rw-r--r--src/gpu/GrStencilClip.h2
-rw-r--r--src/gpu/ops/GrStencilAndCoverPathRenderer.cpp2
-rw-r--r--tests/ClipStackTest.cpp76
11 files changed, 215 insertions, 244 deletions
diff --git a/src/gpu/GrAppliedClip.h b/src/gpu/GrAppliedClip.h
index eb136eaccb..bfe3383e16 100644
--- a/src/gpu/GrAppliedClip.h
+++ b/src/gpu/GrAppliedClip.h
@@ -27,7 +27,8 @@ public:
const GrScissorState& scissorState() const { return fScissorState; }
const GrWindowRectsState& windowRectsState() const { return fWindowRectsState; }
- bool hasStencilClip() const { return fHasStencilClip; }
+ uint32_t stencilStackID() const { return fStencilStackID; }
+ bool hasStencilClip() const { return SkClipStack::kInvalidGenID != fStencilStackID; }
/**
* Intersects the applied clip with the provided rect. Returns false if the draw became empty.
@@ -48,9 +49,9 @@ public:
fWindowRectsState.set(windows, mode);
}
- void addStencilClip() {
- SkASSERT(!fHasStencilClip);
- fHasStencilClip = true;
+ void addStencilClip(uint32_t stencilStackID) {
+ SkASSERT(SkClipStack::kInvalidGenID == fStencilStackID);
+ fStencilStackID = stencilStackID;
}
bool doesClip() const {
@@ -58,15 +59,16 @@ public:
}
bool operator==(const GrAppliedHardClip& that) const {
- return fScissorState == that.fScissorState && fWindowRectsState == that.fWindowRectsState &&
- fHasStencilClip == that.fHasStencilClip;
+ return fScissorState == that.fScissorState &&
+ fWindowRectsState == that.fWindowRectsState &&
+ fStencilStackID == that.fStencilStackID;
}
bool operator!=(const GrAppliedHardClip& that) const { return !(*this == that); }
private:
GrScissorState fScissorState;
GrWindowRectsState fWindowRectsState;
- bool fHasStencilClip = false;
+ uint32_t fStencilStackID = SkClipStack::kInvalidGenID;
};
/**
@@ -80,6 +82,7 @@ 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 dea60ff3b4..68c18be5e8 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -27,10 +27,11 @@
#include "effects/GrRRectEffect.h"
#include "effects/GrTextureDomain.h"
-using MaskElement = GrReducedClip::MaskElement;
-using Element = SkClipStack::Element;
-using InitialState = GrReducedClip::InitialState;
-using ElementList = GrReducedClip::ElementList;
+typedef SkClipStack::Element Element;
+typedef GrReducedClip::InitialState InitialState;
+typedef GrReducedClip::ElementList ElementList;
+
+const char GrClipStackClip::kMaskTestTag[] = "clip_mask";
bool GrClipStackClip::quickContains(const SkRect& rect) const {
if (!fStack || fStack->isWideOpen()) {
@@ -90,10 +91,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) {
@@ -102,11 +103,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();
}
@@ -121,7 +122,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());
@@ -161,10 +162,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()->fElement;
+ const Element* element = iter.get();
- 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;
@@ -280,25 +281,38 @@ bool GrClipStackClip::applyClipMask(GrContext* context, GrRenderTargetContext* r
renderTargetContext->setNeedsStencil();
- if (!renderTargetContext->priv().lastStencilClipKey().isValid() ||
- !reducedClip.maskUniqueKey().isValid() ||
- renderTargetContext->priv().lastStencilClipKey() != reducedClip.maskUniqueKey()) {
+ // 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())) {
reducedClip.drawStencilClipMask(context, renderTargetContext);
- renderTargetContext->priv().setLastStencilClipKey(reducedClip.maskUniqueKey());
+ renderTargetContext->priv().setLastClip(reducedClip.maskGenID(), reducedClip.scissor(),
+ reducedClip.numAnalyticFPs());
}
- // The enables the stencil test against the clip bit in the applied clip.
- out->hardClip().addStencilClip();
+ // 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());
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) {
@@ -314,7 +328,10 @@ 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();
- const GrUniqueKey& key = reducedClip.maskUniqueKey();
+ GrUniqueKey key;
+ create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(),
+ reducedClip.numAnalyticFPs(), &key);
+
sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey(
key, kTopLeft_GrSurfaceOrigin));
if (proxy) {
@@ -345,7 +362,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.topMaskElementID(), key);
+ add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key);
return result;
}
@@ -389,9 +406,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()->fElement;
- SkClipOp op = element.getOp();
- GrAA aa = GrAA(element.isAA());
+ const Element* element = iter.get();
+ 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
@@ -404,7 +421,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);
@@ -413,11 +430,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);
}
@@ -427,7 +444,9 @@ 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 {
- const GrUniqueKey& key = reducedClip.maskUniqueKey();
+ GrUniqueKey key;
+ create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(),
+ reducedClip.numAnalyticFPs(), &key);
GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
@@ -484,7 +503,6 @@ sk_sp<GrTextureProxy> GrClipStackClip::createSoftwareClipMask(
SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
proxyProvider->assignUniqueKeyToProxy(key, proxy.get());
- add_invalidate_on_pop_message(*fStack, reducedClip.topMaskElementID(), key);
-
+ add_invalidate_on_pop_message(*fStack, reducedClip.maskGenID(), key);
return proxy;
}
diff --git a/src/gpu/GrClipStackClip.h b/src/gpu/GrClipStackClip.h
index d4075af4c7..aeb983468c 100644
--- a/src/gpu/GrClipStackClip.h
+++ b/src/gpu/GrClipStackClip.h
@@ -34,6 +34,7 @@ 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,
@@ -41,7 +42,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 de3d27fb56..2377846153 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -26,8 +26,6 @@
#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
@@ -91,6 +89,8 @@ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds
fHasScissor = true;
fAAClipRect = stackBounds;
+ fAAClipRectGenID = stack.getTopmostGenID();
+ SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
fInitialState = InitialState::kAllIn;
} else {
@@ -113,84 +113,18 @@ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds
this->walkStack(stack, tighterQuery);
}
- // 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;
+ 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;
} else {
- // 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;
+ fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
}
+ 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) {
@@ -221,7 +155,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 SkClipStack::Element* element = iter.prev();
+ const Element* element = iter.prev();
if (nullptr == element) {
initialTriState = InitialTriState::kAllIn;
break;
@@ -428,14 +362,19 @@ 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), kReverseDifference_SkClipOp, false,
- element->getGenID());
+ fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(),
+ kReverseDifference_SkClipOp, false);
} else {
- SkClipStack::Element* newElement = &fMaskElements.addToHead(*element)->fElement;
+ Element* newElement = fMaskElements.addToHead(*element);
if (newElement->isAA()) {
++numAAElements;
}
@@ -461,8 +400,8 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
fMaskElements.reset();
numAAElements = 0;
} else {
- SkClipStack::Element* element = &fMaskElements.headIter().get()->fElement;
- while (true) {
+ Element* element = fMaskElements.headIter().get();
+ while (element) {
bool skippable = false;
switch (element->getOp()) {
case kDifference_SkClipOp:
@@ -519,18 +458,14 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
SkDEBUGFAIL("Unexpected op.");
break;
}
- if (skippable) {
+ if (!skippable) {
+ break;
+ } else {
if (element->isAA()) {
--numAAElements;
}
fMaskElements.popHead();
- if (fMaskElements.count()) {
- element = &fMaskElements.headIter().get()->fElement;
- } else {
- break;
- }
- } else {
- break;
+ element = fMaskElements.headIter().get();
}
}
}
@@ -540,7 +475,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound
fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
}
-GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const SkClipStack::Element* element) {
+GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) {
SkIRect elementIBounds;
if (!element->isAA()) {
element->getBounds().round(&elementIBounds);
@@ -554,16 +489,21 @@ GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const SkClipStack::El
}
switch (element->getDeviceSpaceType()) {
- case SkClipStack::Element::DeviceSpaceType::kEmpty:
+ case Element::DeviceSpaceType::kEmpty:
return ClipResult::kMadeEmpty;
- case SkClipStack::Element::DeviceSpaceType::kRect:
+ case Element::DeviceSpaceType::kRect:
SkASSERT(element->getBounds() == element->getDeviceSpaceRect());
SkASSERT(!element->isInverseFilled());
if (element->isAA()) {
- if (fAAClipRectGenID == SK_InvalidGenID) { // No AA clip rect yet?
+ if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet?
fAAClipRect = element->getDeviceSpaceRect();
- fAAClipRectGenID = element->getGenID();
+ // 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);
} else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) {
this->makeEmpty();
return ClipResult::kMadeEmpty;
@@ -571,12 +511,12 @@ GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const SkClipStack::El
}
return ClipResult::kClipped;
- case SkClipStack::Element::DeviceSpaceType::kRRect:
+ case Element::DeviceSpaceType::kRRect:
SkASSERT(!element->isInverseFilled());
return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo,
GrAA(element->isAA()));
- case SkClipStack::Element::DeviceSpaceType::kPath:
+ case Element::DeviceSpaceType::kPath:
return this->addAnalyticFP(element->getDeviceSpacePath(),
Invert(element->isInverseFilled()), GrAA(element->isAA()));
}
@@ -585,12 +525,12 @@ GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const SkClipStack::El
return ClipResult::kNotClipped;
}
-GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const SkClipStack::Element* element) {
+GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) {
switch (element->getDeviceSpaceType()) {
- case SkClipStack::Element::DeviceSpaceType::kEmpty:
+ case Element::DeviceSpaceType::kEmpty:
return ClipResult::kMadeEmpty;
- case SkClipStack::Element::DeviceSpaceType::kRect:
+ case 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,
@@ -603,7 +543,7 @@ GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const SkClipStack::E
return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes,
GrAA(element->isAA()));
- case SkClipStack::Element::DeviceSpaceType::kRRect: {
+ case Element::DeviceSpaceType::kRRect: {
SkASSERT(!element->isInverseFilled());
const SkRRect& clipRRect = element->getDeviceSpaceRRect();
ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes,
@@ -643,7 +583,7 @@ GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const SkClipStack::E
return clipResult;
}
- case SkClipStack::Element::DeviceSpaceType::kPath:
+ case Element::DeviceSpaceType::kPath:
return this->addAnalyticFP(element->getDeviceSpacePath(),
Invert(!element->isInverseFilled()), GrAA(element->isAA()));
}
@@ -741,26 +681,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;
}
}
@@ -773,18 +713,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();
}
@@ -816,10 +756,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 SkClipStack::Element& element = iter.get()->fElement;
- SkRegion::Op op = (SkRegion::Op)element.getOp();
- GrAA aa = GrAA(element.isAA());
- bool invert = element.isInverseFilled();
+ const Element* element = iter.get();
+ 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.
@@ -868,7 +808,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, /*enable stencil clip test*/ true);
+ GrStencilClip stencilClip(fScissor, this->maskGenID());
if (!fWindowRects.empty()) {
stencilClip.fixedClip().setWindowRectangles(fWindowRects,
@@ -880,9 +820,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 SkClipStack::Element& element = iter.get()->fElement;
+ const Element* element = iter.get();
GrAAType aaType = GrAAType::kNone;
- if (element.isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) {
+ if (element->isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) {
aaType = GrAAType::kMSAA;
}
@@ -892,15 +832,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 (SkClipStack::Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) {
+ if (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();
@@ -944,10 +884,10 @@ bool GrReducedClip::drawStencilClipMask(GrContext* context,
GrUserStencilOp::kIncMaybeClamp,
0xffff>()
);
- if (SkClipStack::Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) {
+ if (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());
@@ -985,10 +925,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 (SkClipStack::Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) {
+ if (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 b8447555b0..fcdc66c843 100644
--- a/src/gpu/GrReducedClip.h
+++ b/src/gpu/GrReducedClip.h
@@ -23,22 +23,8 @@ class GrRenderTargetContext;
*/
class SK_API GrReducedClip {
public:
- /**
- * 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>;
+ using Element = SkClipStack::Element;
+ using ElementList = SkTLList<SkClipStack::Element, 16>;
GrReducedClip(const SkClipStack&, const SkRect& queryBounds, const GrCaps* caps,
int maxWindowRectangles = 0, int maxAnalyticFPs = 0, int maxCCPRClipPaths = 0);
@@ -79,13 +65,17 @@ public:
*/
const ElementList& maskElements() const { return fMaskElements; }
- const GrUniqueKey& maskUniqueKey() const;
-
/**
- * 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.
+ * 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.
*/
- uint32_t topMaskElementID() const;
+ uint32_t maskGenID() const { SkASSERT(!fMaskElements.isEmpty()); return fMaskGenID; }
/**
* Indicates whether antialiasing is required to process any of the mask elements.
@@ -110,8 +100,6 @@ public:
uint32_t opListID, int rtWidth,
int rtHeight);
- static constexpr char kMaskTestTag[] = "clip_mask";
-
private:
void walkStack(const SkClipStack&, const SkRect& queryBounds);
@@ -123,11 +111,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 SkClipStack::Element*);
+ ClipResult clipInsideElement(const 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 SkClipStack::Element*);
+ ClipResult clipOutsideElement(const Element*);
void addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA);
@@ -152,11 +140,11 @@ private:
SkIRect fScissor;
bool fHasScissor;
SkRect fAAClipRect;
- uint32_t fAAClipRectGenID; // top most GenID that contributed to fAAClipRect.
+ uint32_t fAAClipRectGenID; // GenID the mask will have if includes the AA clip rect.
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 94d52debcf..52f5497603 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -23,11 +23,24 @@ struct GrUserStencilSettings;
additional data members or virtual methods. */
class GrRenderTargetContextPriv {
public:
- const GrUniqueKey& lastStencilClipKey() const {
- return fRenderTargetContext->getRTOpList()->lastStencilClipKey();
+ // 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;
}
- void setLastStencilClipKey(const GrUniqueKey& key) {
- fRenderTargetContext->getRTOpList()->setLastStencilClipKey(key);
+
+ // 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;
}
using CanClearFullscreen = GrRenderTargetContext::CanClearFullscreen;
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index b3f31874d9..423b71ca10 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -30,6 +30,7 @@ GrRenderTargetOpList::GrRenderTargetOpList(GrResourceProvider* resourceProvider,
GrRenderTargetProxy* proxy,
GrAuditTrail* auditTrail)
: INHERITED(resourceProvider, std::move(opMemoryPool), proxy, auditTrail)
+ , fLastClipStackGenID(SK_InvalidUniqueID)
SkDEBUGCODE(, fNumClips(0)) {
}
@@ -201,7 +202,7 @@ bool GrRenderTargetOpList::onExecute(GrOpFlushState* flushState) {
}
void GrRenderTargetOpList::endFlush() {
- fLastStencilClipKey.reset();
+ fLastClipStackGenID = SK_InvalidUniqueID;
this->deleteOps();
fClipAllocator.reset();
INHERITED::endFlush();
diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h
index 0c7d983b29..8629a7cb1f 100644
--- a/src/gpu/GrRenderTargetOpList.h
+++ b/src/gpu/GrRenderTargetOpList.h
@@ -120,10 +120,9 @@ public:
SkDEBUGCODE(int numClips() const override { return fNumClips; })
SkDEBUGCODE(void visitProxies_debugOnly(const GrOp::VisitProxyFunc&) const;)
- const GrUniqueKey& lastStencilClipKey() const { return fLastStencilClipKey; }
- void setLastStencilClipKey(const GrUniqueKey& key) { fLastStencilClipKey = key; }
-
private:
+ friend class GrRenderTargetContextPriv; // for stencil clip state. TODO: this is invasive
+
void deleteOps();
struct RecordedOp {
@@ -173,7 +172,9 @@ private:
bool combineIfPossible(const RecordedOp& a, GrOp* b, const GrAppliedClip* bClip,
const DstProxy* bDstTexture, const GrCaps&);
- GrUniqueKey fLastStencilClipKey;
+ uint32_t fLastClipStackGenID;
+ SkIRect fLastDevClipBounds;
+ int fLastClipNumAnalyticFPs;
// 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 b0cd4e41ba..2bbf3b44de 100644
--- a/src/gpu/GrStencilClip.h
+++ b/src/gpu/GrStencilClip.h
@@ -44,7 +44,7 @@ public:
return false;
}
if (this->hasStencilClip()) {
- out->addStencilClip();
+ out->addStencilClip(fStencilStackID);
}
return true;
}
diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
index 1f048a9883..f395927b63 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(true);
+ GrStencilClip stencilClip(appliedClip.stencilStackID());
if (appliedClip.scissorState().enabled()) {
stencilClip.fixedClip().setScissor(appliedClip.scissorState().rect());
}
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 60a8626c57..498e60d45a 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -944,18 +944,6 @@ 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.
@@ -1055,35 +1043,41 @@ 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(stack, queryBounds, caps);
+ const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
- if (!reduced.maskElements().isEmpty() && !mask_allowed_no_top_most_gen_id(reduced)) {
- REPORTER_ASSERT(reporter, SK_InvalidGenID != reduced.topMaskElementID(),
- testCase.c_str());
- }
+ REPORTER_ASSERT(reporter,
+ reduced->maskElements().isEmpty() ||
+ SkClipStack::kInvalidGenID != reduced->maskGenID(),
+ 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()->fElement, &reducedStack);
+ for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
+ add_elem_to_stack(*iter.get(), &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);
@@ -1097,6 +1091,8 @@ 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();
}
}
@@ -1106,7 +1102,7 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
#define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
#endif
-static void test_reduced_clip_stack_mask_key(skiatest::Reporter* reporter) {
+static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
{
SkClipStack stack;
stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
@@ -1118,9 +1114,16 @@ static void test_reduced_clip_stack_mask_key(skiatest::Reporter* reporter) {
auto context = GrContext::MakeMock(nullptr);
const GrCaps* caps = context->contextPriv().caps();
- GrReducedClip reduced(stack, bounds, caps);
- REPORTER_ASSERT(reporter, reduced.maskElements().count() == 1);
- REPORTER_ASSERT(reporter, reduced.maskUniqueKey().isValid());
+ 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();
}
{
SkClipStack stack;
@@ -1159,7 +1162,7 @@ static void test_reduced_clip_stack_mask_key(skiatest::Reporter* reporter) {
static const struct SUPPRESS_VISIBILITY_WARNING {
SkRect testBounds;
int reducedClipCount;
- uint32_t topStackGenID;
+ uint32_t reducedGenID;
InitialState initialState;
SkIRect clipIRect;
// parameter.
@@ -1204,14 +1207,17 @@ static void test_reduced_clip_stack_mask_key(skiatest::Reporter* reporter) {
const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
testCases[i].reducedClipCount);
- 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());
+ 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);
}
-
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);
}
}
}
@@ -1468,7 +1474,7 @@ DEF_TEST(ClipStack, reporter) {
test_invfill_diff_bug(reporter);
test_reduced_clip_stack(reporter);
- test_reduced_clip_stack_mask_key(reporter);
+ test_reduced_clip_stack_genid(reporter);
test_reduced_clip_stack_no_aa_crash(reporter);
test_reduced_clip_stack_aa(reporter);
test_tiny_query_bounds_assertion_bug(reporter);
@@ -1493,7 +1499,7 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
path.addCircle(15, 15, 8);
path.setFillType(SkPath::kEvenOdd_FillType);
- static const char* kTag = GrReducedClip::kMaskTestTag;
+ static const char* kTag = GrClipStackClip::kMaskTestTag;
GrResourceCache* cache = context->contextPriv().getResourceCache();
static constexpr int kN = 5;