Revert "Add genIDs from all contributing elements to GrReducedClip's mask key."

This reverts commit 1354048c8f.

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>
This commit is contained in:
Brian Salomon 2018-07-09 18:23:58 +00:00 committed by Skia Commit-Bot
parent d48897b576
commit c3833b4c15
11 changed files with 215 additions and 244 deletions

View File

@ -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 {

View File

@ -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;
}

View File

@ -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);

View File

@ -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,86 +113,20 @@ 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;
fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
}
fMaskRequiresAA = true;
fMaskGenID = fAAClipRectGenID;
}
}
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;
}
}
#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) {
// walk backwards until we get to:
// a) the beginning
@ -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;

View File

@ -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.
};

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -44,7 +44,7 @@ public:
return false;
}
if (this->hasStencilClip()) {
out->addStencilClip();
out->addStencilClip(fStencilStackID);
}
return true;
}

View File

@ -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());
}

View File

@ -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;