1 
2 /*
3  * Copyright 2010 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include "GrDrawTarget.h"
10 
11 #include "GrAuditTrail.h"
12 #include "GrCaps.h"
13 #include "GrGpu.h"
14 #include "GrPath.h"
15 #include "GrPipeline.h"
16 #include "GrMemoryPool.h"
17 #include "GrRenderTarget.h"
18 #include "GrResourceProvider.h"
19 #include "GrRenderTargetPriv.h"
20 #include "GrSurfacePriv.h"
21 #include "GrTexture.h"
22 #include "GrVertexBuffer.h"
23 #include "gl/GrGLRenderTarget.h"
24 
25 #include "SkStrokeRec.h"
26 
27 #include "batches/GrClearBatch.h"
28 #include "batches/GrCopySurfaceBatch.h"
29 #include "batches/GrDiscardBatch.h"
30 #include "batches/GrDrawBatch.h"
31 #include "batches/GrDrawPathBatch.h"
32 #include "batches/GrRectBatchFactory.h"
33 #include "batches/GrStencilPathBatch.h"
34 
35 ////////////////////////////////////////////////////////////////////////////////
36 
37 // Experimentally we have found that most batching occurs within the first 10 comparisons.
38 static const int kDefaultMaxBatchLookback = 10;
39 
GrDrawTarget(GrRenderTarget * rt,GrGpu * gpu,GrResourceProvider * resourceProvider,GrAuditTrail * auditTrail,const Options & options)40 GrDrawTarget::GrDrawTarget(GrRenderTarget* rt, GrGpu* gpu, GrResourceProvider* resourceProvider,
41                            GrAuditTrail* auditTrail, const Options& options)
42     : fGpu(SkRef(gpu))
43     , fResourceProvider(resourceProvider)
44     , fAuditTrail(auditTrail)
45     , fFlags(0)
46     , fRenderTarget(rt) {
47     // TODO: Stop extracting the context (currently needed by GrClipMaskManager)
48     fContext = fGpu->getContext();
49     fClipMaskManager.reset(new GrClipMaskManager(this, options.fClipBatchToBounds));
50 
51     fDrawBatchBounds = options.fDrawBatchBounds;
52     fMaxBatchLookback = (options.fMaxBatchLookback < 0) ? kDefaultMaxBatchLookback :
53                                                           options.fMaxBatchLookback;
54 
55     rt->setLastDrawTarget(this);
56 
57 #ifdef SK_DEBUG
58     static int debugID = 0;
59     fDebugID = debugID++;
60 #endif
61 }
62 
~GrDrawTarget()63 GrDrawTarget::~GrDrawTarget() {
64     if (fRenderTarget && this == fRenderTarget->getLastDrawTarget()) {
65         fRenderTarget->setLastDrawTarget(nullptr);
66     }
67 
68     fGpu->unref();
69 }
70 
71 ////////////////////////////////////////////////////////////////////////////////
72 
73 // Add a GrDrawTarget-based dependency
addDependency(GrDrawTarget * dependedOn)74 void GrDrawTarget::addDependency(GrDrawTarget* dependedOn) {
75     SkASSERT(!dependedOn->dependsOn(this));  // loops are bad
76 
77     if (this->dependsOn(dependedOn)) {
78         return;  // don't add duplicate dependencies
79     }
80 
81     *fDependencies.push() = dependedOn;
82 }
83 
84 // Convert from a GrSurface-based dependency to a GrDrawTarget one
addDependency(GrSurface * dependedOn)85 void GrDrawTarget::addDependency(GrSurface* dependedOn) {
86     if (dependedOn->asRenderTarget() && dependedOn->asRenderTarget()->getLastDrawTarget()) {
87         // If it is still receiving dependencies, this DT shouldn't be closed
88         SkASSERT(!this->isClosed());
89 
90         GrDrawTarget* dt = dependedOn->asRenderTarget()->getLastDrawTarget();
91         if (dt == this) {
92             // self-read - presumably for dst reads
93         } else {
94             this->addDependency(dt);
95 
96             // Can't make it closed in the self-read case
97             dt->makeClosed();
98         }
99     }
100 }
101 
102 #ifdef SK_DEBUG
dump() const103 void GrDrawTarget::dump() const {
104     SkDebugf("--------------------------------------------------------------\n");
105     SkDebugf("node: %d -> RT: %d\n", fDebugID, fRenderTarget ? fRenderTarget->getUniqueID() : -1);
106     SkDebugf("relies On (%d): ", fDependencies.count());
107     for (int i = 0; i < fDependencies.count(); ++i) {
108         SkDebugf("%d, ", fDependencies[i]->fDebugID);
109     }
110     SkDebugf("\n");
111     SkDebugf("batches (%d):\n", fBatches.count());
112     for (int i = 0; i < fBatches.count(); ++i) {
113 #if 0
114         SkDebugf("*******************************\n");
115 #endif
116         SkDebugf("%d: %s\n", i, fBatches[i]->name());
117 #if 0
118         SkString str = fBatches[i]->dumpInfo();
119         SkDebugf("%s\n", str.c_str());
120 #endif
121     }
122 }
123 #endif
124 
setupDstReadIfNecessary(const GrPipelineBuilder & pipelineBuilder,const GrPipelineOptimizations & optimizations,GrXferProcessor::DstTexture * dstTexture,const SkRect & batchBounds)125 bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder,
126                                            const GrPipelineOptimizations& optimizations,
127                                            GrXferProcessor::DstTexture* dstTexture,
128                                            const SkRect& batchBounds) {
129     SkRect bounds = batchBounds;
130     bounds.outset(0.5f, 0.5f);
131 
132     if (!pipelineBuilder.willXPNeedDstTexture(*this->caps(), optimizations)) {
133         return true;
134     }
135 
136     GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
137 
138     if (this->caps()->textureBarrierSupport()) {
139         if (GrTexture* rtTex = rt->asTexture()) {
140             // The render target is a texture, so we can read from it directly in the shader. The XP
141             // will be responsible to detect this situation and request a texture barrier.
142             dstTexture->setTexture(rtTex);
143             dstTexture->setOffset(0, 0);
144             return true;
145         }
146     }
147 
148     SkIRect copyRect;
149     pipelineBuilder.clip().getConservativeBounds(rt->width(), rt->height(), &copyRect);
150 
151     SkIRect drawIBounds;
152     bounds.roundOut(&drawIBounds);
153     if (!copyRect.intersect(drawIBounds)) {
154 #ifdef SK_DEBUG
155         GrCapsDebugf(this->caps(), "Missed an early reject. "
156                                    "Bailing on draw from setupDstReadIfNecessary.\n");
157 #endif
158         return false;
159     }
160 
161     // MSAA consideration: When there is support for reading MSAA samples in the shader we could
162     // have per-sample dst values by making the copy multisampled.
163     GrSurfaceDesc desc;
164     if (!fGpu->initCopySurfaceDstDesc(rt, &desc)) {
165         desc.fOrigin = kDefault_GrSurfaceOrigin;
166         desc.fFlags = kRenderTarget_GrSurfaceFlag;
167         desc.fConfig = rt->config();
168     }
169 
170     desc.fWidth = copyRect.width();
171     desc.fHeight = copyRect.height();
172 
173     static const uint32_t kFlags = 0;
174     SkAutoTUnref<GrTexture> copy(fResourceProvider->createApproxTexture(desc, kFlags));
175 
176     if (!copy) {
177         SkDebugf("Failed to create temporary copy of destination texture.\n");
178         return false;
179     }
180     SkIPoint dstPoint = {0, 0};
181     this->copySurface(copy, rt, copyRect, dstPoint);
182     dstTexture->setTexture(copy);
183     dstTexture->setOffset(copyRect.fLeft, copyRect.fTop);
184     return true;
185 }
186 
prepareBatches(GrBatchFlushState * flushState)187 void GrDrawTarget::prepareBatches(GrBatchFlushState* flushState) {
188     // Semi-usually the drawTargets are already closed at this point, but sometimes Ganesh
189     // needs to flush mid-draw. In that case, the SkGpuDevice's drawTargets won't be closed
190     // but need to be flushed anyway. Closing such drawTargets here will mean new
191     // drawTargets will be created to replace them if the SkGpuDevice(s) write to them again.
192     this->makeClosed();
193 
194     // Loop over the batches that haven't yet generated their geometry
195     for (int i = 0; i < fBatches.count(); ++i) {
196         fBatches[i]->prepare(flushState);
197     }
198 }
199 
drawBatches(GrBatchFlushState * flushState)200 void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) {
201     // Draw all the generated geometry.
202     SkRandom random;
203     for (int i = 0; i < fBatches.count(); ++i) {
204         if (fDrawBatchBounds) {
205             const SkRect& bounds = fBatches[i]->bounds();
206             SkIRect ibounds;
207             bounds.roundOut(&ibounds);
208             // In multi-draw buffer all the batches use the same render target and we won't need to
209             // get the batchs bounds.
210             if (GrRenderTarget* rt = fBatches[i]->renderTarget()) {
211                 fGpu->drawDebugWireRect(rt, ibounds, 0xFF000000 | random.nextU());
212             }
213         }
214         fBatches[i]->draw(flushState);
215     }
216 
217     fGpu->finishDrawTarget();
218 }
219 
reset()220 void GrDrawTarget::reset() {
221     fBatches.reset();
222 }
223 
drawBatch(const GrPipelineBuilder & pipelineBuilder,GrDrawBatch * batch)224 void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawBatch* batch) {
225     // Setup clip
226     GrPipelineBuilder::AutoRestoreStencil ars;
227     GrAppliedClip clip;
228     if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &batch->bounds(), &clip)) {
229         return;
230     }
231     GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
232     if (clip.clipCoverageFragmentProcessor()) {
233         arfps.set(&pipelineBuilder);
234         arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor());
235     }
236 
237     GrPipeline::CreateArgs args;
238     if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(), batch)) {
239         return;
240     }
241 
242 #ifdef ENABLE_MDB
243     SkASSERT(fRenderTarget);
244     batch->pipeline()->addDependenciesTo(fRenderTarget);
245 #endif
246 
247     this->recordBatch(batch);
248 }
249 
winding_path_stencil_settings()250 static const GrStencilSettings& winding_path_stencil_settings() {
251     GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
252         kIncClamp_StencilOp,
253         kIncClamp_StencilOp,
254         kAlwaysIfInClip_StencilFunc,
255         0xFFFF, 0xFFFF, 0xFFFF);
256     return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
257 }
258 
even_odd_path_stencil_settings()259 static const GrStencilSettings& even_odd_path_stencil_settings() {
260     GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
261         kInvert_StencilOp,
262         kInvert_StencilOp,
263         kAlwaysIfInClip_StencilFunc,
264         0xFFFF, 0xFFFF, 0xFFFF);
265     return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
266 }
267 
getPathStencilSettingsForFilltype(GrPathRendering::FillType fill,const GrStencilAttachment * sb,GrStencilSettings * outStencilSettings)268 void GrDrawTarget::getPathStencilSettingsForFilltype(GrPathRendering::FillType fill,
269                                                      const GrStencilAttachment* sb,
270                                                      GrStencilSettings* outStencilSettings) {
271 
272     switch (fill) {
273         default:
274             SkFAIL("Unexpected path fill.");
275         case GrPathRendering::kWinding_FillType:
276             *outStencilSettings = winding_path_stencil_settings();
277             break;
278         case GrPathRendering::kEvenOdd_FillType:
279             *outStencilSettings = even_odd_path_stencil_settings();
280             break;
281     }
282     fClipMaskManager->adjustPathStencilParams(sb, outStencilSettings);
283 }
284 
stencilPath(const GrPipelineBuilder & pipelineBuilder,const SkMatrix & viewMatrix,const GrPath * path,GrPathRendering::FillType fill)285 void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder,
286                                const SkMatrix& viewMatrix,
287                                const GrPath* path,
288                                GrPathRendering::FillType fill) {
289     // TODO: extract portions of checkDraw that are relevant to path stenciling.
290     SkASSERT(path);
291     SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport());
292 
293     // Setup clip
294     GrPipelineBuilder::AutoRestoreStencil ars;
295     GrAppliedClip clip;
296     if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, nullptr, &clip)) {
297         return;
298     }
299 
300     GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
301     if (clip.clipCoverageFragmentProcessor()) {
302         arfps.set(&pipelineBuilder);
303         arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor());
304     }
305 
306     // set stencil settings for path
307     GrStencilSettings stencilSettings;
308     GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
309     GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt);
310     this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings);
311 
312     GrBatch* batch = GrStencilPathBatch::Create(viewMatrix,
313                                                 pipelineBuilder.isHWAntialias(),
314                                                 stencilSettings, clip.scissorState(),
315                                                 pipelineBuilder.getRenderTarget(),
316                                                 path);
317     this->recordBatch(batch);
318     batch->unref();
319 }
320 
drawPathBatch(const GrPipelineBuilder & pipelineBuilder,GrDrawPathBatchBase * batch)321 void GrDrawTarget::drawPathBatch(const GrPipelineBuilder& pipelineBuilder,
322                                  GrDrawPathBatchBase* batch) {
323     // This looks like drawBatch() but there is an added wrinkle that stencil settings get inserted
324     // after setting up clipping but before onDrawBatch(). TODO: Figure out a better model for
325     // handling stencil settings WRT interactions between pipeline(builder), clipmaskmanager, and
326     // batches.
327     SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport());
328 
329     GrPipelineBuilder::AutoRestoreStencil ars;
330     GrAppliedClip clip;
331     if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &batch->bounds(), &clip)) {
332         return;
333     }
334 
335     GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
336     if (clip.clipCoverageFragmentProcessor()) {
337         arfps.set(&pipelineBuilder);
338         arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor());
339     }
340 
341     // Ensure the render target has a stencil buffer and get the stencil settings.
342     GrStencilSettings stencilSettings;
343     GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
344     GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt);
345     this->getPathStencilSettingsForFilltype(batch->fillType(), sb, &stencilSettings);
346     batch->setStencilSettings(stencilSettings);
347 
348     GrPipeline::CreateArgs args;
349     if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(), batch)) {
350         return;
351     }
352 
353     this->recordBatch(batch);
354 }
355 
clear(const SkIRect * rect,GrColor color,bool canIgnoreRect,GrRenderTarget * renderTarget)356 void GrDrawTarget::clear(const SkIRect* rect,
357                          GrColor color,
358                          bool canIgnoreRect,
359                          GrRenderTarget* renderTarget) {
360     SkIRect rtRect = SkIRect::MakeWH(renderTarget->width(), renderTarget->height());
361     SkIRect clippedRect;
362     if (!rect ||
363         (canIgnoreRect && this->caps()->fullClearIsFree()) ||
364         rect->contains(rtRect)) {
365         rect = &rtRect;
366     } else {
367         clippedRect = *rect;
368         if (!clippedRect.intersect(rtRect)) {
369             return;
370         }
371         rect = &clippedRect;
372     }
373 
374     if (this->caps()->useDrawInsteadOfClear()) {
375         // This works around a driver bug with clear by drawing a rect instead.
376         // The driver will ignore a clear if it is the only thing rendered to a
377         // target before the target is read.
378         if (rect == &rtRect) {
379             this->discard(renderTarget);
380         }
381 
382         GrPipelineBuilder pipelineBuilder;
383         pipelineBuilder.setXPFactory(
384             GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref();
385         pipelineBuilder.setRenderTarget(renderTarget);
386 
387         SkRect scalarRect = SkRect::Make(*rect);
388         SkAutoTUnref<GrDrawBatch> batch(
389                 GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(), scalarRect,
390                                                     nullptr, nullptr));
391         this->drawBatch(pipelineBuilder, batch);
392     } else {
393         GrBatch* batch = new GrClearBatch(*rect, color, renderTarget);
394         this->recordBatch(batch);
395         batch->unref();
396     }
397 }
398 
discard(GrRenderTarget * renderTarget)399 void GrDrawTarget::discard(GrRenderTarget* renderTarget) {
400     if (this->caps()->discardRenderTargetSupport()) {
401         GrBatch* batch = new GrDiscardBatch(renderTarget);
402         this->recordBatch(batch);
403         batch->unref();
404     }
405 }
406 
407 ////////////////////////////////////////////////////////////////////////////////
408 
copySurface(GrSurface * dst,GrSurface * src,const SkIRect & srcRect,const SkIPoint & dstPoint)409 bool GrDrawTarget::copySurface(GrSurface* dst,
410                                GrSurface* src,
411                                const SkIRect& srcRect,
412                                const SkIPoint& dstPoint) {
413     GrBatch* batch = GrCopySurfaceBatch::Create(dst, src, srcRect, dstPoint);
414     if (!batch) {
415         return false;
416     }
417 #ifdef ENABLE_MDB
418     this->addDependency(src);
419 #endif
420 
421     this->recordBatch(batch);
422     batch->unref();
423     return true;
424 }
425 
intersect(const Left & a,const Right & b)426 template <class Left, class Right> static bool intersect(const Left& a, const Right& b) {
427     SkASSERT(a.fLeft <= a.fRight && a.fTop <= a.fBottom &&
428              b.fLeft <= b.fRight && b.fTop <= b.fBottom);
429     return a.fLeft < b.fRight && b.fLeft < a.fRight && a.fTop < b.fBottom && b.fTop < a.fBottom;
430 }
431 
recordBatch(GrBatch * batch)432 void GrDrawTarget::recordBatch(GrBatch* batch) {
433     // A closed drawTarget should never receive new/more batches
434     SkASSERT(!this->isClosed());
435 
436     // Check if there is a Batch Draw we can batch with by linearly searching back until we either
437     // 1) check every draw
438     // 2) intersect with something
439     // 3) find a 'blocker'
440     GR_AUDIT_TRAIL_ADDBATCH(fAuditTrail, batch->name(), batch->bounds());
441     GrBATCH_INFO("Re-Recording (%s, B%u)\n"
442         "\tBounds LRTB (%f, %f, %f, %f)\n",
443         batch->name(),
444         batch->uniqueID(),
445         batch->bounds().fLeft, batch->bounds().fRight,
446         batch->bounds().fTop, batch->bounds().fBottom);
447     GrBATCH_INFO(SkTabString(batch->dumpInfo(), 1).c_str());
448     GrBATCH_INFO("\tOutcome:\n");
449     int maxCandidates = SkTMin(fMaxBatchLookback, fBatches.count());
450     if (maxCandidates) {
451         int i = 0;
452         while (true) {
453             GrBatch* candidate = fBatches.fromBack(i);
454             // We cannot continue to search backwards if the render target changes
455             if (candidate->renderTargetUniqueID() != batch->renderTargetUniqueID()) {
456                 GrBATCH_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n",
457                     candidate->name(), candidate->uniqueID());
458                 break;
459             }
460             if (candidate->combineIfPossible(batch, *this->caps())) {
461                 GrBATCH_INFO("\t\tCombining with (%s, B%u)\n", candidate->name(),
462                     candidate->uniqueID());
463                 return;
464             }
465             // Stop going backwards if we would cause a painter's order violation.
466             // TODO: The bounds used here do not fully consider the clip. It may be advantageous
467             // to clip each batch's bounds to the clip.
468             if (intersect(candidate->bounds(), batch->bounds())) {
469                 GrBATCH_INFO("\t\tIntersects with (%s, B%u)\n", candidate->name(),
470                     candidate->uniqueID());
471                 break;
472             }
473             ++i;
474             if (i == maxCandidates) {
475                 GrBATCH_INFO("\t\tReached max lookback or beginning of batch array %d\n", i);
476                 break;
477             }
478         }
479     } else {
480         GrBATCH_INFO("\t\tFirstBatch\n");
481     }
482     fBatches.push_back().reset(SkRef(batch));
483 }
484 
485 ///////////////////////////////////////////////////////////////////////////////
486 
installPipelineInDrawBatch(const GrPipelineBuilder * pipelineBuilder,const GrScissorState * scissor,GrDrawBatch * batch)487 bool GrDrawTarget::installPipelineInDrawBatch(const GrPipelineBuilder* pipelineBuilder,
488                                               const GrScissorState* scissor,
489                                               GrDrawBatch* batch) {
490     GrPipeline::CreateArgs args;
491     args.fPipelineBuilder = pipelineBuilder;
492     args.fCaps = this->caps();
493     args.fScissor = scissor;
494     batch->getPipelineOptimizations(&args.fOpts);
495     GrScissorState finalScissor;
496     if (args.fOpts.fOverrides.fUsePLSDstRead) {
497         GrRenderTarget* rt = pipelineBuilder->getRenderTarget();
498         GrGLIRect viewport;
499         viewport.fLeft = 0;
500         viewport.fBottom = 0;
501         viewport.fWidth = rt->width();
502         viewport.fHeight = rt->height();
503         SkIRect ibounds;
504         ibounds.fLeft = SkTPin(SkScalarFloorToInt(batch->bounds().fLeft), viewport.fLeft,
505                               viewport.fWidth);
506         ibounds.fTop = SkTPin(SkScalarFloorToInt(batch->bounds().fTop), viewport.fBottom,
507                              viewport.fHeight);
508         ibounds.fRight = SkTPin(SkScalarCeilToInt(batch->bounds().fRight), viewport.fLeft,
509                                viewport.fWidth);
510         ibounds.fBottom = SkTPin(SkScalarCeilToInt(batch->bounds().fBottom), viewport.fBottom,
511                                 viewport.fHeight);
512         if (scissor != nullptr && scissor->enabled()) {
513             if (!ibounds.intersect(scissor->rect())) {
514                 ibounds = scissor->rect();
515             }
516         }
517         finalScissor.set(ibounds);
518         args.fScissor = &finalScissor;
519     }
520     args.fOpts.fColorPOI.completeCalculations(pipelineBuilder->fColorFragmentProcessors.begin(),
521                                               pipelineBuilder->numColorFragmentProcessors());
522     args.fOpts.fCoveragePOI.completeCalculations(
523                                                pipelineBuilder->fCoverageFragmentProcessors.begin(),
524                                                pipelineBuilder->numCoverageFragmentProcessors());
525     if (!this->setupDstReadIfNecessary(*pipelineBuilder, args.fOpts, &args.fDstTexture,
526                                        batch->bounds())) {
527         return false;
528     }
529 
530     if (!batch->installPipeline(args)) {
531         return false;
532     }
533 
534     return true;
535 }
536 
clearStencilClip(const SkIRect & rect,bool insideClip,GrRenderTarget * rt)537 void GrDrawTarget::clearStencilClip(const SkIRect& rect, bool insideClip, GrRenderTarget* rt) {
538     GrBatch* batch = new GrClearStencilClipBatch(rect, insideClip, rt);
539     this->recordBatch(batch);
540     batch->unref();
541 }
542