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>
This commit is contained in:
Brian Salomon 2018-07-09 10:31:47 -04:00 committed by Skia Commit-Bot
parent 373224c9ab
commit 1354048c8f
11 changed files with 244 additions and 215 deletions

View File

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

View File

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

View File

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

View File

@ -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,20 +113,86 @@ 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;
}
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
@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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