1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef PictureRenderer_DEFINED
9#define PictureRenderer_DEFINED
10
11#include "SkCanvas.h"
12#include "SkDrawFilter.h"
13#include "SkJSONCPP.h"
14#include "SkMath.h"
15#include "SkPaint.h"
16#include "SkPicture.h"
17#include "SkPictureRecorder.h"
18#include "SkRect.h"
19#include "SkRefCnt.h"
20#include "SkString.h"
21#include "SkTDArray.h"
22#include "SkTypes.h"
23
24#if SK_SUPPORT_GPU
25#include "GrContextFactory.h"
26#include "GrContext.h"
27#endif
28
29#include "image_expectations.h"
30
31class SkBitmap;
32class SkCanvas;
33class SkGLContextHelper;
34class SkThread;
35
36namespace sk_tools {
37
38class TiledPictureRenderer;
39
40class PictureRenderer : public SkRefCnt {
41
42public:
43    enum SkDeviceTypes {
44#if SK_ANGLE
45        kAngle_DeviceType,
46#endif
47#if SK_MESA
48        kMesa_DeviceType,
49#endif
50        kBitmap_DeviceType,
51#if SK_SUPPORT_GPU
52        kGPU_DeviceType,
53        kNVPR_DeviceType,
54#endif
55    };
56
57    enum BBoxHierarchyType {
58        kNone_BBoxHierarchyType = 0,
59        kRTree_BBoxHierarchyType,
60        kTileGrid_BBoxHierarchyType,
61
62        kLast_BBoxHierarchyType = kTileGrid_BBoxHierarchyType,
63    };
64
65    // this uses SkPaint::Flags as a base and adds additional flags
66    enum DrawFilterFlags {
67        kNone_DrawFilterFlag = 0,
68        kHinting_DrawFilterFlag = 0x10000, // toggles between no hinting and normal hinting
69        kSlightHinting_DrawFilterFlag = 0x20000, // toggles between slight and normal hinting
70        kAAClip_DrawFilterFlag = 0x40000, // toggles between soft and hard clip
71        kMaskFilter_DrawFilterFlag = 0x80000, // toggles on/off mask filters (e.g., blurs)
72    };
73
74    SK_COMPILE_ASSERT(!(kMaskFilter_DrawFilterFlag & SkPaint::kAllFlags), maskfilter_flag_must_be_greater);
75    SK_COMPILE_ASSERT(!(kHinting_DrawFilterFlag & SkPaint::kAllFlags),
76            hinting_flag_must_be_greater);
77    SK_COMPILE_ASSERT(!(kSlightHinting_DrawFilterFlag & SkPaint::kAllFlags),
78            slight_hinting_flag_must_be_greater);
79
80    /**
81     * Called with each new SkPicture to render.
82     *
83     * @param pict The SkPicture to render.
84     * @param writePath The output directory within which this renderer should write all images,
85     *     or NULL if this renderer should not write all images.
86     * @param mismatchPath The output directory within which this renderer should write any images
87     *     which do not match expectations, or NULL if this renderer should not write mismatches.
88     * @param inputFilename The name of the input file we are rendering.
89     * @param useChecksumBasedFilenames Whether to use checksum-based filenames when writing
90     *     bitmap images to disk.
91     */
92    virtual void init(const SkPicture* pict,
93                      const SkString* writePath,
94                      const SkString* mismatchPath,
95                      const SkString* inputFilename,
96                      bool useChecksumBasedFilenames);
97
98    /**
99     * TODO(epoger): Temporary hack, while we work on http://skbug.com/2584 ('bench_pictures is
100     * timing reading pixels and writing json files'), such that:
101     * - render_pictures can call this method and continue to work
102     * - any other callers (bench_pictures) will skip calls to write() by default
103     */
104    void enableWrites() { fEnableWrites = true; }
105
106    /**
107     *  Set the viewport so that only the portion listed gets drawn.
108     */
109    void setViewport(SkISize size) { fViewport = size; }
110
111    /**
112     *  Set the scale factor at which draw the picture.
113     */
114    void setScaleFactor(SkScalar scale) { fScaleFactor = scale; }
115
116    /**
117     * Perform any setup that should done prior to each iteration of render() which should not be
118     * timed.
119     */
120    virtual void setup() {}
121
122    /**
123     * Perform the work.  If this is being called within the context of bench_pictures,
124     * this is the step that will be timed.
125     *
126     * Typically "the work" is rendering an SkPicture into a bitmap, but in some subclasses
127     * it is recording the source SkPicture into another SkPicture.
128     *
129     * If fWritePath has been specified, the result of the work will be written to that dir.
130     * If fMismatchPath has been specified, and the actual image result differs from its
131     * expectation, the result of the work will be written to that dir.
132     *
133     * @param out If non-null, the implementing subclass MAY allocate an SkBitmap, copy the
134     *            output image into it, and return it here.  (Some subclasses ignore this parameter)
135     * @return bool True if rendering succeeded and, if fWritePath had been specified, the output
136     *              was successfully written to a file.
137     */
138    virtual bool render(SkBitmap** out = NULL) = 0;
139
140    /**
141     * Called once finished with a particular SkPicture, before calling init again, and before
142     * being done with this Renderer.
143     */
144    virtual void end();
145
146    /**
147     * If this PictureRenderer is actually a TiledPictureRender, return a pointer to this as a
148     * TiledPictureRender so its methods can be called.
149     */
150    virtual TiledPictureRenderer* getTiledRenderer() { return NULL; }
151
152    /**
153     * Resets the GPU's state. Does nothing if the backing is raster. For a GPU renderer, calls
154     * flush, swapBuffers and, if callFinish is true, finish.
155     * @param callFinish Whether to call finish.
156     */
157    void resetState(bool callFinish);
158
159    /**
160     * Remove all decoded textures from the CPU caches and all uploaded textures
161     * from the GPU.
162     */
163    void purgeTextures();
164
165    /**
166     * Set the backend type. Returns true on success and false on failure.
167     */
168#if SK_SUPPORT_GPU
169    bool setDeviceType(SkDeviceTypes deviceType, GrGLStandard gpuAPI = kNone_GrGLStandard) {
170#else
171    bool setDeviceType(SkDeviceTypes deviceType) {
172#endif
173        fDeviceType = deviceType;
174#if SK_SUPPORT_GPU
175        // In case this function is called more than once
176        SkSafeUnref(fGrContext);
177        fGrContext = NULL;
178        // Set to Native so it will have an initial value.
179        GrContextFactory::GLContextType glContextType = GrContextFactory::kNative_GLContextType;
180#endif
181        switch(deviceType) {
182            case kBitmap_DeviceType:
183                return true;
184#if SK_SUPPORT_GPU
185            case kGPU_DeviceType:
186                // Already set to GrContextFactory::kNative_GLContextType, above.
187                break;
188            case kNVPR_DeviceType:
189                glContextType = GrContextFactory::kNVPR_GLContextType;
190                break;
191#if SK_ANGLE
192            case kAngle_DeviceType:
193                glContextType = GrContextFactory::kANGLE_GLContextType;
194                break;
195#endif
196#if SK_MESA
197            case kMesa_DeviceType:
198                glContextType = GrContextFactory::kMESA_GLContextType;
199                break;
200#endif
201#endif
202            default:
203                // Invalid device type.
204                return false;
205        }
206#if SK_SUPPORT_GPU
207        fGrContext = fGrContextFactory.get(glContextType, gpuAPI);
208        if (NULL == fGrContext) {
209            return false;
210        } else {
211            fGrContext->ref();
212            return true;
213        }
214#endif
215    }
216
217#if SK_SUPPORT_GPU
218    void setSampleCount(int sampleCount) {
219        fSampleCount = sampleCount;
220    }
221#endif
222
223    void setDrawFilters(DrawFilterFlags const * const filters, const SkString& configName) {
224        memcpy(fDrawFilters, filters, sizeof(fDrawFilters));
225        fDrawFiltersConfig = configName;
226    }
227
228    void setBBoxHierarchyType(BBoxHierarchyType bbhType) {
229        fBBoxHierarchyType = bbhType;
230    }
231
232    BBoxHierarchyType getBBoxHierarchyType() { return fBBoxHierarchyType; }
233
234    void setGridSize(int width, int height) {
235        fGridInfo.fTileInterval.set(width, height);
236    }
237
238    void setJsonSummaryPtr(ImageResultsAndExpectations* jsonSummaryPtr) {
239        fJsonSummaryPtr = jsonSummaryPtr;
240    }
241
242    bool isUsingBitmapDevice() {
243        return kBitmap_DeviceType == fDeviceType;
244    }
245
246    virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); }
247
248    virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); }
249
250    /**
251     * Reports the configuration of this PictureRenderer.
252     */
253    SkString getConfigName() {
254        SkString config = this->getConfigNameInternal();
255        if (!fViewport.isEmpty()) {
256            config.appendf("_viewport_%ix%i", fViewport.width(), fViewport.height());
257        }
258        if (fScaleFactor != SK_Scalar1) {
259            config.appendf("_scalar_%f", SkScalarToFloat(fScaleFactor));
260        }
261        if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) {
262            config.append("_rtree");
263        } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) {
264            config.append("_grid");
265            config.append("_");
266            config.appendS32(fGridInfo.fTileInterval.width());
267            config.append("x");
268            config.appendS32(fGridInfo.fTileInterval.height());
269        }
270#if SK_SUPPORT_GPU
271        switch (fDeviceType) {
272            case kGPU_DeviceType:
273                if (fSampleCount) {
274                    config.appendf("_msaa%d", fSampleCount);
275                } else {
276                    config.append("_gpu");
277                }
278                break;
279            case kNVPR_DeviceType:
280                config.appendf("_nvprmsaa%d", fSampleCount);
281                break;
282#if SK_ANGLE
283            case kAngle_DeviceType:
284                config.append("_angle");
285                break;
286#endif
287#if SK_MESA
288            case kMesa_DeviceType:
289                config.append("_mesa");
290                break;
291#endif
292            default:
293                // Assume that no extra info means bitmap.
294                break;
295        }
296#endif
297        config.append(fDrawFiltersConfig.c_str());
298        return config;
299    }
300
301    Json::Value getJSONConfig() {
302        Json::Value result;
303
304        result["mode"] = this->getConfigNameInternal().c_str();
305        result["scale"] = 1.0f;
306        if (SK_Scalar1 != fScaleFactor) {
307            result["scale"] = SkScalarToFloat(fScaleFactor);
308        }
309        if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) {
310            result["bbh"] = "rtree";
311        } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) {
312            SkString tmp("grid_");
313            tmp.appendS32(fGridInfo.fTileInterval.width());
314            tmp.append("x");
315            tmp.appendS32(fGridInfo.fTileInterval.height());
316            result["bbh"] = tmp.c_str();
317        }
318#if SK_SUPPORT_GPU
319        SkString tmp;
320        switch (fDeviceType) {
321            case kGPU_DeviceType:
322                if (0 != fSampleCount) {
323                    tmp = "msaa";
324                    tmp.appendS32(fSampleCount);
325                    result["config"] = tmp.c_str();
326                } else {
327                    result["config"] = "gpu";
328                }
329                break;
330            case kNVPR_DeviceType:
331                tmp = "nvprmsaa";
332                tmp.appendS32(fSampleCount);
333                result["config"] = tmp.c_str();
334                break;
335#if SK_ANGLE
336            case kAngle_DeviceType:
337                result["config"] = "angle";
338                break;
339#endif
340#if SK_MESA
341            case kMesa_DeviceType:
342                result["config"] = "mesa";
343                break;
344#endif
345            default:
346                // Assume that no extra info means bitmap.
347                break;
348        }
349#endif
350        return result;
351    }
352
353#if SK_SUPPORT_GPU
354    bool isUsingGpuDevice() {
355        switch (fDeviceType) {
356            case kGPU_DeviceType:
357            case kNVPR_DeviceType:
358                // fall through
359#if SK_ANGLE
360            case kAngle_DeviceType:
361                // fall through
362#endif
363#if SK_MESA
364            case kMesa_DeviceType:
365#endif
366                return true;
367            default:
368                return false;
369        }
370    }
371
372    SkGLContextHelper* getGLContext() {
373        GrContextFactory::GLContextType glContextType
374                = GrContextFactory::kNull_GLContextType;
375        switch(fDeviceType) {
376            case kGPU_DeviceType:
377                glContextType = GrContextFactory::kNative_GLContextType;
378                break;
379            case kNVPR_DeviceType:
380                glContextType = GrContextFactory::kNVPR_GLContextType;
381                break;
382#if SK_ANGLE
383            case kAngle_DeviceType:
384                glContextType = GrContextFactory::kANGLE_GLContextType;
385                break;
386#endif
387#if SK_MESA
388            case kMesa_DeviceType:
389                glContextType = GrContextFactory::kMESA_GLContextType;
390                break;
391#endif
392            default:
393                return NULL;
394        }
395        return fGrContextFactory.getGLContext(glContextType);
396    }
397
398    GrContext* getGrContext() {
399        return fGrContext;
400    }
401
402    const GrContext::Options& getGrContextOptions() {
403        return fGrContextFactory.getGlobalOptions();
404    }
405#endif
406
407    SkCanvas* getCanvas() {
408        return fCanvas;
409    }
410
411    const SkPicture* getPicture() {
412        return fPicture;
413    }
414
415#if SK_SUPPORT_GPU
416    explicit PictureRenderer(const GrContext::Options &opts)
417#else
418    PictureRenderer()
419#endif
420        : fJsonSummaryPtr(NULL)
421        , fDeviceType(kBitmap_DeviceType)
422        , fEnableWrites(false)
423        , fBBoxHierarchyType(kNone_BBoxHierarchyType)
424        , fScaleFactor(SK_Scalar1)
425#if SK_SUPPORT_GPU
426        , fGrContextFactory(opts)
427        , fGrContext(NULL)
428        , fSampleCount(0)
429#endif
430        {
431            fGridInfo.fMargin.setEmpty();
432            fGridInfo.fOffset.setZero();
433            fGridInfo.fTileInterval.set(1, 1);
434            sk_bzero(fDrawFilters, sizeof(fDrawFilters));
435            fViewport.set(0, 0);
436        }
437
438#if SK_SUPPORT_GPU
439    virtual ~PictureRenderer() {
440        SkSafeUnref(fGrContext);
441    }
442#endif
443
444protected:
445    SkAutoTUnref<SkCanvas> fCanvas;
446    SkAutoTUnref<const SkPicture> fPicture;
447    bool                   fUseChecksumBasedFilenames;
448    ImageResultsAndExpectations*   fJsonSummaryPtr;
449    SkDeviceTypes          fDeviceType;
450    bool                   fEnableWrites;
451    BBoxHierarchyType      fBBoxHierarchyType;
452    DrawFilterFlags        fDrawFilters[SkDrawFilter::kTypeCount];
453    SkString               fDrawFiltersConfig;
454    SkString               fWritePath;
455    SkString               fMismatchPath;
456    SkString               fInputFilename;
457    SkTileGridFactory::TileGridInfo fGridInfo; // used when fBBoxHierarchyType is TileGrid
458
459    void buildBBoxHierarchy();
460
461    /**
462     * Return the total width that should be drawn. If the viewport width has been set greater than
463     * 0, this will be the minimum of the current SkPicture's width and the viewport's width.
464     */
465    int getViewWidth();
466
467    /**
468     * Return the total height that should be drawn. If the viewport height has been set greater
469     * than 0, this will be the minimum of the current SkPicture's height and the viewport's height.
470     */
471    int getViewHeight();
472
473    /**
474     * Scales the provided canvas to the scale factor set by setScaleFactor.
475     */
476    void scaleToScaleFactor(SkCanvas*);
477
478    SkBBHFactory* getFactory();
479    uint32_t recordFlags() const { return 0; }
480    SkCanvas* setupCanvas();
481    virtual SkCanvas* setupCanvas(int width, int height);
482
483    /**
484     * Copy src to dest; if src==NULL, set dest to empty string.
485     */
486    static void CopyString(SkString* dest, const SkString* src);
487
488private:
489    SkISize                fViewport;
490    SkScalar               fScaleFactor;
491#if SK_SUPPORT_GPU
492    GrContextFactory       fGrContextFactory;
493    GrContext*             fGrContext;
494    int                    fSampleCount;
495#endif
496
497    virtual SkString getConfigNameInternal() = 0;
498
499    typedef SkRefCnt INHERITED;
500};
501
502/**
503 * This class does not do any rendering, but its render function executes recording, which we want
504 * to time.
505 */
506class RecordPictureRenderer : public PictureRenderer {
507public:
508#if SK_SUPPORT_GPU
509    RecordPictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
510#endif
511
512    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
513
514    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
515
516    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
517
518protected:
519    virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
520
521private:
522    virtual SkString getConfigNameInternal() SK_OVERRIDE;
523
524    typedef PictureRenderer INHERITED;
525};
526
527class PipePictureRenderer : public PictureRenderer {
528public:
529#if SK_SUPPORT_GPU
530    PipePictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
531#endif
532
533    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
534
535private:
536    virtual SkString getConfigNameInternal() SK_OVERRIDE;
537
538    typedef PictureRenderer INHERITED;
539};
540
541class SimplePictureRenderer : public PictureRenderer {
542public:
543#if SK_SUPPORT_GPU
544    SimplePictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
545#endif
546
547    virtual void init(const SkPicture* pict,
548                      const SkString* writePath,
549                      const SkString* mismatchPath,
550                      const SkString* inputFilename,
551                      bool useChecksumBasedFilenames) SK_OVERRIDE;
552
553    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
554
555private:
556    virtual SkString getConfigNameInternal() SK_OVERRIDE;
557
558    typedef PictureRenderer INHERITED;
559};
560
561class TiledPictureRenderer : public PictureRenderer {
562public:
563#if SK_SUPPORT_GPU
564    TiledPictureRenderer(const GrContext::Options &opts);
565#else
566    TiledPictureRenderer();
567#endif
568
569    virtual void init(const SkPicture* pict,
570                      const SkString* writePath,
571                      const SkString* mismatchPath,
572                      const SkString* inputFilename,
573                      bool useChecksumBasedFilenames) SK_OVERRIDE;
574
575    /**
576     * Renders to tiles, rather than a single canvas.
577     * If fWritePath was provided, a separate file is
578     * created for each tile, named "path0.png", "path1.png", etc.
579     */
580    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
581
582    virtual void end() SK_OVERRIDE;
583
584    void setTileWidth(int width) {
585        fTileWidth = width;
586    }
587
588    int getTileWidth() const {
589        return fTileWidth;
590    }
591
592    void setTileHeight(int height) {
593        fTileHeight = height;
594    }
595
596    int getTileHeight() const {
597        return fTileHeight;
598    }
599
600    void setTileWidthPercentage(double percentage) {
601        fTileWidthPercentage = percentage;
602    }
603
604    double getTileWidthPercentage() const {
605        return fTileWidthPercentage;
606    }
607
608    void setTileHeightPercentage(double percentage) {
609        fTileHeightPercentage = percentage;
610    }
611
612    double getTileHeightPercentage() const {
613        return fTileHeightPercentage;
614    }
615
616    void setTileMinPowerOf2Width(int width) {
617        SkASSERT(SkIsPow2(width) && width > 0);
618        if (!SkIsPow2(width) || width <= 0) {
619            return;
620        }
621
622        fTileMinPowerOf2Width = width;
623    }
624
625    int getTileMinPowerOf2Width() const {
626        return fTileMinPowerOf2Width;
627    }
628
629    virtual TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; }
630
631    virtual bool supportsTimingIndividualTiles() { return true; }
632
633    /**
634     * Report the number of tiles in the x and y directions. Must not be called before init.
635     * @param x Output parameter identifying the number of tiles in the x direction.
636     * @param y Output parameter identifying the number of tiles in the y direction.
637     * @return True if the tiles have been set up, and x and y are meaningful. If false, x and y are
638     *         unmodified.
639     */
640    bool tileDimensions(int& x, int&y);
641
642    /**
643     * Move to the next tile and return its indices. Must be called before calling drawCurrentTile
644     * for the first time.
645     * @param i Output parameter identifying the column of the next tile to be drawn on the next
646     *          call to drawNextTile.
647     * @param j Output parameter identifying the row  of the next tile to be drawn on the next call
648     *          to drawNextTile.
649     * @param True if the tiles have been created and the next tile to be drawn by drawCurrentTile
650     *        is within the range of tiles. If false, i and j are unmodified.
651     */
652    bool nextTile(int& i, int& j);
653
654    /**
655     * Render one tile. This will draw the same tile each time it is called until nextTile is
656     * called. The tile rendered will depend on how many calls have been made to nextTile.
657     * It is an error to call this without first calling nextTile, or if nextTile returns false.
658     */
659    void drawCurrentTile();
660
661protected:
662    SkTDArray<SkRect> fTileRects;
663
664    virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
665    virtual SkString getConfigNameInternal() SK_OVERRIDE;
666
667private:
668    int    fTileWidth;
669    int    fTileHeight;
670    double fTileWidthPercentage;
671    double fTileHeightPercentage;
672    int    fTileMinPowerOf2Width;
673
674    // These variables are only used for timing individual tiles.
675    // Next tile to draw in fTileRects.
676    int    fCurrentTileOffset;
677    // Number of tiles in the x direction.
678    int    fTilesX;
679    // Number of tiles in the y direction.
680    int    fTilesY;
681
682    void setupTiles();
683    void setupPowerOf2Tiles();
684
685    typedef PictureRenderer INHERITED;
686};
687
688/**
689 * This class does not do any rendering, but its render function executes turning an SkPictureRecord
690 * into an SkPicturePlayback, which we want to time.
691 */
692class PlaybackCreationRenderer : public PictureRenderer {
693public:
694#if SK_SUPPORT_GPU
695    PlaybackCreationRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
696#endif
697
698    virtual void setup() SK_OVERRIDE;
699
700    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
701
702    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
703
704    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
705
706private:
707    SkAutoTDelete<SkPictureRecorder> fRecorder;
708
709    virtual SkString getConfigNameInternal() SK_OVERRIDE;
710
711    typedef PictureRenderer INHERITED;
712};
713
714#if SK_SUPPORT_GPU
715extern PictureRenderer* CreateGatherPixelRefsRenderer(const GrContext::Options& opts);
716#else
717extern PictureRenderer* CreateGatherPixelRefsRenderer();
718#endif
719
720}
721
722#endif  // PictureRenderer_DEFINED
723