GrContext.cpp revision eb1cb5c5b50febad115d859faca91d2d6af3fff2
1
2/*
3 * Copyright 2011 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 "GrContext.h"
10
11#include "GrAARectRenderer.h"
12#include "GrAtlasTextContext.h"
13#include "GrBatch.h"
14#include "GrBatchFontCache.h"
15#include "GrBatchTarget.h"
16#include "GrBatchTest.h"
17#include "GrCaps.h"
18#include "GrDefaultGeoProcFactory.h"
19#include "GrGpuResource.h"
20#include "GrGpuResourcePriv.h"
21#include "GrGpu.h"
22#include "GrImmediateDrawTarget.h"
23#include "GrIndexBuffer.h"
24#include "GrInOrderDrawBuffer.h"
25#include "GrLayerCache.h"
26#include "GrOvalRenderer.h"
27#include "GrPathRenderer.h"
28#include "GrPathUtils.h"
29#include "GrRenderTargetPriv.h"
30#include "GrResourceCache.h"
31#include "GrResourceProvider.h"
32#include "GrSoftwarePathRenderer.h"
33#include "GrStencilAndCoverTextContext.h"
34#include "GrStrokeInfo.h"
35#include "GrSurfacePriv.h"
36#include "GrTextBlobCache.h"
37#include "GrTexturePriv.h"
38#include "GrTraceMarker.h"
39#include "GrTracing.h"
40#include "GrVertices.h"
41#include "SkDashPathPriv.h"
42#include "SkConfig8888.h"
43#include "SkGr.h"
44#include "SkRRect.h"
45#include "SkStrokeRec.h"
46#include "SkTLazy.h"
47#include "SkTLS.h"
48#include "SkTraceEvent.h"
49
50#include "effects/GrConfigConversionEffect.h"
51#include "effects/GrDashingEffect.h"
52#include "effects/GrSingleTextureEffect.h"
53
54#define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
55#define RETURN_IF_ABANDONED if (!fDrawBuffer) { return; }
56#define RETURN_FALSE_IF_ABANDONED if (!fDrawBuffer) { return false; }
57#define RETURN_NULL_IF_ABANDONED if (!fDrawBuffer) { return NULL; }
58
59class GrContext::AutoCheckFlush {
60public:
61    AutoCheckFlush(GrContext* context) : fContext(context) { SkASSERT(context); }
62
63    ~AutoCheckFlush() {
64        if (fContext->fFlushToReduceCacheSize) {
65            fContext->flush();
66        }
67    }
68
69private:
70    GrContext* fContext;
71};
72
73GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext,
74                             const Options* opts) {
75    GrContext* context;
76    if (NULL == opts) {
77        context = SkNEW_ARGS(GrContext, (Options()));
78    } else {
79        context = SkNEW_ARGS(GrContext, (*opts));
80    }
81
82    if (context->init(backend, backendContext)) {
83        return context;
84    } else {
85        context->unref();
86        return NULL;
87    }
88}
89
90static int32_t gNextID = 1;
91static int32_t next_id() {
92    int32_t id;
93    do {
94        id = sk_atomic_inc(&gNextID);
95    } while (id == SK_InvalidGenID);
96    return id;
97}
98
99GrContext::GrContext(const Options& opts) : fOptions(opts), fUniqueID(next_id()) {
100    fGpu = NULL;
101    fResourceCache = NULL;
102    fResourceProvider = NULL;
103    fPathRendererChain = NULL;
104    fSoftwarePathRenderer = NULL;
105    fBatchFontCache = NULL;
106    fDrawBuffer = NULL;
107    fFlushToReduceCacheSize = false;
108    fAARectRenderer = NULL;
109    fOvalRenderer = NULL;
110    fMaxTextureSizeOverride = 1 << 20;
111}
112
113bool GrContext::init(GrBackend backend, GrBackendContext backendContext) {
114    SkASSERT(NULL == fGpu);
115
116    fGpu = GrGpu::Create(backend, backendContext, this);
117    if (NULL == fGpu) {
118        return false;
119    }
120    this->initCommon();
121    return true;
122}
123
124void GrContext::initCommon() {
125    fResourceCache = SkNEW(GrResourceCache);
126    fResourceCache->setOverBudgetCallback(OverBudgetCB, this);
127    fResourceProvider = SkNEW_ARGS(GrResourceProvider, (fGpu, fResourceCache));
128
129    fLayerCache.reset(SkNEW_ARGS(GrLayerCache, (this)));
130
131    fAARectRenderer = SkNEW(GrAARectRenderer);
132    fOvalRenderer = SkNEW(GrOvalRenderer);
133
134    fDidTestPMConversions = false;
135
136#ifdef IMMEDIATE_MODE
137    fDrawBuffer = SkNEW_ARGS(GrImmediateDrawTarget, (this));
138#else
139    fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (this));
140#endif
141
142    // GrBatchFontCache will eventually replace GrFontCache
143    fBatchFontCache = SkNEW_ARGS(GrBatchFontCache, (this));
144
145    fTextBlobCache.reset(SkNEW_ARGS(GrTextBlobCache, (TextBlobCacheOverBudgetCB, this)));
146}
147
148GrContext::~GrContext() {
149    if (NULL == fGpu) {
150        return;
151    }
152
153    this->flush();
154
155    for (int i = 0; i < fCleanUpData.count(); ++i) {
156        (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo);
157    }
158
159    SkDELETE(fResourceProvider);
160    SkDELETE(fResourceCache);
161    SkDELETE(fBatchFontCache);
162    SkDELETE(fDrawBuffer);
163
164    fAARectRenderer->unref();
165    fOvalRenderer->unref();
166
167    fGpu->unref();
168    SkSafeUnref(fPathRendererChain);
169    SkSafeUnref(fSoftwarePathRenderer);
170}
171
172void GrContext::abandonContext() {
173    fResourceProvider->abandon();
174    // abandon first to so destructors
175    // don't try to free the resources in the API.
176    fResourceCache->abandonAll();
177
178    fGpu->contextAbandoned();
179
180    // a path renderer may be holding onto resources that
181    // are now unusable
182    SkSafeSetNull(fPathRendererChain);
183    SkSafeSetNull(fSoftwarePathRenderer);
184
185    SkDELETE(fDrawBuffer);
186    fDrawBuffer = NULL;
187
188    fBatchFontCache->freeAll();
189    fLayerCache->freeAll();
190    fTextBlobCache->freeAll();
191}
192
193void GrContext::resetContext(uint32_t state) {
194    fGpu->markContextDirty(state);
195}
196
197void GrContext::freeGpuResources() {
198    this->flush();
199
200    if (fDrawBuffer) {
201        fDrawBuffer->purgeResources();
202    }
203
204    fBatchFontCache->freeAll();
205    fLayerCache->freeAll();
206    // a path renderer may be holding onto resources
207    SkSafeSetNull(fPathRendererChain);
208    SkSafeSetNull(fSoftwarePathRenderer);
209
210    fResourceCache->purgeAllUnlocked();
211}
212
213void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const {
214    if (resourceCount) {
215        *resourceCount = fResourceCache->getBudgetedResourceCount();
216    }
217    if (resourceBytes) {
218        *resourceBytes = fResourceCache->getBudgetedResourceBytes();
219    }
220}
221
222GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
223                                            SkGpuDevice* gpuDevice,
224                                            const SkDeviceProperties&
225                                            leakyProperties,
226                                            bool enableDistanceFieldFonts) {
227    if (fGpu->caps()->shaderCaps()->pathRenderingSupport() && renderTarget->isMultisampled()) {
228        GrStencilAttachment* sb = renderTarget->renderTargetPriv().attachStencilAttachment();
229        if (sb) {
230            return GrStencilAndCoverTextContext::Create(this, gpuDevice, leakyProperties);
231        }
232    }
233
234    return GrAtlasTextContext::Create(this, gpuDevice, leakyProperties, enableDistanceFieldFonts);
235}
236
237////////////////////////////////////////////////////////////////////////////////
238
239bool GrContext::isConfigTexturable(GrPixelConfig config) const {
240    return fGpu->caps()->isConfigTexturable(config);
241}
242
243bool GrContext::npotTextureTileSupport() const {
244    return fGpu->caps()->npotTextureTileSupport();
245}
246
247void GrContext::OverBudgetCB(void* data) {
248    SkASSERT(data);
249
250    GrContext* context = reinterpret_cast<GrContext*>(data);
251
252    // Flush the InOrderDrawBuffer to possibly free up some textures
253    context->fFlushToReduceCacheSize = true;
254}
255
256void GrContext::TextBlobCacheOverBudgetCB(void* data) {
257    SkASSERT(data);
258
259    // Unlike the GrResourceCache, TextBlobs are drawn at the SkGpuDevice level, therefore they
260    // cannot use fFlushTorReduceCacheSize because it uses AutoCheckFlush.  The solution is to move
261    // drawText calls to below the GrContext level, but this is not trivial because they call
262    // drawPath on SkGpuDevice
263    GrContext* context = reinterpret_cast<GrContext*>(data);
264    context->flush();
265}
266
267int GrContext::getMaxTextureSize() const {
268    return SkTMin(fGpu->caps()->maxTextureSize(), fMaxTextureSizeOverride);
269}
270
271int GrContext::getMaxRenderTargetSize() const {
272    return fGpu->caps()->maxRenderTargetSize();
273}
274
275int GrContext::getMaxSampleCount() const {
276    return fGpu->caps()->maxSampleCount();
277}
278
279///////////////////////////////////////////////////////////////////////////////
280
281void GrContext::clear(const SkIRect* rect,
282                      const GrColor color,
283                      bool canIgnoreRect,
284                      GrRenderTarget* renderTarget) {
285    RETURN_IF_ABANDONED
286    ASSERT_OWNED_RESOURCE(renderTarget);
287    SkASSERT(renderTarget);
288
289    AutoCheckFlush acf(this);
290    GR_CREATE_TRACE_MARKER_CONTEXT("GrContext::clear", this);
291    GrDrawTarget* target = this->prepareToDraw();
292    if (NULL == target) {
293        return;
294    }
295    target->clear(rect, color, canIgnoreRect, renderTarget);
296}
297
298void GrContext::drawPaint(GrRenderTarget* rt,
299                          const GrClip& clip,
300                          const GrPaint& origPaint,
301                          const SkMatrix& viewMatrix) {
302    RETURN_IF_ABANDONED
303    // set rect to be big enough to fill the space, but not super-huge, so we
304    // don't overflow fixed-point implementations
305    SkRect r;
306    r.setLTRB(0, 0,
307              SkIntToScalar(rt->width()),
308              SkIntToScalar(rt->height()));
309    SkTCopyOnFirstWrite<GrPaint> paint(origPaint);
310
311    // by definition this fills the entire clip, no need for AA
312    if (paint->isAntiAlias()) {
313        paint.writable()->setAntiAlias(false);
314    }
315
316    bool isPerspective = viewMatrix.hasPerspective();
317
318    // We attempt to map r by the inverse matrix and draw that. mapRect will
319    // map the four corners and bound them with a new rect. This will not
320    // produce a correct result for some perspective matrices.
321    if (!isPerspective) {
322        SkMatrix inverse;
323        if (!viewMatrix.invert(&inverse)) {
324            SkDebugf("Could not invert matrix\n");
325            return;
326        }
327        inverse.mapRect(&r);
328        this->drawRect(rt, clip, *paint, viewMatrix, r);
329    } else {
330        SkMatrix localMatrix;
331        if (!viewMatrix.invert(&localMatrix)) {
332            SkDebugf("Could not invert matrix\n");
333            return;
334        }
335
336        AutoCheckFlush acf(this);
337        GrPipelineBuilder pipelineBuilder;
338        GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, paint, &acf);
339        if (NULL == target) {
340            return;
341        }
342
343        GR_CREATE_TRACE_MARKER("GrContext::drawPaintWithPerspective", target);
344        target->drawRect(&pipelineBuilder,
345                         paint->getColor(),
346                         SkMatrix::I(),
347                         r,
348                         NULL,
349                         &localMatrix);
350    }
351}
352
353////////////////////////////////////////////////////////////////////////////////
354
355static inline bool is_irect(const SkRect& r) {
356  return SkScalarIsInt(r.fLeft)  && SkScalarIsInt(r.fTop) &&
357         SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom);
358}
359
360static bool apply_aa_to_rect(GrDrawTarget* target,
361                             GrPipelineBuilder* pipelineBuilder,
362                             SkRect* devBoundRect,
363                             const SkRect& rect,
364                             SkScalar strokeWidth,
365                             const SkMatrix& combinedMatrix,
366                             GrColor color) {
367    if (pipelineBuilder->getRenderTarget()->isMultisampled()) {
368        return false;
369    }
370
371#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
372    if (strokeWidth >= 0) {
373#endif
374        if (!combinedMatrix.preservesAxisAlignment()) {
375            return false;
376        }
377
378#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
379    } else {
380        if (!combinedMatrix.preservesRightAngles()) {
381            return false;
382        }
383    }
384#endif
385
386    combinedMatrix.mapRect(devBoundRect, rect);
387    if (!combinedMatrix.rectStaysRect()) {
388        return true;
389    }
390
391    if (strokeWidth < 0) {
392        return !is_irect(*devBoundRect);
393    }
394
395    return true;
396}
397
398static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) {
399    return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
400           point.fY >= rect.fTop && point.fY <= rect.fBottom;
401}
402
403class StrokeRectBatch : public GrBatch {
404public:
405    struct Geometry {
406        GrColor fColor;
407        SkMatrix fViewMatrix;
408        SkRect fRect;
409        SkScalar fStrokeWidth;
410    };
411
412    static GrBatch* Create(const Geometry& geometry, bool snapToPixelCenters) {
413        return SkNEW_ARGS(StrokeRectBatch, (geometry, snapToPixelCenters));
414    }
415
416    const char* name() const override { return "StrokeRectBatch"; }
417
418    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
419        // When this is called on a batch, there is only one geometry bundle
420        out->setKnownFourComponents(fGeoData[0].fColor);
421    }
422
423    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
424        out->setKnownSingleComponent(0xff);
425    }
426
427    void initBatchTracker(const GrPipelineInfo& init) override {
428        // Handle any color overrides
429        if (init.fColorIgnored) {
430            fGeoData[0].fColor = GrColor_ILLEGAL;
431        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
432            fGeoData[0].fColor = init.fOverrideColor;
433        }
434
435        // setup batch properties
436        fBatch.fColorIgnored = init.fColorIgnored;
437        fBatch.fColor = fGeoData[0].fColor;
438        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
439        fBatch.fCoverageIgnored = init.fCoverageIgnored;
440    }
441
442    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
443        SkAutoTUnref<const GrGeometryProcessor> gp(
444                GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
445                                                this->color(),
446                                                this->usesLocalCoords(),
447                                                this->coverageIgnored(),
448                                                this->viewMatrix(),
449                                                SkMatrix::I()));
450
451        batchTarget->initDraw(gp, pipeline);
452
453        size_t vertexStride = gp->getVertexStride();
454
455        SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
456
457        Geometry& args = fGeoData[0];
458
459        int vertexCount = kVertsPerHairlineRect;
460        if (args.fStrokeWidth > 0) {
461            vertexCount = kVertsPerStrokeRect;
462        }
463
464        const GrVertexBuffer* vertexBuffer;
465        int firstVertex;
466
467        void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount,
468                                                 &vertexBuffer, &firstVertex);
469
470        if (!verts) {
471            SkDebugf("Could not allocate vertices\n");
472            return;
473        }
474
475        SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
476
477        GrPrimitiveType primType;
478
479        if (args.fStrokeWidth > 0) {;
480            primType = kTriangleStrip_GrPrimitiveType;
481            args.fRect.sort();
482            this->setStrokeRectStrip(vertex, args.fRect, args.fStrokeWidth);
483        } else {
484            // hairline
485            primType = kLineStrip_GrPrimitiveType;
486            vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
487            vertex[1].set(args.fRect.fRight, args.fRect.fTop);
488            vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
489            vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
490            vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
491        }
492
493        GrVertices vertices;
494        vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
495        batchTarget->draw(vertices);
496    }
497
498    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
499
500private:
501    StrokeRectBatch(const Geometry& geometry, bool snapToPixelCenters) {
502        this->initClassID<StrokeRectBatch>();
503
504        fBatch.fHairline = geometry.fStrokeWidth == 0;
505
506        fGeoData.push_back(geometry);
507
508        // setup bounds
509        fBounds = geometry.fRect;
510        SkScalar rad = SkScalarHalf(geometry.fStrokeWidth);
511        fBounds.outset(rad, rad);
512        geometry.fViewMatrix.mapRect(&fBounds);
513
514        // If our caller snaps to pixel centers then we have to round out the bounds
515        if (snapToPixelCenters) {
516            fBounds.roundOut();
517        }
518    }
519
520    /*  create a triangle strip that strokes the specified rect. There are 8
521     unique vertices, but we repeat the last 2 to close up. Alternatively we
522     could use an indices array, and then only send 8 verts, but not sure that
523     would be faster.
524     */
525    void setStrokeRectStrip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
526        const SkScalar rad = SkScalarHalf(width);
527        // TODO we should be able to enable this assert, but we'd have to filter these draws
528        // this is a bug
529        //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
530
531        verts[0].set(rect.fLeft + rad, rect.fTop + rad);
532        verts[1].set(rect.fLeft - rad, rect.fTop - rad);
533        verts[2].set(rect.fRight - rad, rect.fTop + rad);
534        verts[3].set(rect.fRight + rad, rect.fTop - rad);
535        verts[4].set(rect.fRight - rad, rect.fBottom - rad);
536        verts[5].set(rect.fRight + rad, rect.fBottom + rad);
537        verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
538        verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
539        verts[8] = verts[0];
540        verts[9] = verts[1];
541    }
542
543
544    GrColor color() const { return fBatch.fColor; }
545    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
546    bool colorIgnored() const { return fBatch.fColorIgnored; }
547    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
548    bool hairline() const { return fBatch.fHairline; }
549    bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
550
551    bool onCombineIfPossible(GrBatch* t) override {
552        // StrokeRectBatch* that = t->cast<StrokeRectBatch>();
553
554        // NonAA stroke rects cannot batch right now
555        // TODO make these batchable
556        return false;
557    }
558
559    struct BatchTracker {
560        GrColor fColor;
561        bool fUsesLocalCoords;
562        bool fColorIgnored;
563        bool fCoverageIgnored;
564        bool fHairline;
565    };
566
567    const static int kVertsPerHairlineRect = 5;
568    const static int kVertsPerStrokeRect = 10;
569
570    BatchTracker fBatch;
571    SkSTArray<1, Geometry, true> fGeoData;
572};
573
574void GrContext::drawRect(GrRenderTarget* rt,
575                         const GrClip& clip,
576                         const GrPaint& paint,
577                         const SkMatrix& viewMatrix,
578                         const SkRect& rect,
579                         const GrStrokeInfo* strokeInfo) {
580    RETURN_IF_ABANDONED
581    if (strokeInfo && strokeInfo->isDashed()) {
582        SkPath path;
583        path.setIsVolatile(true);
584        path.addRect(rect);
585        this->drawPath(rt, clip, paint, viewMatrix, path, *strokeInfo);
586        return;
587    }
588
589    AutoCheckFlush acf(this);
590    GrPipelineBuilder pipelineBuilder;
591    GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
592    if (NULL == target) {
593        return;
594    }
595
596    GR_CREATE_TRACE_MARKER("GrContext::drawRect", target);
597    SkScalar width = NULL == strokeInfo ? -1 : strokeInfo->getWidth();
598
599    // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking
600    // cases where the RT is fully inside a stroke.
601    if (width < 0) {
602        SkRect rtRect;
603        pipelineBuilder.getRenderTarget()->getBoundsRect(&rtRect);
604        SkRect clipSpaceRTRect = rtRect;
605        bool checkClip = GrClip::kWideOpen_ClipType != clip.clipType();
606        if (checkClip) {
607            clipSpaceRTRect.offset(SkIntToScalar(clip.origin().fX),
608                                   SkIntToScalar(clip.origin().fY));
609        }
610        // Does the clip contain the entire RT?
611        if (!checkClip || clip.quickContains(clipSpaceRTRect)) {
612            SkMatrix invM;
613            if (!viewMatrix.invert(&invM)) {
614                return;
615            }
616            // Does the rect bound the RT?
617            SkPoint srcSpaceRTQuad[4];
618            invM.mapRectToQuad(srcSpaceRTQuad, rtRect);
619            if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) &&
620                rect_contains_inclusive(rect, srcSpaceRTQuad[1]) &&
621                rect_contains_inclusive(rect, srcSpaceRTQuad[2]) &&
622                rect_contains_inclusive(rect, srcSpaceRTQuad[3])) {
623                // Will it blend?
624                GrColor clearColor;
625                if (paint.isOpaqueAndConstantColor(&clearColor)) {
626                    target->clear(NULL, clearColor, true, rt);
627                    return;
628                }
629            }
630        }
631    }
632
633    GrColor color = paint.getColor();
634    SkRect devBoundRect;
635    bool needAA = paint.isAntiAlias() && !pipelineBuilder.getRenderTarget()->isMultisampled();
636    bool doAA = needAA && apply_aa_to_rect(target, &pipelineBuilder, &devBoundRect, rect, width,
637                                           viewMatrix, color);
638
639    if (doAA) {
640        if (width >= 0) {
641            fAARectRenderer->strokeAARect(target,
642                                          &pipelineBuilder,
643                                          color,
644                                          viewMatrix,
645                                          rect,
646                                          devBoundRect,
647                                          *strokeInfo);
648        } else {
649            // filled AA rect
650            fAARectRenderer->fillAARect(target,
651                                        &pipelineBuilder,
652                                        color,
653                                        viewMatrix,
654                                        rect,
655                                        devBoundRect);
656        }
657        return;
658    }
659
660    if (width >= 0) {
661        StrokeRectBatch::Geometry geometry;
662        geometry.fViewMatrix = viewMatrix;
663        geometry.fColor = color;
664        geometry.fRect = rect;
665        geometry.fStrokeWidth = width;
666
667        // Non-AA hairlines are snapped to pixel centers to make which pixels are hit deterministic
668        bool snapToPixelCenters = (0 == width && !rt->isMultisampled());
669        SkAutoTUnref<GrBatch> batch(StrokeRectBatch::Create(geometry, snapToPixelCenters));
670
671        // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
672        // hairline rects. We jam all the vertices to pixel centers to avoid this, but not when MSAA
673        // is enabled because it can cause ugly artifacts.
674        pipelineBuilder.setState(GrPipelineBuilder::kSnapVerticesToPixelCenters_Flag,
675                                 snapToPixelCenters);
676        target->drawBatch(&pipelineBuilder, batch);
677    } else {
678        // filled BW rect
679        target->drawSimpleRect(&pipelineBuilder, color, viewMatrix, rect);
680    }
681}
682
683void GrContext::drawNonAARectToRect(GrRenderTarget* rt,
684                                    const GrClip& clip,
685                                    const GrPaint& paint,
686                                    const SkMatrix& viewMatrix,
687                                    const SkRect& rectToDraw,
688                                    const SkRect& localRect,
689                                    const SkMatrix* localMatrix) {
690    RETURN_IF_ABANDONED
691    AutoCheckFlush acf(this);
692    GrPipelineBuilder pipelineBuilder;
693    GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
694    if (NULL == target) {
695        return;
696    }
697
698    GR_CREATE_TRACE_MARKER("GrContext::drawRectToRect", target);
699
700    target->drawRect(&pipelineBuilder,
701                     paint.getColor(),
702                     viewMatrix,
703                     rectToDraw,
704                     &localRect,
705                     localMatrix);
706}
707
708static const GrGeometryProcessor* set_vertex_attributes(bool hasLocalCoords,
709                                                        bool hasColors,
710                                                        int* colorOffset,
711                                                        int* texOffset,
712                                                        GrColor color,
713                                                        const SkMatrix& viewMatrix,
714                                                        bool coverageIgnored) {
715    *texOffset = -1;
716    *colorOffset = -1;
717    uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType;
718    if (hasLocalCoords && hasColors) {
719        *colorOffset = sizeof(SkPoint);
720        *texOffset = sizeof(SkPoint) + sizeof(GrColor);
721        flags |= GrDefaultGeoProcFactory::kColor_GPType |
722                 GrDefaultGeoProcFactory::kLocalCoord_GPType;
723    } else if (hasLocalCoords) {
724        *texOffset = sizeof(SkPoint);
725        flags |= GrDefaultGeoProcFactory::kLocalCoord_GPType;
726    } else if (hasColors) {
727        *colorOffset = sizeof(SkPoint);
728        flags |= GrDefaultGeoProcFactory::kColor_GPType;
729    }
730    return GrDefaultGeoProcFactory::Create(flags, color, hasLocalCoords, coverageIgnored,
731                                           viewMatrix, SkMatrix::I());
732}
733
734class DrawVerticesBatch : public GrBatch {
735public:
736    struct Geometry {
737        GrColor fColor;
738        SkTDArray<SkPoint> fPositions;
739        SkTDArray<uint16_t> fIndices;
740        SkTDArray<GrColor> fColors;
741        SkTDArray<SkPoint> fLocalCoords;
742    };
743
744    static GrBatch* Create(const Geometry& geometry, GrPrimitiveType primitiveType,
745                           const SkMatrix& viewMatrix,
746                           const SkPoint* positions, int vertexCount,
747                           const uint16_t* indices, int indexCount,
748                           const GrColor* colors, const SkPoint* localCoords,
749                           const SkRect& bounds) {
750        return SkNEW_ARGS(DrawVerticesBatch, (geometry, primitiveType, viewMatrix, positions,
751                                              vertexCount, indices, indexCount, colors,
752                                              localCoords, bounds));
753    }
754
755    const char* name() const override { return "DrawVerticesBatch"; }
756
757    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
758        // When this is called on a batch, there is only one geometry bundle
759        if (this->hasColors()) {
760            out->setUnknownFourComponents();
761        } else {
762            out->setKnownFourComponents(fGeoData[0].fColor);
763        }
764    }
765
766    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
767        out->setKnownSingleComponent(0xff);
768    }
769
770    void initBatchTracker(const GrPipelineInfo& init) override {
771        // Handle any color overrides
772        if (init.fColorIgnored) {
773            fGeoData[0].fColor = GrColor_ILLEGAL;
774        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
775            fGeoData[0].fColor = init.fOverrideColor;
776        }
777
778        // setup batch properties
779        fBatch.fColorIgnored = init.fColorIgnored;
780        fBatch.fColor = fGeoData[0].fColor;
781        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
782        fBatch.fCoverageIgnored = init.fCoverageIgnored;
783    }
784
785    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
786        int colorOffset = -1, texOffset = -1;
787        SkAutoTUnref<const GrGeometryProcessor> gp(
788                set_vertex_attributes(this->hasLocalCoords(), this->hasColors(), &colorOffset,
789                                      &texOffset, this->color(), this->viewMatrix(),
790                                      this->coverageIgnored()));
791
792        batchTarget->initDraw(gp, pipeline);
793
794        size_t vertexStride = gp->getVertexStride();
795
796        SkASSERT(vertexStride == sizeof(SkPoint) + (this->hasLocalCoords() ? sizeof(SkPoint) : 0)
797                                                 + (this->hasColors() ? sizeof(GrColor) : 0));
798
799        int instanceCount = fGeoData.count();
800
801        const GrVertexBuffer* vertexBuffer;
802        int firstVertex;
803
804        void* verts = batchTarget->makeVertSpace(vertexStride, this->vertexCount(),
805                                                 &vertexBuffer, &firstVertex);
806
807        if (!verts) {
808            SkDebugf("Could not allocate vertices\n");
809            return;
810        }
811
812        const GrIndexBuffer* indexBuffer = NULL;
813        int firstIndex = 0;
814
815        uint16_t* indices = NULL;
816        if (this->hasIndices()) {
817            indices = batchTarget->makeIndexSpace(this->indexCount(), &indexBuffer, &firstIndex);
818
819            if (!indices) {
820                SkDebugf("Could not allocate indices\n");
821                return;
822            }
823        }
824
825        int indexOffset = 0;
826        int vertexOffset = 0;
827        for (int i = 0; i < instanceCount; i++) {
828            const Geometry& args = fGeoData[i];
829
830            // TODO we can actually cache this interleaved and then just memcopy
831            if (this->hasIndices()) {
832                for (int j = 0; j < args.fIndices.count(); ++j, ++indexOffset) {
833                    *(indices + indexOffset) = args.fIndices[j] + vertexOffset;
834                }
835            }
836
837            for (int j = 0; j < args.fPositions.count(); ++j) {
838                *((SkPoint*)verts) = args.fPositions[j];
839                if (this->hasColors()) {
840                    *(GrColor*)((intptr_t)verts + colorOffset) = args.fColors[j];
841                }
842                if (this->hasLocalCoords()) {
843                    *(SkPoint*)((intptr_t)verts + texOffset) = args.fLocalCoords[j];
844                }
845                verts = (void*)((intptr_t)verts + vertexStride);
846                vertexOffset++;
847            }
848        }
849
850        GrVertices vertices;
851        if (this->hasIndices()) {
852            vertices.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex,
853                                 firstIndex, this->vertexCount(), this->indexCount());
854
855        } else {
856            vertices.init(this->primitiveType(), vertexBuffer, firstVertex, this->vertexCount());
857        }
858        batchTarget->draw(vertices);
859    }
860
861    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
862
863private:
864    DrawVerticesBatch(const Geometry& geometry, GrPrimitiveType primitiveType,
865                      const SkMatrix& viewMatrix,
866                      const SkPoint* positions, int vertexCount,
867                      const uint16_t* indices, int indexCount,
868                      const GrColor* colors, const SkPoint* localCoords, const SkRect& bounds) {
869        this->initClassID<DrawVerticesBatch>();
870        SkASSERT(positions);
871
872        fBatch.fViewMatrix = viewMatrix;
873        Geometry& installedGeo = fGeoData.push_back(geometry);
874
875        installedGeo.fPositions.append(vertexCount, positions);
876        if (indices) {
877            installedGeo.fIndices.append(indexCount, indices);
878            fBatch.fHasIndices = true;
879        } else {
880            fBatch.fHasIndices = false;
881        }
882
883        if (colors) {
884            installedGeo.fColors.append(vertexCount, colors);
885            fBatch.fHasColors = true;
886        } else {
887            fBatch.fHasColors = false;
888        }
889
890        if (localCoords) {
891            installedGeo.fLocalCoords.append(vertexCount, localCoords);
892            fBatch.fHasLocalCoords = true;
893        } else {
894            fBatch.fHasLocalCoords = false;
895        }
896        fBatch.fVertexCount = vertexCount;
897        fBatch.fIndexCount = indexCount;
898        fBatch.fPrimitiveType = primitiveType;
899
900        this->setBounds(bounds);
901    }
902
903    GrPrimitiveType primitiveType() const { return fBatch.fPrimitiveType; }
904    bool batchablePrimitiveType() const {
905        return kTriangles_GrPrimitiveType == fBatch.fPrimitiveType ||
906               kLines_GrPrimitiveType == fBatch.fPrimitiveType ||
907               kPoints_GrPrimitiveType == fBatch.fPrimitiveType;
908    }
909    GrColor color() const { return fBatch.fColor; }
910    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
911    bool colorIgnored() const { return fBatch.fColorIgnored; }
912    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
913    bool hasColors() const { return fBatch.fHasColors; }
914    bool hasIndices() const { return fBatch.fHasIndices; }
915    bool hasLocalCoords() const { return fBatch.fHasLocalCoords; }
916    int vertexCount() const { return fBatch.fVertexCount; }
917    int indexCount() const { return fBatch.fIndexCount; }
918    bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
919
920    bool onCombineIfPossible(GrBatch* t) override {
921        DrawVerticesBatch* that = t->cast<DrawVerticesBatch>();
922
923        if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) {
924            return false;
925        }
926
927        SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
928
929        // We currently use a uniform viewmatrix for this batch
930        if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
931            return false;
932        }
933
934        if (this->hasColors() != that->hasColors()) {
935            return false;
936        }
937
938        if (this->hasIndices() != that->hasIndices()) {
939            return false;
940        }
941
942        if (this->hasLocalCoords() != that->hasLocalCoords()) {
943            return false;
944        }
945
946        if (!this->hasColors() && this->color() != that->color()) {
947            return false;
948        }
949
950        if (this->color() != that->color()) {
951            fBatch.fColor = GrColor_ILLEGAL;
952        }
953        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
954        fBatch.fVertexCount += that->vertexCount();
955        fBatch.fIndexCount += that->indexCount();
956
957        this->joinBounds(that->bounds());
958        return true;
959    }
960
961    struct BatchTracker {
962        GrPrimitiveType fPrimitiveType;
963        SkMatrix fViewMatrix;
964        GrColor fColor;
965        bool fUsesLocalCoords;
966        bool fColorIgnored;
967        bool fCoverageIgnored;
968        bool fHasColors;
969        bool fHasIndices;
970        bool fHasLocalCoords;
971        int fVertexCount;
972        int fIndexCount;
973    };
974
975    BatchTracker fBatch;
976    SkSTArray<1, Geometry, true> fGeoData;
977};
978
979void GrContext::drawVertices(GrRenderTarget* rt,
980                             const GrClip& clip,
981                             const GrPaint& paint,
982                             const SkMatrix& viewMatrix,
983                             GrPrimitiveType primitiveType,
984                             int vertexCount,
985                             const SkPoint positions[],
986                             const SkPoint texCoords[],
987                             const GrColor colors[],
988                             const uint16_t indices[],
989                             int indexCount) {
990    RETURN_IF_ABANDONED
991    AutoCheckFlush acf(this);
992    GrPipelineBuilder pipelineBuilder;
993
994    GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
995    if (NULL == target) {
996        return;
997    }
998
999    GR_CREATE_TRACE_MARKER("GrContext::drawVertices", target);
1000
1001    // TODO clients should give us bounds
1002    SkRect bounds;
1003    if (!bounds.setBoundsCheck(positions, vertexCount)) {
1004        SkDebugf("drawVertices call empty bounds\n");
1005        return;
1006    }
1007
1008    viewMatrix.mapRect(&bounds);
1009
1010    // If we don't have AA then we outset for a half pixel in each direction to account for
1011    // snapping
1012    if (!paint.isAntiAlias()) {
1013        bounds.outset(0.5f, 0.5f);
1014    }
1015
1016    DrawVerticesBatch::Geometry geometry;
1017    geometry.fColor = paint.getColor();
1018    SkAutoTUnref<GrBatch> batch(DrawVerticesBatch::Create(geometry, primitiveType, viewMatrix,
1019                                                          positions, vertexCount, indices,
1020                                                          indexCount, colors, texCoords,
1021                                                          bounds));
1022
1023    target->drawBatch(&pipelineBuilder, batch);
1024}
1025
1026///////////////////////////////////////////////////////////////////////////////
1027
1028void GrContext::drawRRect(GrRenderTarget*rt,
1029                          const GrClip& clip,
1030                          const GrPaint& paint,
1031                          const SkMatrix& viewMatrix,
1032                          const SkRRect& rrect,
1033                          const GrStrokeInfo& strokeInfo) {
1034    RETURN_IF_ABANDONED
1035    if (rrect.isEmpty()) {
1036       return;
1037    }
1038
1039    if (strokeInfo.isDashed()) {
1040        SkPath path;
1041        path.setIsVolatile(true);
1042        path.addRRect(rrect);
1043        this->drawPath(rt, clip, paint, viewMatrix, path, strokeInfo);
1044        return;
1045    }
1046
1047    AutoCheckFlush acf(this);
1048    GrPipelineBuilder pipelineBuilder;
1049    GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
1050    if (NULL == target) {
1051        return;
1052    }
1053
1054    GR_CREATE_TRACE_MARKER("GrContext::drawRRect", target);
1055
1056    GrColor color = paint.getColor();
1057    if (!fOvalRenderer->drawRRect(target,
1058                                  &pipelineBuilder,
1059                                  color,
1060                                  viewMatrix,
1061                                  paint.isAntiAlias(),
1062                                  rrect,
1063                                  strokeInfo)) {
1064        SkPath path;
1065        path.setIsVolatile(true);
1066        path.addRRect(rrect);
1067        this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(),
1068                               path, strokeInfo);
1069    }
1070}
1071
1072///////////////////////////////////////////////////////////////////////////////
1073
1074void GrContext::drawDRRect(GrRenderTarget* rt,
1075                           const GrClip& clip,
1076                           const GrPaint& paint,
1077                           const SkMatrix& viewMatrix,
1078                           const SkRRect& outer,
1079                           const SkRRect& inner) {
1080    RETURN_IF_ABANDONED
1081    if (outer.isEmpty()) {
1082       return;
1083    }
1084
1085    AutoCheckFlush acf(this);
1086    GrPipelineBuilder pipelineBuilder;
1087    GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
1088
1089    GR_CREATE_TRACE_MARKER("GrContext::drawDRRect", target);
1090
1091    GrColor color = paint.getColor();
1092    if (!fOvalRenderer->drawDRRect(target,
1093                                   &pipelineBuilder,
1094                                   color,
1095                                   viewMatrix,
1096                                   paint.isAntiAlias(),
1097                                   outer,
1098                                   inner)) {
1099        SkPath path;
1100        path.setIsVolatile(true);
1101        path.addRRect(inner);
1102        path.addRRect(outer);
1103        path.setFillType(SkPath::kEvenOdd_FillType);
1104        GrStrokeInfo fillRec(SkStrokeRec::kFill_InitStyle);
1105        this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(),
1106                               path, fillRec);
1107    }
1108}
1109
1110///////////////////////////////////////////////////////////////////////////////
1111
1112void GrContext::drawOval(GrRenderTarget* rt,
1113                         const GrClip& clip,
1114                         const GrPaint& paint,
1115                         const SkMatrix& viewMatrix,
1116                         const SkRect& oval,
1117                         const GrStrokeInfo& strokeInfo) {
1118    RETURN_IF_ABANDONED
1119    if (oval.isEmpty()) {
1120       return;
1121    }
1122
1123    if (strokeInfo.isDashed()) {
1124        SkPath path;
1125        path.setIsVolatile(true);
1126        path.addOval(oval);
1127        this->drawPath(rt, clip, paint, viewMatrix, path, strokeInfo);
1128        return;
1129    }
1130
1131    AutoCheckFlush acf(this);
1132    GrPipelineBuilder pipelineBuilder;
1133    GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
1134    if (NULL == target) {
1135        return;
1136    }
1137
1138    GR_CREATE_TRACE_MARKER("GrContext::drawOval", target);
1139
1140    GrColor color = paint.getColor();
1141    if (!fOvalRenderer->drawOval(target,
1142                                 &pipelineBuilder,
1143                                 color,
1144                                 viewMatrix,
1145                                 paint.isAntiAlias(),
1146                                 oval,
1147                                 strokeInfo)) {
1148        SkPath path;
1149        path.setIsVolatile(true);
1150        path.addOval(oval);
1151        this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(),
1152                               path, strokeInfo);
1153    }
1154}
1155
1156// Can 'path' be drawn as a pair of filled nested rectangles?
1157static bool is_nested_rects(GrDrawTarget* target,
1158                            GrPipelineBuilder* pipelineBuilder,
1159                            GrColor color,
1160                            const SkMatrix& viewMatrix,
1161                            const SkPath& path,
1162                            const SkStrokeRec& stroke,
1163                            SkRect rects[2]) {
1164    SkASSERT(stroke.isFillStyle());
1165
1166    if (path.isInverseFillType()) {
1167        return false;
1168    }
1169
1170    // TODO: this restriction could be lifted if we were willing to apply
1171    // the matrix to all the points individually rather than just to the rect
1172    if (!viewMatrix.preservesAxisAlignment()) {
1173        return false;
1174    }
1175
1176    SkPath::Direction dirs[2];
1177    if (!path.isNestedFillRects(rects, dirs)) {
1178        return false;
1179    }
1180
1181    if (SkPath::kWinding_FillType == path.getFillType() && dirs[0] == dirs[1]) {
1182        // The two rects need to be wound opposite to each other
1183        return false;
1184    }
1185
1186    // Right now, nested rects where the margin is not the same width
1187    // all around do not render correctly
1188    const SkScalar* outer = rects[0].asScalars();
1189    const SkScalar* inner = rects[1].asScalars();
1190
1191    bool allEq = true;
1192
1193    SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
1194    bool allGoE1 = margin >= SK_Scalar1;
1195
1196    for (int i = 1; i < 4; ++i) {
1197        SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
1198        if (temp < SK_Scalar1) {
1199            allGoE1 = false;
1200        }
1201        if (!SkScalarNearlyEqual(margin, temp)) {
1202            allEq = false;
1203        }
1204    }
1205
1206    return allEq || allGoE1;
1207}
1208
1209void GrContext::drawPath(GrRenderTarget* rt,
1210                         const GrClip& clip,
1211                         const GrPaint& paint,
1212                         const SkMatrix& viewMatrix,
1213                         const SkPath& path,
1214                         const GrStrokeInfo& strokeInfo) {
1215    RETURN_IF_ABANDONED
1216    if (path.isEmpty()) {
1217       if (path.isInverseFillType()) {
1218           this->drawPaint(rt, clip, paint, viewMatrix);
1219       }
1220       return;
1221    }
1222
1223    GrColor color = paint.getColor();
1224
1225    // Note that internalDrawPath may sw-rasterize the path into a scratch texture.
1226    // Scratch textures can be recycled after they are returned to the texture
1227    // cache. This presents a potential hazard for buffered drawing. However,
1228    // the writePixels that uploads to the scratch will perform a flush so we're
1229    // OK.
1230    AutoCheckFlush acf(this);
1231    GrPipelineBuilder pipelineBuilder;
1232    GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
1233    if (NULL == target) {
1234        return;
1235    }
1236
1237    GR_CREATE_TRACE_MARKER1("GrContext::drawPath", target, "Is Convex", path.isConvex());
1238
1239    if (!strokeInfo.isDashed()) {
1240        bool useCoverageAA = paint.isAntiAlias() &&
1241                !pipelineBuilder.getRenderTarget()->isMultisampled();
1242
1243        if (useCoverageAA && strokeInfo.getWidth() < 0 && !path.isConvex()) {
1244            // Concave AA paths are expensive - try to avoid them for special cases
1245            SkRect rects[2];
1246
1247            if (is_nested_rects(target, &pipelineBuilder, color, viewMatrix, path, strokeInfo,
1248                                rects)) {
1249                fAARectRenderer->fillAANestedRects(target, &pipelineBuilder, color, viewMatrix,
1250                                                   rects);
1251                return;
1252            }
1253        }
1254        SkRect ovalRect;
1255        bool isOval = path.isOval(&ovalRect);
1256
1257        if (isOval && !path.isInverseFillType()) {
1258            if (fOvalRenderer->drawOval(target,
1259                                        &pipelineBuilder,
1260                                        color,
1261                                        viewMatrix,
1262                                        paint.isAntiAlias(),
1263                                        ovalRect,
1264                                        strokeInfo)) {
1265                return;
1266            }
1267        }
1268    }
1269    this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(),
1270                           path, strokeInfo);
1271}
1272
1273void GrContext::internalDrawPath(GrDrawTarget* target,
1274                                 GrPipelineBuilder* pipelineBuilder,
1275                                 const SkMatrix& viewMatrix,
1276                                 GrColor color,
1277                                 bool useAA,
1278                                 const SkPath& path,
1279                                 const GrStrokeInfo& strokeInfo) {
1280    RETURN_IF_ABANDONED
1281    SkASSERT(!path.isEmpty());
1282
1283    GR_CREATE_TRACE_MARKER("GrContext::internalDrawPath", target);
1284
1285
1286    // An Assumption here is that path renderer would use some form of tweaking
1287    // the src color (either the input alpha or in the frag shader) to implement
1288    // aa. If we have some future driver-mojo path AA that can do the right
1289    // thing WRT to the blend then we'll need some query on the PR.
1290    bool useCoverageAA = useAA &&
1291        !pipelineBuilder->getRenderTarget()->isMultisampled();
1292
1293
1294    GrPathRendererChain::DrawType type =
1295        useCoverageAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
1296                        GrPathRendererChain::kColor_DrawType;
1297
1298    const SkPath* pathPtr = &path;
1299    SkTLazy<SkPath> tmpPath;
1300    const GrStrokeInfo* strokeInfoPtr = &strokeInfo;
1301
1302    // Try a 1st time without stroking the path and without allowing the SW renderer
1303    GrPathRenderer* pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr,
1304                                               *strokeInfoPtr, false, type);
1305
1306    GrStrokeInfo dashlessStrokeInfo(strokeInfo, false);
1307    if (NULL == pr && strokeInfo.isDashed()) {
1308        // It didn't work above, so try again with dashed stroke converted to a dashless stroke.
1309        if (!strokeInfo.applyDashToPath(tmpPath.init(), &dashlessStrokeInfo, *pathPtr)) {
1310            return;
1311        }
1312        pathPtr = tmpPath.get();
1313        if (pathPtr->isEmpty()) {
1314            return;
1315        }
1316        strokeInfoPtr = &dashlessStrokeInfo;
1317        pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr,
1318                                   false, type);
1319    }
1320
1321    if (NULL == pr) {
1322        if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(*strokeInfoPtr, viewMatrix, NULL) &&
1323            !strokeInfoPtr->isFillStyle()) {
1324            // It didn't work above, so try again with stroke converted to a fill.
1325            if (!tmpPath.isValid()) {
1326                tmpPath.init();
1327            }
1328            dashlessStrokeInfo.setResScale(SkScalarAbs(viewMatrix.getMaxScale()));
1329            if (!dashlessStrokeInfo.applyToPath(tmpPath.get(), *pathPtr)) {
1330                return;
1331            }
1332            pathPtr = tmpPath.get();
1333            if (pathPtr->isEmpty()) {
1334                return;
1335            }
1336            dashlessStrokeInfo.setFillStyle();
1337            strokeInfoPtr = &dashlessStrokeInfo;
1338        }
1339
1340        // This time, allow SW renderer
1341        pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr,
1342                                   true, type);
1343    }
1344
1345    if (NULL == pr) {
1346#ifdef SK_DEBUG
1347        SkDebugf("Unable to find path renderer compatible with path.\n");
1348#endif
1349        return;
1350    }
1351
1352    pr->drawPath(target, pipelineBuilder, color, viewMatrix, *pathPtr, *strokeInfoPtr, useCoverageAA);
1353}
1354
1355////////////////////////////////////////////////////////////////////////////////
1356
1357void GrContext::flush(int flagsBitfield) {
1358    if (NULL == fDrawBuffer) {
1359        return;
1360    }
1361
1362    if (kDiscard_FlushBit & flagsBitfield) {
1363        fDrawBuffer->reset();
1364    } else {
1365        fDrawBuffer->flush();
1366    }
1367    fResourceCache->notifyFlushOccurred();
1368    fFlushToReduceCacheSize = false;
1369}
1370
1371bool sw_convert_to_premul(GrPixelConfig srcConfig, int width, int height, size_t inRowBytes,
1372                          const void* inPixels, size_t outRowBytes, void* outPixels) {
1373    SkSrcPixelInfo srcPI;
1374    if (!GrPixelConfig2ColorAndProfileType(srcConfig, &srcPI.fColorType, NULL)) {
1375        return false;
1376    }
1377    srcPI.fAlphaType = kUnpremul_SkAlphaType;
1378    srcPI.fPixels = inPixels;
1379    srcPI.fRowBytes = inRowBytes;
1380
1381    SkDstPixelInfo dstPI;
1382    dstPI.fColorType = srcPI.fColorType;
1383    dstPI.fAlphaType = kPremul_SkAlphaType;
1384    dstPI.fPixels = outPixels;
1385    dstPI.fRowBytes = outRowBytes;
1386
1387    return srcPI.convertPixelsTo(&dstPI, width, height);
1388}
1389
1390bool GrContext::writeSurfacePixels(GrSurface* surface,
1391                                   int left, int top, int width, int height,
1392                                   GrPixelConfig srcConfig, const void* buffer, size_t rowBytes,
1393                                   uint32_t pixelOpsFlags) {
1394    RETURN_FALSE_IF_ABANDONED
1395    {
1396        GrTexture* texture = NULL;
1397        if (!(kUnpremul_PixelOpsFlag & pixelOpsFlags) && (texture = surface->asTexture()) &&
1398            fGpu->canWriteTexturePixels(texture, srcConfig)) {
1399
1400            if (!(kDontFlush_PixelOpsFlag & pixelOpsFlags) &&
1401                surface->surfacePriv().hasPendingIO()) {
1402                this->flush();
1403            }
1404            return fGpu->writeTexturePixels(texture, left, top, width, height,
1405                                            srcConfig, buffer, rowBytes);
1406            // Don't need to check kFlushWrites_PixelOp here, we just did a direct write so the
1407            // upload is already flushed.
1408        }
1409    }
1410
1411    // If we didn't do a direct texture write then we upload the pixels to a texture and draw.
1412    GrRenderTarget* renderTarget = surface->asRenderTarget();
1413    if (NULL == renderTarget) {
1414        return false;
1415    }
1416
1417    // We ignore the preferred config unless it is a R/B swap of the src config. In that case
1418    // we will upload the original src data to a scratch texture but we will spoof it as the swapped
1419    // config. This scratch will then have R and B swapped. We correct for this by swapping again
1420    // when drawing the scratch to the dst using a conversion effect.
1421    bool swapRAndB = false;
1422    GrPixelConfig writeConfig = srcConfig;
1423    if (GrPixelConfigSwapRAndB(srcConfig) ==
1424        fGpu->preferredWritePixelsConfig(srcConfig, renderTarget->config())) {
1425        writeConfig = GrPixelConfigSwapRAndB(srcConfig);
1426        swapRAndB = true;
1427    }
1428
1429    GrSurfaceDesc desc;
1430    desc.fWidth = width;
1431    desc.fHeight = height;
1432    desc.fConfig = writeConfig;
1433    SkAutoTUnref<GrTexture> texture(this->textureProvider()->refScratchTexture(desc,
1434        GrTextureProvider::kApprox_ScratchTexMatch));
1435    if (!texture) {
1436        return false;
1437    }
1438
1439    SkAutoTUnref<const GrFragmentProcessor> fp;
1440    SkMatrix textureMatrix;
1441    textureMatrix.setIDiv(texture->width(), texture->height());
1442
1443    // allocate a tmp buffer and sw convert the pixels to premul
1444    SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
1445
1446    if (kUnpremul_PixelOpsFlag & pixelOpsFlags) {
1447        if (!GrPixelConfigIs8888(srcConfig)) {
1448            return false;
1449        }
1450        fp.reset(this->createUPMToPMEffect(texture, swapRAndB, textureMatrix));
1451        // handle the unpremul step on the CPU if we couldn't create an effect to do it.
1452        if (NULL == fp) {
1453            size_t tmpRowBytes = 4 * width;
1454            tmpPixels.reset(width * height);
1455            if (!sw_convert_to_premul(srcConfig, width, height, rowBytes, buffer, tmpRowBytes,
1456                                      tmpPixels.get())) {
1457                return false;
1458            }
1459            rowBytes = tmpRowBytes;
1460            buffer = tmpPixels.get();
1461        }
1462    }
1463    if (NULL == fp) {
1464        fp.reset(GrConfigConversionEffect::Create(texture,
1465                                                  swapRAndB,
1466                                                  GrConfigConversionEffect::kNone_PMConversion,
1467                                                  textureMatrix));
1468    }
1469
1470    // Even if the client told us not to flush, we still flush here. The client may have known that
1471    // writes to the original surface caused no data hazards, but they can't know that the scratch
1472    // we just got is safe.
1473    if (texture->surfacePriv().hasPendingIO()) {
1474        this->flush();
1475    }
1476    if (!fGpu->writeTexturePixels(texture, 0, 0, width, height,
1477                                  writeConfig, buffer, rowBytes)) {
1478        return false;
1479    }
1480
1481    SkMatrix matrix;
1482    matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
1483
1484    // This function can be called in the midst of drawing another object (e.g., when uploading a
1485    // SW-rasterized clip while issuing a draw). So we push the current geometry state before
1486    // drawing a rect to the render target.
1487    // The bracket ensures we pop the stack if we wind up flushing below.
1488    {
1489        GrDrawTarget* drawTarget = this->prepareToDraw();
1490        if (!drawTarget) {
1491            return false;
1492        }
1493
1494        GrPipelineBuilder pipelineBuilder;
1495        pipelineBuilder.addColorProcessor(fp);
1496        pipelineBuilder.setRenderTarget(renderTarget);
1497        drawTarget->drawSimpleRect(&pipelineBuilder,
1498                                   GrColor_WHITE,
1499                                   matrix,
1500                                   SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)));
1501    }
1502
1503    if (kFlushWrites_PixelOp & pixelOpsFlags) {
1504        this->flushSurfaceWrites(surface);
1505    }
1506
1507    return true;
1508}
1509
1510// toggles between RGBA and BGRA
1511static SkColorType toggle_colortype32(SkColorType ct) {
1512    if (kRGBA_8888_SkColorType == ct) {
1513        return kBGRA_8888_SkColorType;
1514    } else {
1515        SkASSERT(kBGRA_8888_SkColorType == ct);
1516        return kRGBA_8888_SkColorType;
1517    }
1518}
1519
1520bool GrContext::readRenderTargetPixels(GrRenderTarget* target,
1521                                       int left, int top, int width, int height,
1522                                       GrPixelConfig dstConfig, void* buffer, size_t rowBytes,
1523                                       uint32_t flags) {
1524    RETURN_FALSE_IF_ABANDONED
1525    ASSERT_OWNED_RESOURCE(target);
1526    SkASSERT(target);
1527
1528    if (!(kDontFlush_PixelOpsFlag & flags) && target->surfacePriv().hasPendingWrite()) {
1529        this->flush();
1530    }
1531
1532    // Determine which conversions have to be applied: flipY, swapRAnd, and/or unpremul.
1533
1534    // If fGpu->readPixels would incur a y-flip cost then we will read the pixels upside down. We'll
1535    // either do the flipY by drawing into a scratch with a matrix or on the cpu after the read.
1536    bool flipY = fGpu->readPixelsWillPayForYFlip(target, left, top,
1537                                                 width, height, dstConfig,
1538                                                 rowBytes);
1539    // We ignore the preferred config if it is different than our config unless it is an R/B swap.
1540    // In that case we'll perform an R and B swap while drawing to a scratch texture of the swapped
1541    // config. Then we will call readPixels on the scratch with the swapped config. The swaps during
1542    // the draw cancels out the fact that we call readPixels with a config that is R/B swapped from
1543    // dstConfig.
1544    GrPixelConfig readConfig = dstConfig;
1545    bool swapRAndB = false;
1546    if (GrPixelConfigSwapRAndB(dstConfig) ==
1547        fGpu->preferredReadPixelsConfig(dstConfig, target->config())) {
1548        readConfig = GrPixelConfigSwapRAndB(readConfig);
1549        swapRAndB = true;
1550    }
1551
1552    bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags);
1553
1554    if (unpremul && !GrPixelConfigIs8888(dstConfig)) {
1555        // The unpremul flag is only allowed for these two configs.
1556        return false;
1557    }
1558
1559    SkAutoTUnref<GrTexture> tempTexture;
1560
1561    // If the src is a texture and we would have to do conversions after read pixels, we instead
1562    // do the conversions by drawing the src to a scratch texture. If we handle any of the
1563    // conversions in the draw we set the corresponding bool to false so that we don't reapply it
1564    // on the read back pixels.
1565    GrTexture* src = target->asTexture();
1566    if (src && (swapRAndB || unpremul || flipY)) {
1567        // Make the scratch a render so we can read its pixels.
1568        GrSurfaceDesc desc;
1569        desc.fFlags = kRenderTarget_GrSurfaceFlag;
1570        desc.fWidth = width;
1571        desc.fHeight = height;
1572        desc.fConfig = readConfig;
1573        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
1574
1575        // When a full read back is faster than a partial we could always make the scratch exactly
1576        // match the passed rect. However, if we see many different size rectangles we will trash
1577        // our texture cache and pay the cost of creating and destroying many textures. So, we only
1578        // request an exact match when the caller is reading an entire RT.
1579        GrTextureProvider::ScratchTexMatch match = GrTextureProvider::kApprox_ScratchTexMatch;
1580        if (0 == left &&
1581            0 == top &&
1582            target->width() == width &&
1583            target->height() == height &&
1584            fGpu->fullReadPixelsIsFasterThanPartial()) {
1585            match = GrTextureProvider::kExact_ScratchTexMatch;
1586        }
1587        tempTexture.reset(this->textureProvider()->refScratchTexture(desc, match));
1588        if (tempTexture) {
1589            // compute a matrix to perform the draw
1590            SkMatrix textureMatrix;
1591            textureMatrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top);
1592            textureMatrix.postIDiv(src->width(), src->height());
1593
1594            SkAutoTUnref<const GrFragmentProcessor> fp;
1595            if (unpremul) {
1596                fp.reset(this->createPMToUPMEffect(src, swapRAndB, textureMatrix));
1597                if (fp) {
1598                    unpremul = false; // we no longer need to do this on CPU after the read back.
1599                }
1600            }
1601            // If we failed to create a PM->UPM effect and have no other conversions to perform then
1602            // there is no longer any point to using the scratch.
1603            if (fp || flipY || swapRAndB) {
1604                if (!fp) {
1605                    fp.reset(GrConfigConversionEffect::Create(
1606                            src, swapRAndB, GrConfigConversionEffect::kNone_PMConversion,
1607                            textureMatrix));
1608                }
1609                swapRAndB = false; // we will handle the swap in the draw.
1610
1611                // We protect the existing geometry here since it may not be
1612                // clear to the caller that a draw operation (i.e., drawSimpleRect)
1613                // can be invoked in this method
1614                {
1615                    GrPipelineBuilder pipelineBuilder;
1616                    SkASSERT(fp);
1617                    pipelineBuilder.addColorProcessor(fp);
1618
1619                    pipelineBuilder.setRenderTarget(tempTexture->asRenderTarget());
1620                    SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
1621                    fDrawBuffer->drawSimpleRect(&pipelineBuilder,
1622                                                GrColor_WHITE,
1623                                                SkMatrix::I(),
1624                                                rect);
1625                    // we want to read back from the scratch's origin
1626                    left = 0;
1627                    top = 0;
1628                    target = tempTexture->asRenderTarget();
1629                }
1630                this->flushSurfaceWrites(target);
1631            }
1632        }
1633    }
1634
1635    if (!fGpu->readPixels(target,
1636                          left, top, width, height,
1637                          readConfig, buffer, rowBytes)) {
1638        return false;
1639    }
1640    // Perform any conversions we weren't able to perform using a scratch texture.
1641    if (unpremul || swapRAndB) {
1642        SkDstPixelInfo dstPI;
1643        if (!GrPixelConfig2ColorAndProfileType(dstConfig, &dstPI.fColorType, NULL)) {
1644            return false;
1645        }
1646        dstPI.fAlphaType = kUnpremul_SkAlphaType;
1647        dstPI.fPixels = buffer;
1648        dstPI.fRowBytes = rowBytes;
1649
1650        SkSrcPixelInfo srcPI;
1651        srcPI.fColorType = swapRAndB ? toggle_colortype32(dstPI.fColorType) : dstPI.fColorType;
1652        srcPI.fAlphaType = kPremul_SkAlphaType;
1653        srcPI.fPixels = buffer;
1654        srcPI.fRowBytes = rowBytes;
1655
1656        return srcPI.convertPixelsTo(&dstPI, width, height);
1657    }
1658    return true;
1659}
1660
1661void GrContext::prepareSurfaceForExternalRead(GrSurface* surface) {
1662    RETURN_IF_ABANDONED
1663    SkASSERT(surface);
1664    ASSERT_OWNED_RESOURCE(surface);
1665    if (surface->surfacePriv().hasPendingIO()) {
1666        this->flush();
1667    }
1668    GrRenderTarget* rt = surface->asRenderTarget();
1669    if (fGpu && rt) {
1670        fGpu->resolveRenderTarget(rt);
1671    }
1672}
1673
1674void GrContext::discardRenderTarget(GrRenderTarget* renderTarget) {
1675    RETURN_IF_ABANDONED
1676    SkASSERT(renderTarget);
1677    ASSERT_OWNED_RESOURCE(renderTarget);
1678    AutoCheckFlush acf(this);
1679    GrDrawTarget* target = this->prepareToDraw();
1680    if (NULL == target) {
1681        return;
1682    }
1683    target->discard(renderTarget);
1684}
1685
1686void GrContext::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
1687                            const SkIPoint& dstPoint, uint32_t pixelOpsFlags) {
1688    RETURN_IF_ABANDONED
1689    if (NULL == src || NULL == dst) {
1690        return;
1691    }
1692    ASSERT_OWNED_RESOURCE(src);
1693    ASSERT_OWNED_RESOURCE(dst);
1694
1695    // Since we're going to the draw target and not GPU, no need to check kNoFlush
1696    // here.
1697
1698    GrDrawTarget* target = this->prepareToDraw();
1699    if (NULL == target) {
1700        return;
1701    }
1702    target->copySurface(dst, src, srcRect, dstPoint);
1703
1704    if (kFlushWrites_PixelOp & pixelOpsFlags) {
1705        this->flush();
1706    }
1707}
1708
1709void GrContext::flushSurfaceWrites(GrSurface* surface) {
1710    RETURN_IF_ABANDONED
1711    if (surface->surfacePriv().hasPendingWrite()) {
1712        this->flush();
1713    }
1714}
1715
1716GrDrawTarget* GrContext::prepareToDraw(GrPipelineBuilder* pipelineBuilder,
1717                                       GrRenderTarget* rt,
1718                                       const GrClip& clip,
1719                                       const GrPaint* paint,
1720                                       const AutoCheckFlush* acf) {
1721    if (NULL == fGpu || NULL == fDrawBuffer) {
1722        return NULL;
1723    }
1724
1725    ASSERT_OWNED_RESOURCE(rt);
1726    SkASSERT(rt && paint && acf);
1727    pipelineBuilder->setFromPaint(*paint, rt, clip);
1728    return fDrawBuffer;
1729}
1730
1731GrDrawTarget* GrContext::prepareToDraw() {
1732    if (NULL == fGpu) {
1733        return NULL;
1734    }
1735    return fDrawBuffer;
1736}
1737
1738/*
1739 * This method finds a path renderer that can draw the specified path on
1740 * the provided target.
1741 * Due to its expense, the software path renderer has split out so it can
1742 * can be individually allowed/disallowed via the "allowSW" boolean.
1743 */
1744GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target,
1745                                           const GrPipelineBuilder* pipelineBuilder,
1746                                           const SkMatrix& viewMatrix,
1747                                           const SkPath& path,
1748                                           const GrStrokeInfo& stroke,
1749                                           bool allowSW,
1750                                           GrPathRendererChain::DrawType drawType,
1751                                           GrPathRendererChain::StencilSupport* stencilSupport) {
1752
1753    if (NULL == fPathRendererChain) {
1754        fPathRendererChain = SkNEW_ARGS(GrPathRendererChain, (this));
1755    }
1756
1757    GrPathRenderer* pr = fPathRendererChain->getPathRenderer(target,
1758                                                             pipelineBuilder,
1759                                                             viewMatrix,
1760                                                             path,
1761                                                             stroke,
1762                                                             drawType,
1763                                                             stencilSupport);
1764
1765    if (NULL == pr && allowSW) {
1766        if (NULL == fSoftwarePathRenderer) {
1767            fSoftwarePathRenderer = SkNEW_ARGS(GrSoftwarePathRenderer, (this));
1768        }
1769        pr = fSoftwarePathRenderer;
1770    }
1771
1772    return pr;
1773}
1774
1775////////////////////////////////////////////////////////////////////////////////
1776bool GrContext::isConfigRenderable(GrPixelConfig config, bool withMSAA) const {
1777    return fGpu->caps()->isConfigRenderable(config, withMSAA);
1778}
1779
1780int GrContext::getRecommendedSampleCount(GrPixelConfig config,
1781                                         SkScalar dpi) const {
1782    if (!this->isConfigRenderable(config, true)) {
1783        return 0;
1784    }
1785    int chosenSampleCount = 0;
1786    if (fGpu->caps()->shaderCaps()->pathRenderingSupport()) {
1787        if (dpi >= 250.0f) {
1788            chosenSampleCount = 4;
1789        } else {
1790            chosenSampleCount = 16;
1791        }
1792    }
1793    return chosenSampleCount <= fGpu->caps()->maxSampleCount() ?
1794        chosenSampleCount : 0;
1795}
1796
1797GrDrawTarget* GrContext::getTextTarget() {
1798    return this->prepareToDraw();
1799}
1800
1801namespace {
1802void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) {
1803    GrConfigConversionEffect::PMConversion pmToUPM;
1804    GrConfigConversionEffect::PMConversion upmToPM;
1805    GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM);
1806    *pmToUPMValue = pmToUPM;
1807    *upmToPMValue = upmToPM;
1808}
1809}
1810
1811const GrFragmentProcessor* GrContext::createPMToUPMEffect(GrTexture* texture,
1812                                                          bool swapRAndB,
1813                                                          const SkMatrix& matrix) {
1814    if (!fDidTestPMConversions) {
1815        test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
1816        fDidTestPMConversions = true;
1817    }
1818    GrConfigConversionEffect::PMConversion pmToUPM =
1819        static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
1820    if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
1821        return GrConfigConversionEffect::Create(texture, swapRAndB, pmToUPM, matrix);
1822    } else {
1823        return NULL;
1824    }
1825}
1826
1827const GrFragmentProcessor* GrContext::createUPMToPMEffect(GrTexture* texture,
1828                                                          bool swapRAndB,
1829                                                          const SkMatrix& matrix) {
1830    if (!fDidTestPMConversions) {
1831        test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
1832        fDidTestPMConversions = true;
1833    }
1834    GrConfigConversionEffect::PMConversion upmToPM =
1835        static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
1836    if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) {
1837        return GrConfigConversionEffect::Create(texture, swapRAndB, upmToPM, matrix);
1838    } else {
1839        return NULL;
1840    }
1841}
1842
1843//////////////////////////////////////////////////////////////////////////////
1844
1845void GrContext::getResourceCacheLimits(int* maxTextures, size_t* maxTextureBytes) const {
1846    if (maxTextures) {
1847        *maxTextures = fResourceCache->getMaxResourceCount();
1848    }
1849    if (maxTextureBytes) {
1850        *maxTextureBytes = fResourceCache->getMaxResourceBytes();
1851    }
1852}
1853
1854void GrContext::setResourceCacheLimits(int maxTextures, size_t maxTextureBytes) {
1855    fResourceCache->setLimits(maxTextures, maxTextureBytes);
1856}
1857
1858//////////////////////////////////////////////////////////////////////////////
1859
1860void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) {
1861    fGpu->addGpuTraceMarker(marker);
1862    if (fDrawBuffer) {
1863        fDrawBuffer->addGpuTraceMarker(marker);
1864    }
1865}
1866
1867void GrContext::removeGpuTraceMarker(const GrGpuTraceMarker* marker) {
1868    fGpu->removeGpuTraceMarker(marker);
1869    if (fDrawBuffer) {
1870        fDrawBuffer->removeGpuTraceMarker(marker);
1871    }
1872}
1873
1874///////////////////////////////////////////////////////////////////////////////////////////////////
1875
1876#ifdef GR_TEST_UTILS
1877
1878BATCH_TEST_DEFINE(StrokeRectBatch) {
1879    StrokeRectBatch::Geometry geometry;
1880    geometry.fViewMatrix = GrTest::TestMatrix(random);
1881    geometry.fColor = GrRandomColor(random);
1882    geometry.fRect = GrTest::TestRect(random);
1883    geometry.fStrokeWidth = random->nextBool() ? 0.0f : 1.0f;
1884
1885    return StrokeRectBatch::Create(geometry, random->nextBool());
1886}
1887
1888static uint32_t seed_vertices(GrPrimitiveType type) {
1889    switch (type) {
1890        case kTriangles_GrPrimitiveType:
1891        case kTriangleStrip_GrPrimitiveType:
1892        case kTriangleFan_GrPrimitiveType:
1893            return 3;
1894        case kPoints_GrPrimitiveType:
1895            return 1;
1896        case kLines_GrPrimitiveType:
1897        case kLineStrip_GrPrimitiveType:
1898            return 2;
1899    }
1900    SkFAIL("Incomplete switch\n");
1901    return 0;
1902}
1903
1904static uint32_t primitive_vertices(GrPrimitiveType type) {
1905    switch (type) {
1906        case kTriangles_GrPrimitiveType:
1907            return 3;
1908        case kLines_GrPrimitiveType:
1909            return 2;
1910        case kTriangleStrip_GrPrimitiveType:
1911        case kTriangleFan_GrPrimitiveType:
1912        case kPoints_GrPrimitiveType:
1913        case kLineStrip_GrPrimitiveType:
1914            return 1;
1915    }
1916    SkFAIL("Incomplete switch\n");
1917    return 0;
1918}
1919
1920static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
1921    SkPoint p;
1922    p.fX = random->nextRangeScalar(min, max);
1923    p.fY = random->nextRangeScalar(min, max);
1924    return p;
1925}
1926
1927static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
1928                             SkRandom* random,
1929                             SkTArray<SkPoint>* positions,
1930                             SkTArray<SkPoint>* texCoords, bool hasTexCoords,
1931                             SkTArray<GrColor>* colors, bool hasColors,
1932                             SkTArray<uint16_t>* indices, bool hasIndices) {
1933    for (uint32_t v = 0; v < count; v++) {
1934        positions->push_back(random_point(random, min, max));
1935        if (hasTexCoords) {
1936            texCoords->push_back(random_point(random, min, max));
1937        }
1938        if (hasColors) {
1939            colors->push_back(GrRandomColor(random));
1940        }
1941        if (hasIndices) {
1942            SkASSERT(maxVertex <= SK_MaxU16);
1943            indices->push_back(random->nextULessThan((uint16_t)maxVertex));
1944        }
1945    }
1946}
1947
1948BATCH_TEST_DEFINE(VerticesBatch) {
1949    GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1));
1950    uint32_t primitiveCount = random->nextRangeU(1, 100);
1951
1952    // TODO make 'sensible' indexbuffers
1953    SkTArray<SkPoint> positions;
1954    SkTArray<SkPoint> texCoords;
1955    SkTArray<GrColor> colors;
1956    SkTArray<uint16_t> indices;
1957
1958    bool hasTexCoords = random->nextBool();
1959    bool hasIndices = random->nextBool();
1960    bool hasColors = random->nextBool();
1961
1962    uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
1963
1964    static const SkScalar kMinVertExtent = -100.f;
1965    static const SkScalar kMaxVertExtent = 100.f;
1966    randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
1967                     random,
1968                     &positions,
1969                     &texCoords, hasTexCoords,
1970                     &colors, hasColors,
1971                     &indices, hasIndices);
1972
1973    for (uint32_t i = 1; i < primitiveCount; i++) {
1974        randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
1975                         random,
1976                         &positions,
1977                         &texCoords, hasTexCoords,
1978                         &colors, hasColors,
1979                         &indices, hasIndices);
1980    }
1981
1982    SkMatrix viewMatrix = GrTest::TestMatrix(random);
1983    SkRect bounds;
1984    SkDEBUGCODE(bool result = ) bounds.setBoundsCheck(positions.begin(), vertexCount);
1985    SkASSERT(result);
1986
1987    viewMatrix.mapRect(&bounds);
1988
1989    DrawVerticesBatch::Geometry geometry;
1990    geometry.fColor = GrRandomColor(random);
1991    return DrawVerticesBatch::Create(geometry, type, viewMatrix,
1992                                     positions.begin(), vertexCount,
1993                                     indices.begin(), hasIndices ? vertexCount : 0,
1994                                     colors.begin(),
1995                                     texCoords.begin(),
1996                                     bounds);
1997}
1998
1999#endif
2000