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 "SkCountdown.h"
13#include "SkDrawFilter.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 "SkRunnable.h"
21#include "SkString.h"
22#include "SkTDArray.h"
23#include "SkThreadPool.h"
24#include "SkTypes.h"
25
26#if SK_SUPPORT_GPU
27#include "GrContextFactory.h"
28#include "GrContext.h"
29#endif
30
31#include "image_expectations.h"
32
33class SkBitmap;
34class SkCanvas;
35class SkGLContextHelper;
36class SkThread;
37
38namespace sk_tools {
39
40class TiledPictureRenderer;
41
42class PictureRenderer : public SkRefCnt {
43
44public:
45    enum SkDeviceTypes {
46#if SK_ANGLE
47        kAngle_DeviceType,
48#endif
49#if SK_MESA
50        kMesa_DeviceType,
51#endif
52        kBitmap_DeviceType,
53#if SK_SUPPORT_GPU
54        kGPU_DeviceType,
55        kNVPR_DeviceType,
56#endif
57    };
58
59    enum BBoxHierarchyType {
60        kNone_BBoxHierarchyType = 0,
61        kQuadTree_BBoxHierarchyType,
62        kRTree_BBoxHierarchyType,
63        kTileGrid_BBoxHierarchyType,
64
65        kLast_BBoxHierarchyType = kTileGrid_BBoxHierarchyType,
66    };
67
68    // this uses SkPaint::Flags as a base and adds additional flags
69    enum DrawFilterFlags {
70        kNone_DrawFilterFlag = 0,
71        kHinting_DrawFilterFlag = 0x10000, // toggles between no hinting and normal hinting
72        kSlightHinting_DrawFilterFlag = 0x20000, // toggles between slight and normal hinting
73        kAAClip_DrawFilterFlag = 0x40000, // toggles between soft and hard clip
74        kMaskFilter_DrawFilterFlag = 0x80000, // toggles on/off mask filters (e.g., blurs)
75    };
76
77    SK_COMPILE_ASSERT(!(kMaskFilter_DrawFilterFlag & SkPaint::kAllFlags), maskfilter_flag_must_be_greater);
78    SK_COMPILE_ASSERT(!(kHinting_DrawFilterFlag & SkPaint::kAllFlags),
79            hinting_flag_must_be_greater);
80    SK_COMPILE_ASSERT(!(kSlightHinting_DrawFilterFlag & SkPaint::kAllFlags),
81            slight_hinting_flag_must_be_greater);
82
83    /**
84     * Called with each new SkPicture to render.
85     *
86     * @param pict The SkPicture to render.
87     * @param writePath The output directory within which this renderer should write all images,
88     *     or NULL if this renderer should not write all images.
89     * @param mismatchPath The output directory within which this renderer should write any images
90     *     which do not match expectations, or NULL if this renderer should not write mismatches.
91     * @param inputFilename The name of the input file we are rendering.
92     * @param useChecksumBasedFilenames Whether to use checksum-based filenames when writing
93     *     bitmap images to disk.
94     */
95    virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath,
96                      const SkString* inputFilename, 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    bool setDeviceType(SkDeviceTypes deviceType) {
169        fDeviceType = deviceType;
170#if SK_SUPPORT_GPU
171        // In case this function is called more than once
172        SkSafeUnref(fGrContext);
173        fGrContext = NULL;
174        // Set to Native so it will have an initial value.
175        GrContextFactory::GLContextType glContextType = GrContextFactory::kNative_GLContextType;
176#endif
177        switch(deviceType) {
178            case kBitmap_DeviceType:
179                return true;
180#if SK_SUPPORT_GPU
181            case kGPU_DeviceType:
182                // Already set to GrContextFactory::kNative_GLContextType, above.
183                break;
184            case kNVPR_DeviceType:
185                glContextType = GrContextFactory::kNVPR_GLContextType;
186                break;
187#if SK_ANGLE
188            case kAngle_DeviceType:
189                glContextType = GrContextFactory::kANGLE_GLContextType;
190                break;
191#endif
192#if SK_MESA
193            case kMesa_DeviceType:
194                glContextType = GrContextFactory::kMESA_GLContextType;
195                break;
196#endif
197#endif
198            default:
199                // Invalid device type.
200                return false;
201        }
202#if SK_SUPPORT_GPU
203        fGrContext = fGrContextFactory.get(glContextType);
204        if (NULL == fGrContext) {
205            return false;
206        } else {
207            fGrContext->ref();
208            return true;
209        }
210#endif
211    }
212
213#if SK_SUPPORT_GPU
214    void setSampleCount(int sampleCount) {
215        fSampleCount = sampleCount;
216    }
217#endif
218
219    void setDrawFilters(DrawFilterFlags const * const filters, const SkString& configName) {
220        memcpy(fDrawFilters, filters, sizeof(fDrawFilters));
221        fDrawFiltersConfig = configName;
222    }
223
224    void setBBoxHierarchyType(BBoxHierarchyType bbhType) {
225        fBBoxHierarchyType = bbhType;
226    }
227
228    BBoxHierarchyType getBBoxHierarchyType() { return fBBoxHierarchyType; }
229
230    void setGridSize(int width, int height) {
231        fGridInfo.fTileInterval.set(width, height);
232    }
233
234    void setJsonSummaryPtr(ImageResultsAndExpectations* jsonSummaryPtr) {
235        fJsonSummaryPtr = jsonSummaryPtr;
236    }
237
238    bool isUsingBitmapDevice() {
239        return kBitmap_DeviceType == fDeviceType;
240    }
241
242    virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); }
243
244    virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); }
245
246    /**
247     * Reports the configuration of this PictureRenderer.
248     */
249    SkString getConfigName() {
250        SkString config = this->getConfigNameInternal();
251        if (!fViewport.isEmpty()) {
252            config.appendf("_viewport_%ix%i", fViewport.width(), fViewport.height());
253        }
254        if (fScaleFactor != SK_Scalar1) {
255            config.appendf("_scalar_%f", SkScalarToFloat(fScaleFactor));
256        }
257        if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) {
258            config.append("_rtree");
259        } else if (kQuadTree_BBoxHierarchyType == fBBoxHierarchyType) {
260            config.append("_quadtree");
261        } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) {
262            config.append("_grid");
263            config.append("_");
264            config.appendS32(fGridInfo.fTileInterval.width());
265            config.append("x");
266            config.appendS32(fGridInfo.fTileInterval.height());
267        }
268#if SK_SUPPORT_GPU
269        switch (fDeviceType) {
270            case kGPU_DeviceType:
271                if (fSampleCount) {
272                    config.appendf("_msaa%d", fSampleCount);
273                } else {
274                    config.append("_gpu");
275                }
276                break;
277            case kNVPR_DeviceType:
278                config.appendf("_nvprmsaa%d", fSampleCount);
279                break;
280#if SK_ANGLE
281            case kAngle_DeviceType:
282                config.append("_angle");
283                break;
284#endif
285#if SK_MESA
286            case kMesa_DeviceType:
287                config.append("_mesa");
288                break;
289#endif
290            default:
291                // Assume that no extra info means bitmap.
292                break;
293        }
294#endif
295        config.append(fDrawFiltersConfig.c_str());
296        return config;
297    }
298
299#if SK_SUPPORT_GPU
300    bool isUsingGpuDevice() {
301        switch (fDeviceType) {
302            case kGPU_DeviceType:
303            case kNVPR_DeviceType:
304                // fall through
305#if SK_ANGLE
306            case kAngle_DeviceType:
307                // fall through
308#endif
309#if SK_MESA
310            case kMesa_DeviceType:
311#endif
312                return true;
313            default:
314                return false;
315        }
316    }
317
318    SkGLContextHelper* getGLContext() {
319        GrContextFactory::GLContextType glContextType
320                = GrContextFactory::kNull_GLContextType;
321        switch(fDeviceType) {
322            case kGPU_DeviceType:
323                glContextType = GrContextFactory::kNative_GLContextType;
324                break;
325            case kNVPR_DeviceType:
326                glContextType = GrContextFactory::kNVPR_GLContextType;
327                break;
328#if SK_ANGLE
329            case kAngle_DeviceType:
330                glContextType = GrContextFactory::kANGLE_GLContextType;
331                break;
332#endif
333#if SK_MESA
334            case kMesa_DeviceType:
335                glContextType = GrContextFactory::kMESA_GLContextType;
336                break;
337#endif
338            default:
339                return NULL;
340        }
341        return fGrContextFactory.getGLContext(glContextType);
342    }
343
344    GrContext* getGrContext() {
345        return fGrContext;
346    }
347#endif
348
349    SkCanvas* getCanvas() {
350        return fCanvas;
351    }
352
353    SkPicture* getPicture() {
354        return fPicture;
355    }
356
357    PictureRenderer()
358        : fJsonSummaryPtr(NULL)
359        , fDeviceType(kBitmap_DeviceType)
360        , fEnableWrites(false)
361        , fBBoxHierarchyType(kNone_BBoxHierarchyType)
362        , fScaleFactor(SK_Scalar1)
363#if SK_SUPPORT_GPU
364        , fGrContext(NULL)
365        , fSampleCount(0)
366#endif
367        {
368            fGridInfo.fMargin.setEmpty();
369            fGridInfo.fOffset.setZero();
370            fGridInfo.fTileInterval.set(1, 1);
371            sk_bzero(fDrawFilters, sizeof(fDrawFilters));
372            fViewport.set(0, 0);
373        }
374
375#if SK_SUPPORT_GPU
376    virtual ~PictureRenderer() {
377        SkSafeUnref(fGrContext);
378    }
379#endif
380
381protected:
382    SkAutoTUnref<SkCanvas> fCanvas;
383    SkAutoTUnref<SkPicture> fPicture;
384    bool                   fUseChecksumBasedFilenames;
385    ImageResultsAndExpectations*   fJsonSummaryPtr;
386    SkDeviceTypes          fDeviceType;
387    bool                   fEnableWrites;
388    BBoxHierarchyType      fBBoxHierarchyType;
389    DrawFilterFlags        fDrawFilters[SkDrawFilter::kTypeCount];
390    SkString               fDrawFiltersConfig;
391    SkString               fWritePath;
392    SkString               fMismatchPath;
393    SkString               fInputFilename;
394    SkTileGridFactory::TileGridInfo fGridInfo; // used when fBBoxHierarchyType is TileGrid
395
396    void buildBBoxHierarchy();
397
398    /**
399     * Return the total width that should be drawn. If the viewport width has been set greater than
400     * 0, this will be the minimum of the current SkPicture's width and the viewport's width.
401     */
402    int getViewWidth();
403
404    /**
405     * Return the total height that should be drawn. If the viewport height has been set greater
406     * than 0, this will be the minimum of the current SkPicture's height and the viewport's height.
407     */
408    int getViewHeight();
409
410    /**
411     * Scales the provided canvas to the scale factor set by setScaleFactor.
412     */
413    void scaleToScaleFactor(SkCanvas*);
414
415    SkBBHFactory* getFactory();
416    uint32_t recordFlags() const { return 0; }
417    SkCanvas* setupCanvas();
418    virtual SkCanvas* setupCanvas(int width, int height);
419
420    /**
421     * Copy src to dest; if src==NULL, set dest to empty string.
422     */
423    static void CopyString(SkString* dest, const SkString* src);
424
425private:
426    SkISize                fViewport;
427    SkScalar               fScaleFactor;
428#if SK_SUPPORT_GPU
429    GrContextFactory       fGrContextFactory;
430    GrContext*             fGrContext;
431    int                    fSampleCount;
432#endif
433
434    virtual SkString getConfigNameInternal() = 0;
435
436    typedef SkRefCnt INHERITED;
437};
438
439/**
440 * This class does not do any rendering, but its render function executes recording, which we want
441 * to time.
442 */
443class RecordPictureRenderer : public PictureRenderer {
444    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
445
446    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
447
448    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
449
450protected:
451    virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
452
453private:
454    virtual SkString getConfigNameInternal() SK_OVERRIDE;
455};
456
457class PipePictureRenderer : public PictureRenderer {
458public:
459    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
460
461private:
462    virtual SkString getConfigNameInternal() SK_OVERRIDE;
463
464    typedef PictureRenderer INHERITED;
465};
466
467class SimplePictureRenderer : public PictureRenderer {
468public:
469    virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath,
470                      const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE;
471
472    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
473
474private:
475    virtual SkString getConfigNameInternal() SK_OVERRIDE;
476
477    typedef PictureRenderer INHERITED;
478};
479
480class TiledPictureRenderer : public PictureRenderer {
481public:
482    TiledPictureRenderer();
483
484    virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath,
485                      const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE;
486
487    /**
488     * Renders to tiles, rather than a single canvas.
489     * If fWritePath was provided, a separate file is
490     * created for each tile, named "path0.png", "path1.png", etc.
491     * Multithreaded mode currently does not support writing to a file.
492     */
493    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
494
495    virtual void end() SK_OVERRIDE;
496
497    void setTileWidth(int width) {
498        fTileWidth = width;
499    }
500
501    int getTileWidth() const {
502        return fTileWidth;
503    }
504
505    void setTileHeight(int height) {
506        fTileHeight = height;
507    }
508
509    int getTileHeight() const {
510        return fTileHeight;
511    }
512
513    void setTileWidthPercentage(double percentage) {
514        fTileWidthPercentage = percentage;
515    }
516
517    double getTileWidthPercentage() const {
518        return fTileWidthPercentage;
519    }
520
521    void setTileHeightPercentage(double percentage) {
522        fTileHeightPercentage = percentage;
523    }
524
525    double getTileHeightPercentage() const {
526        return fTileHeightPercentage;
527    }
528
529    void setTileMinPowerOf2Width(int width) {
530        SkASSERT(SkIsPow2(width) && width > 0);
531        if (!SkIsPow2(width) || width <= 0) {
532            return;
533        }
534
535        fTileMinPowerOf2Width = width;
536    }
537
538    int getTileMinPowerOf2Width() const {
539        return fTileMinPowerOf2Width;
540    }
541
542    virtual TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; }
543
544    virtual bool supportsTimingIndividualTiles() { return true; }
545
546    /**
547     * Report the number of tiles in the x and y directions. Must not be called before init.
548     * @param x Output parameter identifying the number of tiles in the x direction.
549     * @param y Output parameter identifying the number of tiles in the y direction.
550     * @return True if the tiles have been set up, and x and y are meaningful. If false, x and y are
551     *         unmodified.
552     */
553    bool tileDimensions(int& x, int&y);
554
555    /**
556     * Move to the next tile and return its indices. Must be called before calling drawCurrentTile
557     * for the first time.
558     * @param i Output parameter identifying the column of the next tile to be drawn on the next
559     *          call to drawNextTile.
560     * @param j Output parameter identifying the row  of the next tile to be drawn on the next call
561     *          to drawNextTile.
562     * @param True if the tiles have been created and the next tile to be drawn by drawCurrentTile
563     *        is within the range of tiles. If false, i and j are unmodified.
564     */
565    bool nextTile(int& i, int& j);
566
567    /**
568     * Render one tile. This will draw the same tile each time it is called until nextTile is
569     * called. The tile rendered will depend on how many calls have been made to nextTile.
570     * It is an error to call this without first calling nextTile, or if nextTile returns false.
571     */
572    void drawCurrentTile();
573
574protected:
575    SkTDArray<SkRect> fTileRects;
576
577    virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
578    virtual SkString getConfigNameInternal() SK_OVERRIDE;
579
580private:
581    int    fTileWidth;
582    int    fTileHeight;
583    double fTileWidthPercentage;
584    double fTileHeightPercentage;
585    int    fTileMinPowerOf2Width;
586
587    // These variables are only used for timing individual tiles.
588    // Next tile to draw in fTileRects.
589    int    fCurrentTileOffset;
590    // Number of tiles in the x direction.
591    int    fTilesX;
592    // Number of tiles in the y direction.
593    int    fTilesY;
594
595    void setupTiles();
596    void setupPowerOf2Tiles();
597
598    typedef PictureRenderer INHERITED;
599};
600
601class CloneData;
602
603class MultiCorePictureRenderer : public TiledPictureRenderer {
604public:
605    explicit MultiCorePictureRenderer(int threadCount);
606
607    ~MultiCorePictureRenderer();
608
609    virtual void init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath,
610                      const SkString* inputFilename, bool useChecksumBasedFilenames) SK_OVERRIDE;
611
612    /**
613     * Behaves like TiledPictureRenderer::render(), only using multiple threads.
614     */
615    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
616
617    virtual void end() SK_OVERRIDE;
618
619    virtual bool supportsTimingIndividualTiles() SK_OVERRIDE { return false; }
620
621private:
622    virtual SkString getConfigNameInternal() SK_OVERRIDE;
623
624    const int            fNumThreads;
625    SkTDArray<SkCanvas*> fCanvasPool;
626    SkThreadPool         fThreadPool;
627    SkPicture*           fPictureClones;
628    CloneData**          fCloneData;
629    SkCountdown          fCountdown;
630
631    typedef TiledPictureRenderer INHERITED;
632};
633
634/**
635 * This class does not do any rendering, but its render function executes turning an SkPictureRecord
636 * into an SkPicturePlayback, which we want to time.
637 */
638class PlaybackCreationRenderer : public PictureRenderer {
639public:
640    virtual void setup() SK_OVERRIDE;
641
642    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
643
644    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
645
646    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
647
648private:
649    SkAutoTDelete<SkPictureRecorder> fRecorder;
650
651    virtual SkString getConfigNameInternal() SK_OVERRIDE;
652
653    typedef PictureRenderer INHERITED;
654};
655
656extern PictureRenderer* CreateGatherPixelRefsRenderer();
657extern PictureRenderer* CreatePictureCloneRenderer();
658
659}
660
661#endif  // PictureRenderer_DEFINED
662