diff --git a/dm/DMGpuSupport.h b/dm/DMGpuSupport.h index b170a36d10..9f20dd51da 100644 --- a/dm/DMGpuSupport.h +++ b/dm/DMGpuSupport.h @@ -57,6 +57,7 @@ public: struct GrContextOptions { bool fImmediateMode; + bool fClipBatchToBounds; }; class GrContextFactory { diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index af62dafdd4..2861ae8ea2 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -806,12 +806,16 @@ int GPUSink::enclave() const { void PreAbandonGpuContextErrorHandler(SkError, void*) {} DEFINE_bool(imm, false, "Run gpu configs in immediate mode."); +DEFINE_bool(batchClip, false, "Clip each GrBatch to its device bounds for testing."); Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) const { GrContextOptions options; if (FLAGS_imm) { options.fImmediateMode = true; } + if (FLAGS_batchClip) { + options.fClipBatchToBounds = true; + } src.modifyGrContextOptions(&options); GrContextFactory factory(options); diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 7621f8b826..b492fe7353 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -406,7 +406,7 @@ private: bool init(GrBackend, GrBackendContext, const GrContextOptions& options); void initMockContext(); - void initCommon(); + void initCommon(const GrContextOptions&); /** * These functions create premul <-> unpremul effects if it is possible to generate a pair diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h index 1e5897b313..52173eb7a0 100644 --- a/include/gpu/GrContextOptions.h +++ b/include/gpu/GrContextOptions.h @@ -20,6 +20,7 @@ struct GrContextOptions { , fGeometryBufferMapThreshold(-1) , fUseDrawInsteadOfPartialRenderTargetWrite(false) , fImmediateMode(false) + , fClipBatchToBounds(false) , fUseShaderSwizzling(false) {} // EXPERIMENTAL @@ -48,10 +49,14 @@ struct GrContextOptions { /** some gpus have problems with partial writes of the rendertarget */ bool fUseDrawInsteadOfPartialRenderTargetWrite; - /** The GrContext operates in immedidate mode. It will issue all draws to the backend API + /** The GrContext operates in immediate mode. It will issue all draws to the backend API immediately. Intended to ease debugging. */ bool fImmediateMode; + /** For debugging purposes turn each GrBatch's bounds into a clip rect. This is used to + verify that the clip bounds are conservative. */ + bool fClipBatchToBounds; + /** Force us to do all swizzling manually in the shader and don't rely on extensions to do swizzling. */ bool fUseShaderSwizzling; diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index fa9d189e8c..303aeccc8a 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -126,9 +126,10 @@ GrPathRenderer* GrClipMaskManager::GetPathRenderer(GrContext* context, return pr; } -GrClipMaskManager::GrClipMaskManager(GrDrawTarget* drawTarget) +GrClipMaskManager::GrClipMaskManager(GrDrawTarget* drawTarget, bool debugClipBatchToBounds) : fDrawTarget(drawTarget) - , fClipMode(kIgnoreClip_StencilClipMode) { + , fClipMode(kIgnoreClip_StencilClipMode) + , fDebugClipBatchToBounds(debugClipBatchToBounds) { } GrContext* GrClipMaskManager::getContext() { @@ -272,6 +273,38 @@ bool GrClipMaskManager::getAnalyticClipProcessor(const GrReducedClip::ElementLis return !failed; } +static void add_rect_to_clip(const GrClip& clip, const SkRect& devRect, GrClip* out) { + switch (clip.clipType()) { + case GrClip::kClipStack_ClipType: { + SkClipStack* stack = new SkClipStack; + *stack = *clip.clipStack(); + // The stack is actually in clip space not device space. + SkRect clipRect = devRect; + SkPoint origin = { SkIntToScalar(clip.origin().fX), SkIntToScalar(clip.origin().fY) }; + clipRect.offset(origin); + SkIRect iclipRect; + clipRect.roundOut(&iclipRect); + clipRect = SkRect::Make(iclipRect); + stack->clipDevRect(clipRect, SkRegion::kIntersect_Op, false); + out->setClipStack(stack, &clip.origin()); + break; + } + case GrClip::kWideOpen_ClipType: + *out = GrClip(devRect); + break; + case GrClip::kIRect_ClipType: { + SkIRect intersect; + devRect.roundOut(&intersect); + if (intersect.intersect(clip.irect())) { + *out = GrClip(intersect); + } else { + *out = clip; + } + break; + } + } +} + //////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software @@ -294,7 +327,13 @@ bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder, SkASSERT(rt); SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height()); - const GrClip& clip = pipelineBuilder.clip(); + GrClip devBoundsClip; + bool doDevBoundsClip = fDebugClipBatchToBounds && devBounds; + if (doDevBoundsClip) { + add_rect_to_clip(pipelineBuilder.clip(), *devBounds, &devBoundsClip); + } + const GrClip& clip = doDevBoundsClip ? devBoundsClip : pipelineBuilder.clip(); + if (clip.isWideOpen(clipSpaceRTIBounds)) { this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h index 98fd3af351..6b9b727161 100644 --- a/src/gpu/GrClipMaskManager.h +++ b/src/gpu/GrClipMaskManager.h @@ -55,7 +55,7 @@ private: */ class GrClipMaskManager : SkNoncopyable { public: - GrClipMaskManager(GrDrawTarget* owner); + GrClipMaskManager(GrDrawTarget* owner, bool debugClipBatchToBounds); /** * Creates a clip mask if necessary as a stencil buffer or alpha texture @@ -171,6 +171,7 @@ private: GrDrawTarget* fDrawTarget; // This is our owning draw target. StencilClipMode fClipMode; + bool fDebugClipBatchToBounds; typedef SkNoncopyable INHERITED; }; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 920e3c4bbd..ad89672f3b 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -72,11 +72,11 @@ bool GrContext::init(GrBackend backend, GrBackendContext backendContext, if (!fGpu) { return false; } - this->initCommon(); + this->initCommon(options); return true; } -void GrContext::initCommon() { +void GrContext::initCommon(const GrContextOptions& options) { fCaps = SkRef(fGpu->caps()); fResourceCache = new GrResourceCache(fCaps); fResourceCache->setOverBudgetCallback(OverBudgetCB, this); @@ -86,7 +86,9 @@ void GrContext::initCommon() { fDidTestPMConversions = false; - fDrawingManager.reset(new GrDrawingManager(this)); + GrDrawTarget::Options dtOptions; + dtOptions.fClipBatchToBounds = options.fClipBatchToBounds; + fDrawingManager.reset(new GrDrawingManager(this, dtOptions)); // GrBatchFontCache will eventually replace GrFontCache fBatchFontCache = new GrBatchFontCache(this); diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index f34e6fc956..e41a1b72a8 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -32,7 +32,8 @@ //////////////////////////////////////////////////////////////////////////////// -GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* resourceProvider) +GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* resourceProvider, + const Options& options) : fGpu(SkRef(gpu)) , fResourceProvider(resourceProvider) , fFlushing(false) @@ -40,7 +41,7 @@ GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* r , fRenderTarget(rt) { // TODO: Stop extracting the context (currently needed by GrClipMaskManager) fContext = fGpu->getContext(); - fClipMaskManager.reset(new GrClipMaskManager(this)); + fClipMaskManager.reset(new GrClipMaskManager(this, options.fClipBatchToBounds)); rt->setLastDrawTarget(this); diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index 240731e81b..7b0159e5d5 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -43,9 +43,13 @@ class GrPathRangeDraw; class GrDrawTarget final : public SkRefCnt { public: - // The context may not be fully constructed and should not be used during GrDrawTarget - // construction. - GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider*); + /** Options for GrDrawTarget behavior. */ + struct Options { + Options () : fClipBatchToBounds(false) {} + bool fClipBatchToBounds; + }; + + GrDrawTarget(GrRenderTarget*, GrGpu*, GrResourceProvider*, const Options&); ~GrDrawTarget() override; diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp index 73b7d1695e..13167d4642 100644 --- a/src/gpu/GrDrawingManager.cpp +++ b/src/gpu/GrDrawingManager.cpp @@ -152,7 +152,8 @@ GrDrawTarget* GrDrawingManager::newDrawTarget(GrRenderTarget* rt) { } #endif - GrDrawTarget* dt = new GrDrawTarget(rt, fContext->getGpu(), fContext->resourceProvider()); + GrDrawTarget* dt = new GrDrawTarget(rt, fContext->getGpu(), fContext->resourceProvider(), + fOptionsForDrawTargets); *fDrawTargets.append() = dt; diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h index 41d8ab5904..672f7b08da 100644 --- a/src/gpu/GrDrawingManager.h +++ b/src/gpu/GrDrawingManager.h @@ -53,8 +53,9 @@ public: static bool ProgramUnitTest(GrContext* context, int maxStages); private: - GrDrawingManager(GrContext* context) + GrDrawingManager(GrContext* context, const GrDrawTarget::Options& optionsForDrawTargets) : fContext(context) + , fOptionsForDrawTargets(optionsForDrawTargets) , fAbandoned(false) , fNVPRTextContext(nullptr) , fPathRendererChain(nullptr) @@ -74,6 +75,7 @@ private: static const int kNumDFTOptions = 2; // DFT or no DFT GrContext* fContext; + GrDrawTarget::Options fOptionsForDrawTargets; bool fAbandoned; SkTDArray fDrawTargets; diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp index f0f6370254..b0d92fec30 100644 --- a/src/gpu/GrTest.cpp +++ b/src/gpu/GrTest.cpp @@ -318,7 +318,7 @@ void GrContext::initMockContext() { SkASSERT(nullptr == fGpu); fGpu = new MockGpu(this, options); SkASSERT(fGpu); - this->initCommon(); + this->initCommon(options); // We delete these because we want to test the cache starting with zero resources. Also, none of // these objects are required for any of tests that use this context. TODO: make stop allocating