From 584a79a1d5396cccd159ec6b8660be8fe128334f Mon Sep 17 00:00:00 2001 From: Chris Dalton Date: Wed, 15 Nov 2017 13:14:01 -0700 Subject: [PATCH] Reland "Fold analytic clip FPs into GrReducedClip" This is a reland of 4355b26b359d5f2597a10012e7eb11fb3a1f025b Original change's description: > Fold analytic clip FPs into GrReducedClip > > Perf result on Pixel phone (sorted by impact): > > GEOMEAN 7.44 -> 6.92 ms [ 93%] > > keymobi_cnn_com.skp 3.55 -> 3.59 ms [101%] > keymobi_theverge_com.skp 4.08 -> 4.13 ms [101%] > desk_skbug6850autoscroll.skp 1.21 -> 1.22 ms [101%] > ... > top25desk_weather_com.skp 14.2 -> 11.5 ms [ 81%] > keymobi_androidpolice_com_2012_.skp 3.84 -> 2.95 ms [ 77%] > keymobi_shop_mobileweb_ebay_com.skp 2.94 -> 2.26 ms [ 77%] > keymobi_boingboing_net.skp 3.08 -> 2.24 ms [ 73%] > desk_jsfiddlebigcar.skp 1.90 -> 1.25 ms [ 66%] > keymobi_m_youtube_com_watch_v_9.skp 13.5 -> 8.84 ms [ 65%] > keymobi_sfgate_com_.skp 8.55 -> 5.55 ms [ 65%] > keymobi_blogger.skp 4.01 -> 2.60 ms [ 65%] > > Cleaner code, improved skps, slightly better geometric mean time. > > Pixel C is mostly unaffected, presumably because it uses window > rectangles. > > Bug: skia:7190 > Change-Id: Ia93f68b2f971ea66b3ab19dc73557ea602932a92 > Reviewed-on: https://skia-review.googlesource.com/67424 > Commit-Queue: Chris Dalton > Reviewed-by: Brian Salomon TBR=bsalomon@google.com Bug: skia:7190 Change-Id: I92f1ed21b6292feb3209fcbd1725487784d420da Reviewed-on: https://skia-review.googlesource.com/72562 Reviewed-by: Chris Dalton Commit-Queue: Chris Dalton --- include/gpu/GrCaps.h | 5 + src/gpu/GrCaps.cpp | 9 ++ src/gpu/GrClipStackClip.cpp | 152 +++++++--------------------- src/gpu/GrReducedClip.cpp | 111 ++++++++++++++------ src/gpu/GrReducedClip.h | 46 ++++++--- src/gpu/GrRenderTargetContextPriv.h | 10 +- src/gpu/GrRenderTargetOpList.h | 1 + src/gpu/gl/GrGLCaps.cpp | 7 ++ 8 files changed, 177 insertions(+), 164 deletions(-) diff --git a/include/gpu/GrCaps.h b/include/gpu/GrCaps.h index 4da73c995c..b19281b00d 100644 --- a/include/gpu/GrCaps.h +++ b/include/gpu/GrCaps.h @@ -143,6 +143,10 @@ public: int maxWindowRectangles() const { return fMaxWindowRectangles; } + // A tuned, platform-specific value for the maximum number of analytic fragment processors we + // should use to implement a clip, before falling back on a mask. + int maxClipAnalyticFPs() const { return fMaxClipAnalyticFPs; } + virtual bool isConfigTexturable(GrPixelConfig) const = 0; virtual bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const = 0; // Returns whether a texture of the given config can be copied to a texture of the same config. @@ -241,6 +245,7 @@ protected: int fMaxStencilSampleCount; int fMaxRasterSamples; int fMaxWindowRectangles; + int fMaxClipAnalyticFPs; private: virtual void onApplyOptionsOverrides(const GrContextOptions&) {} diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp index 2e25845876..a4a78ad744 100644 --- a/src/gpu/GrCaps.cpp +++ b/src/gpu/GrCaps.cpp @@ -69,6 +69,14 @@ GrCaps::GrCaps(const GrContextOptions& options) { fMaxRasterSamples = 0; fMaxWindowRectangles = 0; + // An default count of 4 was chosen because of the common pattern in Blink of: + // isect RR + // diff RR + // isect convex_poly + // isect convex_poly + // when drawing rounded div borders. + fMaxClipAnalyticFPs = 4; + fSuppressPrints = options.fSuppressPrints; #if GR_TEST_UTILS fWireframeMode = options.fWireframeMode; @@ -161,6 +169,7 @@ void GrCaps::dumpJSON(SkJSONWriter* writer) const { writer->appendS32("Max Stencil Sample Count", fMaxStencilSampleCount); writer->appendS32("Max Raster Samples", fMaxRasterSamples); writer->appendS32("Max Window Rectangles", fMaxWindowRectangles); + writer->appendS32("Max Clip Analytic Fragment Processors", fMaxClipAnalyticFPs); static const char* kInstancedSupportNames[] = { "None", diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp index c0fe10678b..52fab72476 100644 --- a/src/gpu/GrClipStackClip.cpp +++ b/src/gpu/GrClipStackClip.cpp @@ -30,7 +30,6 @@ typedef SkClipStack::Element Element; typedef GrReducedClip::InitialState InitialState; typedef GrReducedClip::ElementList ElementList; -static const int kMaxAnalyticElements = 4; const char GrClipStackClip::kMaskTestTag[] = "clip_mask"; bool GrClipStackClip::quickContains(const SkRect& rect) const { @@ -176,80 +175,6 @@ bool GrClipStackClip::UseSWOnlyPath(GrContext* context, return false; } -static bool get_analytic_clip_processor(const ElementList& elements, - bool abortIfAA, - const SkRect& drawDevBounds, - std::unique_ptr* resultFP) { - SkASSERT(elements.count() <= kMaxAnalyticElements); - SkSTArray> fps; - ElementList::Iter iter(elements); - while (iter.get()) { - SkClipOp op = iter.get()->getOp(); - bool invert; - bool skip = false; - switch (op) { - case kReplace_SkClipOp: - SkASSERT(iter.get() == elements.head()); - // Fallthrough, handled same as intersect. - case kIntersect_SkClipOp: - invert = false; - if (iter.get()->contains(drawDevBounds)) { - skip = true; - } - break; - case kDifference_SkClipOp: - invert = true; - // We don't currently have a cheap test for whether a rect is fully outside an - // element's primitive, so don't attempt to set skip. - break; - default: - return false; - } - if (!skip) { - GrClipEdgeType edgeType; - if (iter.get()->isAA()) { - if (abortIfAA) { - return false; - } - edgeType = - invert ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kFillAA; - } else { - edgeType = - invert ? GrClipEdgeType::kInverseFillBW : GrClipEdgeType::kFillBW; - } - - switch (iter.get()->getDeviceSpaceType()) { - case SkClipStack::Element::DeviceSpaceType::kPath: - fps.emplace_back( - GrConvexPolyEffect::Make(edgeType, iter.get()->getDeviceSpacePath())); - break; - case SkClipStack::Element::DeviceSpaceType::kRRect: { - fps.emplace_back( - GrRRectEffect::Make(edgeType, iter.get()->getDeviceSpaceRRect())); - break; - } - case SkClipStack::Element::DeviceSpaceType::kRect: { - fps.emplace_back( - GrConvexPolyEffect::Make(edgeType, iter.get()->getDeviceSpaceRect())); - break; - } - default: - break; - } - if (!fps.back()) { - return false; - } - } - iter.next(); - } - - *resultFP = nullptr; - if (fps.count()) { - *resultFP = GrFragmentProcessor::RunInSeries(fps.begin(), fps.count()); - } - return true; -} - //////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software @@ -265,8 +190,19 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar return true; } - const GrReducedClip reducedClip(*fStack, devBounds, - renderTargetContext->priv().maxWindowRectangles()); + int maxAnalyticFPs = context->caps()->maxClipAnalyticFPs(); + if (GrFSAAType::kNone != renderTargetContext->fsaaType()) { + // With mixed samples (non-msaa color buffer), any coverage info is lost from color once it + // hits the color buffer anyway, so we may as well use coverage AA if nothing else in the + // pipe is multisampled. + if (renderTargetContext->numColorSamples() > 0 || useHWAA || hasUserStencilSettings) { + maxAnalyticFPs = 0; + } + SkASSERT(!context->caps()->avoidStencilBuffers()); // We disable MSAA when avoiding stencil. + } + + GrReducedClip reducedClip(*fStack, devBounds, renderTargetContext->priv().maxWindowRectangles(), + maxAnalyticFPs); if (reducedClip.hasScissor() && !GrClip::IsInsideClip(reducedClip.scissor(), devBounds)) { out->hardClip().addScissor(reducedClip.scissor(), bounds); @@ -277,6 +213,10 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar GrWindowRectsState::Mode::kExclusive); } + if (std::unique_ptr clipFPs = reducedClip.detachAnalyticFPs()) { + out->addCoverageFP(std::move(clipFPs)); + } + if (reducedClip.maskElements().isEmpty()) { return InitialState::kAllIn == reducedClip.initialState(); } @@ -289,41 +229,9 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar SkASSERT(rtIBounds.contains(scissor)); // Mask shouldn't be larger than the RT. #endif - bool avoidStencilBuffers = context->caps()->avoidStencilBuffers(); - - // An element count of 4 was chosen because of the common pattern in Blink of: - // isect RR - // diff RR - // isect convex_poly - // isect convex_poly - // when drawing rounded div borders. This could probably be tuned based on a - // configuration's relative costs of switching RTs to generate a mask vs - // longer shaders. - if (reducedClip.maskElements().count() <= kMaxAnalyticElements) { - // When there are multiple samples we want to do per-sample clipping, not compute a - // fractional pixel coverage. - bool disallowAnalyticAA = - GrFSAAType::kNone != renderTargetContext->fsaaType() && !avoidStencilBuffers; - if (disallowAnalyticAA && !renderTargetContext->numColorSamples()) { - // With a single color sample, any coverage info is lost from color once it hits the - // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe - // is multisampled. - disallowAnalyticAA = useHWAA || hasUserStencilSettings; - } - std::unique_ptr clipFP; - if ((reducedClip.maskRequiresAA() || avoidStencilBuffers) && - get_analytic_clip_processor(reducedClip.maskElements(), disallowAnalyticAA, devBounds, - &clipFP)) { - if (clipFP) { - out->addCoverageFP(std::move(clipFP)); - } - return true; - } - } - // If the stencil buffer is multisampled we can use it to do everything. if ((GrFSAAType::kNone == renderTargetContext->fsaaType() && reducedClip.maskRequiresAA()) || - avoidStencilBuffers) { + context->caps()->avoidStencilBuffers()) { sk_sp result; if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) { // The clip geometry is complex enough that it will be more efficient to create it @@ -343,7 +251,8 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar // If alpha or software clip mask creation fails, fall through to the stencil code paths, // unless stencils are disallowed. if (context->caps()->avoidStencilBuffers()) { - SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. Clip will be ignored.\n"); + SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. " + "Clip will be ignored.\n"); return false; } } @@ -353,11 +262,14 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar // 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())) { + if (renderTargetContext->priv().mustRenderClip(reducedClip.maskGenID(), reducedClip.scissor(), + reducedClip.numAnalyticFPs())) { reducedClip.drawStencilClipMask(context, renderTargetContext); - renderTargetContext->priv().setLastClip(reducedClip.maskGenID(), reducedClip.scissor()); + renderTargetContext->priv().setLastClip(reducedClip.maskGenID(), reducedClip.scissor(), + reducedClip.numAnalyticFPs()); } + // 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; } @@ -365,14 +277,16 @@ bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTar //////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha -static void create_clip_mask_key(uint32_t clipGenID, const SkIRect& bounds, GrUniqueKey* key) { +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, 3, GrClipStackClip::kMaskTestTag); + 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, @@ -393,7 +307,8 @@ sk_sp GrClipStackClip::createAlphaClipMask(GrContext* context, const GrReducedClip& reducedClip) const { GrResourceProvider* resourceProvider = context->resourceProvider(); GrUniqueKey key; - create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), &key); + create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), + reducedClip.numAnalyticFPs(), &key); sk_sp proxy(resourceProvider->findOrCreateProxyByUniqueKey( key, kBottomLeft_GrSurfaceOrigin)); @@ -505,7 +420,8 @@ sk_sp GrClipStackClip::createSoftwareClipMask( GrContext* context, const GrReducedClip& reducedClip, GrRenderTargetContext* renderTargetContext) const { GrUniqueKey key; - create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), &key); + create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), + reducedClip.numAnalyticFPs(), &key); sk_sp proxy(context->resourceProvider()->findOrCreateProxyByUniqueKey( key, kTopLeft_GrSurfaceOrigin)); diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp index d37c83f11a..992411a4a2 100644 --- a/src/gpu/GrReducedClip.cpp +++ b/src/gpu/GrReducedClip.cpp @@ -21,6 +21,8 @@ #include "GrStyle.h" #include "GrUserStencilSettings.h" #include "SkClipOpPriv.h" +#include "effects/GrConvexPolyEffect.h" +#include "effects/GrRRectEffect.h" /** * There are plenty of optimizations that could be added here. Maybe flips could be folded into @@ -30,8 +32,11 @@ * take a rect in case the caller knows a bound on what is to be drawn through this clip. */ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds, - int maxWindowRectangles) { + int maxWindowRectangles, int maxAnalyticFPs) + : fMaxWindowRectangles(maxWindowRectangles) + , fMaxAnalyticFPs(maxAnalyticFPs) { SkASSERT(!queryBounds.isEmpty()); + SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows); fHasScissor = false; fAAClipRectGenID = SK_InvalidGenID; @@ -97,12 +102,13 @@ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds } fHasScissor = true; - // Now that we have determined the bounds to use and filtered out the trivial cases, call the - // helper that actually walks the stack. - this->walkStack(stack, tighterQuery, maxWindowRectangles); + // Now that we have determined the bounds to use and filtered out the trivial cases, call + // the helper that actually walks the stack. + this->walkStack(stack, tighterQuery); } - if (SK_InvalidGenID != fAAClipRectGenID) { // Is there an AA clip rect? + if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect? + ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, true)) { if (fMaskElements.isEmpty()) { // Use a replace since it is faster than intersect. fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/); @@ -112,12 +118,10 @@ GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds } fMaskRequiresAA = true; fMaskGenID = fAAClipRectGenID; - fAAClipRectGenID = SK_InvalidGenID; } } -void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds, - int maxWindowRectangles) { +void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) { // walk backwards until we get to: // a) the beginning // b) an operation that is known to make the bounds all inside/outside @@ -180,7 +184,7 @@ void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBound } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) { skippable = true; } else if (!embiggens) { - ClipResult result = this->clipOutsideElement(element, maxWindowRectangles); + ClipResult result = this->clipOutsideElement(element); if (ClipResult::kMadeEmpty == result) { return; } @@ -480,34 +484,43 @@ GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* elemen return ClipResult::kClipped; case Element::DeviceSpaceType::kRRect: + return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo, + element->isAA()); + case Element::DeviceSpaceType::kPath: - return ClipResult::kNotClipped; + return this->addAnalyticFP(element->getDeviceSpacePath(), Invert::kNo, element->isAA()); } SK_ABORT("Unexpected DeviceSpaceType"); return ClipResult::kNotClipped; } -GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element, - int maxWindowRectangles) { - if (fWindowRects.count() >= maxWindowRectangles) { - return ClipResult::kNotClipped; - } - +GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) { switch (element->getDeviceSpaceType()) { case Element::DeviceSpaceType::kEmpty: return ClipResult::kMadeEmpty; case Element::DeviceSpaceType::kRect: - // Clip out the inside of every rect. We won't be able to entirely skip the AA ones, but - // it saves processing time. - this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA()); - return !element->isAA() ? ClipResult::kClipped : ClipResult::kNotClipped; + if (fWindowRects.count() < fMaxWindowRectangles) { + // Clip out the inside of every rect. We won't be able to entirely skip the AA ones, + // but it saves processing time. + this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA()); + if (!element->isAA()) { + return ClipResult::kClipped; + } + } + return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes, + element->isAA()); case Element::DeviceSpaceType::kRRect: { - // Clip out the interiors of round rects with two window rectangles in the shape of a - // plus. It doesn't allow us to skip the clip element, but still saves processing time. const SkRRect& clipRRect = element->getDeviceSpaceRRect(); + ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes, element->isAA()); + if (fWindowRects.count() >= fMaxWindowRectangles) { + return clipResult; + } + + // Clip out the interiors of round rects with two window rectangles in the shape of a + // "plus". This doesn't let us skip the clip element, but still saves processing time. SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner); SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner); if (SkRRect::kComplex_Type == clipRRect.getType()) { @@ -521,24 +534,25 @@ GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* eleme const SkRect& bounds = clipRRect.getBounds(); if (insetTL.x() + insetBR.x() >= bounds.width() || insetTL.y() + insetBR.y() >= bounds.height()) { - return ClipResult::kNotClipped; // The interior "plus" is empty. + return clipResult; // The interior "plus" is empty. } SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(), bounds.right(), bounds.bottom() - insetBR.y()); this->addWindowRectangle(horzRect, element->isAA()); - if (fWindowRects.count() >= maxWindowRectangles) { - return ClipResult::kNotClipped; + + if (fWindowRects.count() < fMaxWindowRectangles) { + SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(), + bounds.right() - insetBR.x(), bounds.bottom()); + this->addWindowRectangle(vertRect, element->isAA()); } - SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(), - bounds.right() - insetBR.x(), bounds.bottom()); - this->addWindowRectangle(vertRect, element->isAA()); - return ClipResult::kNotClipped; + return clipResult; } case Element::DeviceSpaceType::kPath: - return ClipResult::kNotClipped; + return this->addAnalyticFP(element->getDeviceSpacePath(), Invert::kYes, + element->isAA()); } SK_ABORT("Unexpected DeviceSpaceType"); @@ -557,6 +571,43 @@ inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, } } +template +inline GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const T& deviceSpaceShape, + Invert invert, bool aa) { + if (fAnalyticFPs.count() >= fMaxAnalyticFPs) { + return ClipResult::kNotClipped; + } + + GrClipEdgeType edgeType; + if (Invert::kNo == invert) { + edgeType = aa ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW; + } else { + edgeType = aa ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW; + } + + if (auto fp = make_analytic_clip_fp(edgeType, deviceSpaceShape)) { + fAnalyticFPs.push_back(std::move(fp)); + return ClipResult::kClipped; + } + + return ClipResult::kNotClipped; +} + +std::unique_ptr make_analytic_clip_fp(GrClipEdgeType edgeType, + const SkRect& deviceSpaceRect) { + return GrConvexPolyEffect::Make(edgeType, deviceSpaceRect); +} + +std::unique_ptr make_analytic_clip_fp(GrClipEdgeType edgeType, + const SkRRect& deviceSpaceRRect) { + return GrRRectEffect::Make(edgeType, deviceSpaceRRect); +} + +std::unique_ptr make_analytic_clip_fp(GrClipEdgeType edgeType, + const SkPath& deviceSpacePath) { + return GrConvexPolyEffect::Make(edgeType, deviceSpacePath); +} + void GrReducedClip::makeEmpty() { fHasScissor = false; fAAClipRectGenID = SK_InvalidGenID; diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h index 0746439ba3..3ad2ab9971 100644 --- a/src/gpu/GrReducedClip.h +++ b/src/gpu/GrReducedClip.h @@ -8,6 +8,7 @@ #ifndef GrReducedClip_DEFINED #define GrReducedClip_DEFINED +#include "GrFragmentProcessor.h" #include "GrWindowRectangles.h" #include "SkClipStack.h" #include "SkTLList.h" @@ -24,7 +25,8 @@ public: using Element = SkClipStack::Element; using ElementList = SkTLList; - GrReducedClip(const SkClipStack&, const SkRect& queryBounds, int maxWindowRectangles = 0); + GrReducedClip(const SkClipStack&, const SkRect& queryBounds, + int maxWindowRectangles = 0, int maxAnalyticFPs = 0); /** * If hasScissor() is true, the clip mask is not valid outside this rect and the caller must @@ -48,6 +50,13 @@ public: */ const GrWindowRectangles& windowRectangles() const { return fWindowRects; } + int numAnalyticFPs() const { return fAnalyticFPs.count(); } + + std::unique_ptr detachAnalyticFPs() { + SkDEBUGCODE(for (const auto& fp : fAnalyticFPs) { SkASSERT(fp); }) + return GrFragmentProcessor::RunInSeries(fAnalyticFPs.begin(), fAnalyticFPs.count()); + } + /** * An ordered list of clip elements that could not be skipped or implemented by other means. If * nonempty, the caller must create an alpha and/or stencil mask for these elements and apply it @@ -81,7 +90,7 @@ public: bool drawStencilClipMask(GrContext*, GrRenderTargetContext*) const; private: - void walkStack(const SkClipStack&, const SkRect& queryBounds, int maxWindowRectangles); + void walkStack(const SkClipStack&, const SkRect& queryBounds); enum class ClipResult { kNotClipped, @@ -91,24 +100,35 @@ private: // Clips the the given element's interior out of the final clip. // NOTE: do not call for elements followed by ops that can grow the clip. - ClipResult clipInsideElement(const Element* element); + ClipResult clipInsideElement(const Element*); // Clips the the given element's exterior out of the final clip. // NOTE: do not call for elements followed by ops that can grow the clip. - ClipResult clipOutsideElement(const Element* element, int maxWindowRectangles); + ClipResult clipOutsideElement(const Element*); void addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA); + + enum class Invert : bool { + kNo, + kYes + }; + + template ClipResult addAnalyticFP(const T& deviceSpaceShape, Invert, bool aa); + void makeEmpty(); - SkIRect fScissor; - bool fHasScissor; - SkRect fAAClipRect; - uint32_t fAAClipRectGenID; // GenID the mask will have if includes the AA clip rect. - GrWindowRectangles fWindowRects; - ElementList fMaskElements; - uint32_t fMaskGenID; - bool fMaskRequiresAA; - InitialState fInitialState; + const int fMaxWindowRectangles; + const int fMaxAnalyticFPs; + SkIRect fScissor; + bool fHasScissor; + SkRect fAAClipRect; + uint32_t fAAClipRectGenID; // GenID the mask will have if includes the AA clip rect. + GrWindowRectangles fWindowRects; + SkSTArray<4, std::unique_ptr> fAnalyticFPs; + ElementList fMaskElements; + uint32_t fMaskGenID; + bool fMaskRequiresAA; + InitialState fInitialState; }; #endif diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h index 874e9c9f00..0d73793c9d 100644 --- a/src/gpu/GrRenderTargetContextPriv.h +++ b/src/gpu/GrRenderTargetContextPriv.h @@ -29,18 +29,22 @@ 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) { + void setLastClip(uint32_t clipStackGenID, const SkIRect& devClipBounds, + int numClipAnalyticFPs) { GrRenderTargetOpList* opList = fRenderTargetContext->getRTOpList(); opList->fLastClipStackGenID = clipStackGenID; opList->fLastDevClipBounds = devClipBounds; + opList->fLastClipNumAnalyticFPs = numClipAnalyticFPs; } // 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) const { + bool mustRenderClip(uint32_t clipStackGenID, const SkIRect& devClipBounds, + int numClipAnalyticFPs) const { GrRenderTargetOpList* opList = fRenderTargetContext->getRTOpList(); return opList->fLastClipStackGenID != clipStackGenID || - !opList->fLastDevClipBounds.contains(devClipBounds); + !opList->fLastDevClipBounds.contains(devClipBounds) || + opList->fLastClipNumAnalyticFPs != numClipAnalyticFPs; } void clear(const GrFixedClip&, const GrColor, bool canIgnoreClip); diff --git a/src/gpu/GrRenderTargetOpList.h b/src/gpu/GrRenderTargetOpList.h index 24125acba6..e9797db8f5 100644 --- a/src/gpu/GrRenderTargetOpList.h +++ b/src/gpu/GrRenderTargetOpList.h @@ -156,6 +156,7 @@ private: uint32_t fLastClipStackGenID; SkIRect fLastDevClipBounds; + int fLastClipNumAnalyticFPs; // For ops/opList we have mean: 5 stdDev: 28 SkSTArray<5, RecordedOp, true> fRecordedOps; diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index 0c185e4a78..172c220605 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -527,6 +527,13 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, GR_GL_GetIntegerv(gli, GR_GL_MAX_WINDOW_RECTANGLES, &fMaxWindowRectangles); } + if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) { + // Temporarily disabling clip analytic fragments processors on Nexus player while we work + // around a driver bug related to gl_FragCoord. + // https://bugs.chromium.org/p/skia/issues/detail?id=7286 + fMaxClipAnalyticFPs = 0; + } + #ifndef SK_BUILD_FOR_IOS if (kPowerVR54x_GrGLRenderer == ctxInfo.renderer() || kPowerVRRogue_GrGLRenderer == ctxInfo.renderer() ||