1/*
2 * Copyright 2014 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#include "SkCanvas.h"
9#include "SkCanvasPriv.h"
10#include "SkMultiPictureDraw.h"
11#include "SkPicture.h"
12#include "SkTaskGroup.h"
13
14void SkMultiPictureDraw::DrawData::draw() {
15    fCanvas->drawPicture(fPicture, &fMatrix, fPaint);
16}
17
18void SkMultiPictureDraw::DrawData::init(SkCanvas* canvas, const SkPicture* picture,
19                                        const SkMatrix* matrix, const SkPaint* paint) {
20    fPicture = SkRef(picture);
21    fCanvas = canvas;
22    if (matrix) {
23        fMatrix = *matrix;
24    } else {
25        fMatrix.setIdentity();
26    }
27    if (paint) {
28        fPaint = new SkPaint(*paint);
29    } else {
30        fPaint = nullptr;
31    }
32}
33
34void SkMultiPictureDraw::DrawData::Reset(SkTDArray<DrawData>& data) {
35    for (int i = 0; i < data.count(); ++i) {
36        data[i].fPicture->unref();
37        delete data[i].fPaint;
38    }
39    data.rewind();
40}
41
42//////////////////////////////////////////////////////////////////////////////////////
43
44SkMultiPictureDraw::SkMultiPictureDraw(int reserve) {
45    if (reserve > 0) {
46        fGPUDrawData.setReserve(reserve);
47        fThreadSafeDrawData.setReserve(reserve);
48    }
49}
50
51void SkMultiPictureDraw::reset() {
52    DrawData::Reset(fGPUDrawData);
53    DrawData::Reset(fThreadSafeDrawData);
54}
55
56void SkMultiPictureDraw::add(SkCanvas* canvas,
57                             const SkPicture* picture,
58                             const SkMatrix* matrix,
59                             const SkPaint* paint) {
60    if (nullptr == canvas || nullptr == picture) {
61        SkDEBUGFAIL("parameters to SkMultiPictureDraw::add should be non-nullptr");
62        return;
63    }
64
65    SkTDArray<DrawData>& array = canvas->getGrContext() ? fGPUDrawData : fThreadSafeDrawData;
66    array.append()->init(canvas, picture, matrix, paint);
67}
68
69class AutoMPDReset : SkNoncopyable {
70    SkMultiPictureDraw* fMPD;
71public:
72    AutoMPDReset(SkMultiPictureDraw* mpd) : fMPD(mpd) {}
73    ~AutoMPDReset() { fMPD->reset(); }
74};
75
76//#define FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING
77
78void SkMultiPictureDraw::draw(bool flush) {
79    AutoMPDReset mpdreset(this);
80
81#ifdef FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING
82    for (int i = 0; i < fThreadSafeDrawData.count(); ++i) {
83        fThreadSafeDrawData[i].draw();
84    }
85#else
86    SkTaskGroup().batch(fThreadSafeDrawData.count(), [&](int i) {
87        fThreadSafeDrawData[i].draw();
88    });
89#endif
90
91    // N.B. we could get going on any GPU work from this main thread while the CPU work runs.
92    // But in practice, we've either got GPU work or CPU work, not both.
93
94    const int count = fGPUDrawData.count();
95    if (0 == count) {
96        return;
97    }
98
99    for (int i = 0; i < count; ++i) {
100        const DrawData& data = fGPUDrawData[i];
101        SkCanvas* canvas = data.fCanvas;
102        const SkPicture* picture = data.fPicture;
103
104        canvas->drawPicture(picture, &data.fMatrix, data.fPaint);
105        if (flush) {
106            canvas->flush();
107        }
108    }
109}
110