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