SkMultiPictureDraw.cpp revision 66cad7669be6c47768d084090a3d498014dfc847
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#if SK_SUPPORT_GPU
9#include "GrLayerHoister.h"
10#include "GrRecordReplaceDraw.h"
11#endif
12
13#include "SkCanvas.h"
14#include "SkMultiPictureDraw.h"
15#include "SkPicture.h"
16#include "SkTaskGroup.h"
17
18void SkMultiPictureDraw::DrawData::draw() {
19    fCanvas->drawPicture(fPicture, &fMatrix, fPaint);
20}
21
22void SkMultiPictureDraw::DrawData::init(SkCanvas* canvas, const SkPicture* picture,
23                                        const SkMatrix* matrix, const SkPaint* paint) {
24    fPicture = SkRef(picture);
25    fCanvas = SkRef(canvas);
26    if (matrix) {
27        fMatrix = *matrix;
28    } else {
29        fMatrix.setIdentity();
30    }
31    if (paint) {
32        fPaint = SkNEW_ARGS(SkPaint, (*paint));
33    } else {
34        fPaint = NULL;
35    }
36}
37
38void SkMultiPictureDraw::DrawData::Reset(SkTDArray<DrawData>& data) {
39    for (int i = 0; i < data.count(); ++i) {
40        data[i].fPicture->unref();
41        data[i].fCanvas->unref();
42        SkDELETE(data[i].fPaint);
43    }
44    data.rewind();
45}
46
47//////////////////////////////////////////////////////////////////////////////////////
48
49SkMultiPictureDraw::SkMultiPictureDraw(int reserve) {
50    if (reserve > 0) {
51        fGPUDrawData.setReserve(reserve);
52        fThreadSafeDrawData.setReserve(reserve);
53    }
54}
55
56void SkMultiPictureDraw::reset() {
57    DrawData::Reset(fGPUDrawData);
58    DrawData::Reset(fThreadSafeDrawData);
59}
60
61void SkMultiPictureDraw::add(SkCanvas* canvas,
62                             const SkPicture* picture,
63                             const SkMatrix* matrix,
64                             const SkPaint* paint) {
65    if (NULL == canvas || NULL == picture) {
66        SkDEBUGFAIL("parameters to SkMultiPictureDraw::add should be non-NULL");
67        return;
68    }
69
70    SkTDArray<DrawData>& array = canvas->getGrContext() ? fGPUDrawData : fThreadSafeDrawData;
71    array.append()->init(canvas, picture, matrix, paint);
72}
73
74class AutoMPDReset : SkNoncopyable {
75    SkMultiPictureDraw* fMPD;
76public:
77    AutoMPDReset(SkMultiPictureDraw* mpd) : fMPD(mpd) {}
78    ~AutoMPDReset() { fMPD->reset(); }
79};
80
81void SkMultiPictureDraw::draw() {
82    AutoMPDReset mpdreset(this);
83    // we place the taskgroup after the MPDReset, to ensure that we don't delete the DrawData
84    // objects until after we're finished the tasks (which have pointers to the data).
85
86#if 0
87    SkTaskGroup group;
88    group.batch(DrawData::Draw, fThreadSafeDrawData.begin(), fThreadSafeDrawData.count());
89    // we deliberately don't call wait() here, since the destructor will do that, this allows us
90    // to continue processing gpu-data without having to wait on the cpu tasks.
91#else
92    for (int i = 0; i < fThreadSafeDrawData.count(); i++) {
93        DrawData::Draw(&fThreadSafeDrawData[i]);
94    }
95#endif
96
97    const int count = fGPUDrawData.count();
98    if (0 == count) {
99        return;
100    }
101
102#if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
103    GrContext* context = fGPUDrawData[0].fCanvas->getGrContext();
104    SkASSERT(context);
105
106    // Start by collecting all the layers that are going to be atlased and render
107    // them (if necessary). Hoisting the free floating layers is deferred until
108    // drawing the canvas that requires them.
109    SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled;
110
111    for (int i = 0; i < count; ++i) {
112        const DrawData& data = fGPUDrawData[i];
113        // we only expect 1 context for all the canvases
114        SkASSERT(data.fCanvas->getGrContext() == context);
115
116        if (!data.fPaint && data.fMatrix.isIdentity()) {
117            // TODO: this path always tries to optimize pictures. Should we
118            // switch to this API approach (vs. SkCanvas::EXPERIMENTAL_optimize)?
119            data.fCanvas->EXPERIMENTAL_optimize(data.fPicture);
120
121            SkRect clipBounds;
122            if (!data.fCanvas->getClipBounds(&clipBounds)) {
123                continue;
124            }
125
126            // TODO: sorting the cacheable layers from smallest to largest
127            // would improve the packing and reduce the number of swaps
128            // TODO: another optimization would be to make a first pass to
129            // lock any required layer that is already in the atlas
130            GrLayerHoister::FindLayersToAtlas(context, data.fPicture,
131                                              clipBounds,
132                                              &atlasedNeedRendering, &atlasedRecycled);
133        }
134    }
135
136    GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering);
137
138    SkTDArray<GrHoistedLayer> needRendering, recycled;
139#endif
140
141    for (int i = 0; i < count; ++i) {
142        const DrawData& data = fGPUDrawData[i];
143        SkCanvas* canvas = data.fCanvas;
144        const SkPicture* picture = data.fPicture;
145
146#if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
147        if (!data.fPaint && data.fMatrix.isIdentity()) {
148
149            SkRect clipBounds;
150            if (!canvas->getClipBounds(&clipBounds)) {
151                continue;
152            }
153
154            // Find the layers required by this canvas. It will return atlased
155            // layers in the 'recycled' list since they have already been drawn.
156            GrLayerHoister::FindLayersToHoist(context, picture,
157                                              clipBounds, &needRendering, &recycled);
158
159            GrLayerHoister::DrawLayers(context, needRendering);
160
161            GrReplacements replacements;
162
163            GrLayerHoister::ConvertLayersToReplacements(needRendering, &replacements);
164            GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements);
165
166            const SkMatrix initialMatrix = canvas->getTotalMatrix();
167
168            // Render the entire picture using new layers
169            GrRecordReplaceDraw(picture, canvas, &replacements, initialMatrix, NULL);
170
171            GrLayerHoister::UnlockLayers(context, needRendering);
172            GrLayerHoister::UnlockLayers(context, recycled);
173
174            needRendering.rewind();
175            recycled.rewind();
176        } else
177#endif
178        {
179            canvas->drawPicture(picture, &data.fMatrix, data.fPaint);
180        }
181    }
182
183#if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
184    GrLayerHoister::UnlockLayers(context, atlasedNeedRendering);
185    GrLayerHoister::UnlockLayers(context, atlasedRecycled);
186#if !GR_CACHE_HOISTED_LAYERS
187    GrLayerHoister::PurgeCache(context);
188#endif
189#endif
190}
191
192