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:
parent
373224c9ab
commit
1354048c8f
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
if (this->hasStencilClip()) {
|
||||
out->addStencilClip(fStencilStackID);
|
||||
out->addStencilClip();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user