aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkMultiPictureDraw.cpp
blob: 09efba9377d988ab9349c0961bd420a7550912f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

// Need to include something before #if SK_SUPPORT_GPU so that the Android
// framework build, which gets its defines from SkTypes rather than a makefile,
// has the definition before checking it.
#include "SkCanvas.h"
#include "SkCanvasPriv.h"
#include "SkMultiPictureDraw.h"
#include "SkPicture.h"
#include "SkTaskGroup.h"

#if SK_SUPPORT_GPU
#include "GrContext.h"
#include "GrLayerHoister.h"
#include "GrRecordReplaceDraw.h"
#include "GrRenderTarget.h"
#endif

void SkMultiPictureDraw::DrawData::draw() {
    fCanvas->drawPicture(fPicture, &fMatrix, fPaint);
}

void SkMultiPictureDraw::DrawData::init(SkCanvas* canvas, const SkPicture* picture,
                                        const SkMatrix* matrix, const SkPaint* paint) {
    fPicture = SkRef(picture);
    fCanvas = SkRef(canvas);
    if (matrix) {
        fMatrix = *matrix;
    } else {
        fMatrix.setIdentity();
    }
    if (paint) {
        fPaint = new SkPaint(*paint);
    } else {
        fPaint = nullptr;
    }
}

void SkMultiPictureDraw::DrawData::Reset(SkTDArray<DrawData>& data) {
    for (int i = 0; i < data.count(); ++i) {
        data[i].fPicture->unref();
        data[i].fCanvas->unref();
        delete data[i].fPaint;
    }
    data.rewind();
}

//////////////////////////////////////////////////////////////////////////////////////

SkMultiPictureDraw::SkMultiPictureDraw(int reserve) {
    if (reserve > 0) {
        fGPUDrawData.setReserve(reserve);
        fThreadSafeDrawData.setReserve(reserve);
    }
}

void SkMultiPictureDraw::reset() {
    DrawData::Reset(fGPUDrawData);
    DrawData::Reset(fThreadSafeDrawData);
}

void SkMultiPictureDraw::add(SkCanvas* canvas,
                             const SkPicture* picture,
                             const SkMatrix* matrix,
                             const SkPaint* paint) {
    if (nullptr == canvas || nullptr == picture) {
        SkDEBUGFAIL("parameters to SkMultiPictureDraw::add should be non-nullptr");
        return;
    }

    SkTDArray<DrawData>& array = canvas->getGrContext() ? fGPUDrawData : fThreadSafeDrawData;
    array.append()->init(canvas, picture, matrix, paint);
}

class AutoMPDReset : SkNoncopyable {
    SkMultiPictureDraw* fMPD;
public:
    AutoMPDReset(SkMultiPictureDraw* mpd) : fMPD(mpd) {}
    ~AutoMPDReset() { fMPD->reset(); }
};

//#define FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING

void SkMultiPictureDraw::draw(bool flush) {
    AutoMPDReset mpdreset(this);

#ifdef FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING
    for (int i = 0; i < fThreadSafeDrawData.count(); ++i) {
        fThreadSafeDrawData[i].draw();
    }
#else
    SkTaskGroup().batch(fThreadSafeDrawData.count(), [&](int i) {
        fThreadSafeDrawData[i].draw();
    });
#endif

    // N.B. we could get going on any GPU work from this main thread while the CPU work runs.
    // But in practice, we've either got GPU work or CPU work, not both.

    const int count = fGPUDrawData.count();
    if (0 == count) {
        return;
    }

#if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
    GrContext* context = fGPUDrawData[0].fCanvas->getGrContext();
    SkASSERT(context);

    // Start by collecting all the layers that are going to be atlased and render
    // them (if necessary). Hoisting the free floating layers is deferred until
    // drawing the canvas that requires them.
    SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled;

    GrLayerHoister::Begin(context);

    for (int i = 0; i < count; ++i) {
        const DrawData& data = fGPUDrawData[i];
        // we only expect 1 context for all the canvases
        SkASSERT(data.fCanvas->getGrContext() == context);

        if (!data.fPaint) {
            SkRect clipBounds;
            if (!data.fCanvas->getClipBounds(&clipBounds)) {
                continue;
            }

            SkMatrix initialMatrix = data.fCanvas->getTotalMatrix();
            initialMatrix.preConcat(data.fMatrix);

            GrRenderTarget* rt = data.fCanvas->internal_private_accessTopLayerRenderTarget();
            SkASSERT(rt);

            // TODO: sorting the cacheable layers from smallest to largest
            // would improve the packing and reduce the number of swaps
            // TODO: another optimization would be to make a first pass to
            // lock any required layer that is already in the atlas
            GrLayerHoister::FindLayersToAtlas(context, data.fPicture, initialMatrix,
                                              clipBounds,
                                              &atlasedNeedRendering, &atlasedRecycled,
                                              rt->numColorSamples());
        }
    }

    GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering);

    SkTDArray<GrHoistedLayer> needRendering, recycled;
#endif

    for (int i = 0; i < count; ++i) {
        const DrawData& data = fGPUDrawData[i];
        SkCanvas* canvas = data.fCanvas;
        const SkPicture* picture = data.fPicture;

#if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
        if (!data.fPaint) {

            SkRect clipBounds;
            if (!canvas->getClipBounds(&clipBounds)) {
                continue;
            }

            SkAutoCanvasMatrixPaint acmp(canvas, &data.fMatrix, data.fPaint, picture->cullRect());

            const SkMatrix initialMatrix = canvas->getTotalMatrix();

            GrRenderTarget* rt = data.fCanvas->internal_private_accessTopLayerRenderTarget();
            SkASSERT(rt);

            // Find the layers required by this canvas. It will return atlased
            // layers in the 'recycled' list since they have already been drawn.
            GrLayerHoister::FindLayersToHoist(context, picture, initialMatrix,
                                              clipBounds, &needRendering, &recycled,
                                              rt->numColorSamples());

            GrLayerHoister::DrawLayers(context, needRendering);

            // Render the entire picture using new layers
            GrRecordReplaceDraw(picture, canvas, context->getLayerCache(),
                                initialMatrix, nullptr);

            GrLayerHoister::UnlockLayers(context, needRendering);
            GrLayerHoister::UnlockLayers(context, recycled);

            needRendering.rewind();
            recycled.rewind();
        } else
#endif
        {
            canvas->drawPicture(picture, &data.fMatrix, data.fPaint);
        }
        if (flush) {
            canvas->flush();
        }
    }

#if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
    GrLayerHoister::UnlockLayers(context, atlasedNeedRendering);
    GrLayerHoister::UnlockLayers(context, atlasedRecycled);
    GrLayerHoister::End(context);
#endif
}