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