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