1/*
2 * Copyright 2017 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 SkThreadedBMPDevice_DEFINED
9#define SkThreadedBMPDevice_DEFINED
10
11#include "SkBitmapDevice.h"
12#include "SkDraw.h"
13#include "SkTaskGroup2D.h"
14
15class SkThreadedBMPDevice : public SkBitmapDevice {
16public:
17    // When threads = 0, we make fThreadCnt = tiles. Otherwise fThreadCnt = threads.
18    // When executor = nullptr, we manages the thread pool. Otherwise, the caller manages it.
19    SkThreadedBMPDevice(const SkBitmap& bitmap, int tiles, int threads = 0,
20                        SkExecutor* executor = nullptr);
21
22    ~SkThreadedBMPDevice() override { fQueue.finish(); }
23
24protected:
25    void drawPaint(const SkPaint& paint) override;
26    void drawPoints(SkCanvas::PointMode mode, size_t count,
27                            const SkPoint[], const SkPaint& paint) override;
28    void drawRect(const SkRect& r, const SkPaint& paint) override;
29    void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
30
31    void drawPath(const SkPath&, const SkPaint&, const SkMatrix* prePathMatrix,
32                  bool pathIsMutable) override;
33    void drawBitmap(const SkBitmap&, SkScalar x, SkScalar y, const SkPaint&) override;
34    void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) override;
35
36    void drawText(const void* text, size_t len, SkScalar x, SkScalar y,
37                  const SkPaint&) override;
38    void drawPosText(const void* text, size_t len, const SkScalar pos[],
39                     int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override;
40    void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) override;
41    void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override;
42
43    void flush() override;
44
45private:
46    struct DrawState {
47        SkPixmap fDst;
48        SkMatrix fMatrix;
49        SkRasterClip fRC;
50
51        DrawState() {}
52        explicit DrawState(SkThreadedBMPDevice* dev);
53
54        SkDraw getDraw() const;
55    };
56
57    class TileDraw : public SkDraw {
58        public: TileDraw(const DrawState& ds, const SkIRect& tileBounds);
59        private: SkRasterClip fTileRC;
60    };
61
62    struct DrawElement {
63        using DrawFn = std::function<void(SkArenaAlloc* threadAlloc, const DrawState& ds,
64                                          const SkIRect& tileBounds)>;
65
66        DrawFn      fDrawFn;
67        DrawState   fDS;
68        SkIRect     fDrawBounds;
69    };
70
71    class DrawQueue {
72    public:
73        static constexpr int MAX_QUEUE_SIZE = 100000;
74
75        DrawQueue(SkThreadedBMPDevice* device) : fDevice(device) {}
76        void reset();
77
78        // For ~SkThreadedBMPDevice() to shutdown tasks, we use this instead of reset because reset
79        // will start new tasks.
80        void finish() { fTasks->finish(); }
81
82        SK_ALWAYS_INLINE void push(const SkRect& rawDrawBounds,
83                                   DrawElement::DrawFn&& drawFn) {
84            if (fSize == MAX_QUEUE_SIZE) {
85                this->reset();
86            }
87            SkASSERT(fSize < MAX_QUEUE_SIZE);
88
89            DrawElement* element = &fElements[fSize++];
90            element->fDS = DrawState(fDevice);
91            element->fDrawFn = std::move(drawFn);
92            element->fDrawBounds = fDevice->transformDrawBounds(rawDrawBounds);
93            fTasks->addColumn();
94        }
95
96    private:
97        SkThreadedBMPDevice*            fDevice;
98        std::unique_ptr<SkTaskGroup2D>  fTasks;
99        DrawElement                     fElements[MAX_QUEUE_SIZE];
100        int                             fSize;
101    };
102
103    SkIRect transformDrawBounds(const SkRect& drawBounds) const;
104
105    const int fTileCnt;
106    const int fThreadCnt;
107    SkTArray<SkIRect> fTileBounds;
108
109    /**
110     * This can either be
111     * 1. fInternalExecutor.get() which means that we're managing the thread pool's life cycle.
112     * 2. provided by our caller which means that our caller is managing the threads' life cycle.
113     * In the 2nd case, fInternalExecutor == nullptr.
114     */
115    SkExecutor* fExecutor = nullptr;
116    std::unique_ptr<SkExecutor> fInternalExecutor;
117
118    DrawQueue fQueue;
119
120    typedef SkBitmapDevice INHERITED;
121};
122
123#endif // SkThreadedBMPDevice_DEFINED
124