2 * Copyright 2014 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
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.
12 #include "SkMultiPictureDraw.h"
13 #include "SkPicture.h"
14 #include "SkTaskGroup.h"
17 #include "GrLayerHoister.h"
18 #include "GrRecordReplaceDraw.h"
21 void SkMultiPictureDraw::DrawData::draw() {
22 fCanvas->drawPicture(fPicture, &fMatrix, fPaint);
25 void SkMultiPictureDraw::DrawData::init(SkCanvas* canvas, const SkPicture* picture,
26 const SkMatrix* matrix, const SkPaint* paint) {
27 fPicture = SkRef(picture);
28 fCanvas = SkRef(canvas);
32 fMatrix.setIdentity();
35 fPaint = SkNEW_ARGS(SkPaint, (*paint));
41 void SkMultiPictureDraw::DrawData::Reset(SkTDArray<DrawData>& data) {
42 for (int i = 0; i < data.count(); ++i) {
43 data[i].fPicture->unref();
44 data[i].fCanvas->unref();
45 SkDELETE(data[i].fPaint);
50 //////////////////////////////////////////////////////////////////////////////////////
52 SkMultiPictureDraw::SkMultiPictureDraw(int reserve) {
54 fGPUDrawData.setReserve(reserve);
55 fThreadSafeDrawData.setReserve(reserve);
59 void SkMultiPictureDraw::reset() {
60 DrawData::Reset(fGPUDrawData);
61 DrawData::Reset(fThreadSafeDrawData);
64 void SkMultiPictureDraw::add(SkCanvas* canvas,
65 const SkPicture* picture,
66 const SkMatrix* matrix,
67 const SkPaint* paint) {
68 if (NULL == canvas || NULL == picture) {
69 SkDEBUGFAIL("parameters to SkMultiPictureDraw::add should be non-NULL");
73 SkTDArray<DrawData>& array = canvas->getGrContext() ? fGPUDrawData : fThreadSafeDrawData;
74 array.append()->init(canvas, picture, matrix, paint);
77 class AutoMPDReset : SkNoncopyable {
78 SkMultiPictureDraw* fMPD;
80 AutoMPDReset(SkMultiPictureDraw* mpd) : fMPD(mpd) {}
81 ~AutoMPDReset() { fMPD->reset(); }
84 void SkMultiPictureDraw::draw() {
85 AutoMPDReset mpdreset(this);
86 // we place the taskgroup after the MPDReset, to ensure that we don't delete the DrawData
87 // objects until after we're finished the tasks (which have pointers to the data).
90 group.batch(DrawData::Draw, fThreadSafeDrawData.begin(), fThreadSafeDrawData.count());
91 // we deliberately don't call wait() here, since the destructor will do that, this allows us
92 // to continue processing gpu-data without having to wait on the cpu tasks.
94 const int count = fGPUDrawData.count();
99 #if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
100 GrContext* context = fGPUDrawData[0].fCanvas->getGrContext();
103 // Start by collecting all the layers that are going to be atlased and render
104 // them (if necessary). Hoisting the free floating layers is deferred until
105 // drawing the canvas that requires them.
106 SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled;
108 for (int i = 0; i < count; ++i) {
109 const DrawData& data = fGPUDrawData[i];
110 // we only expect 1 context for all the canvases
111 SkASSERT(data.fCanvas->getGrContext() == context);
113 if (!data.fPaint && data.fMatrix.isIdentity()) {
114 // TODO: this path always tries to optimize pictures. Should we
115 // switch to this API approach (vs. SkCanvas::EXPERIMENTAL_optimize)?
116 data.fCanvas->EXPERIMENTAL_optimize(data.fPicture);
119 if (!data.fCanvas->getClipBounds(&clipBounds)) {
123 // TODO: sorting the cacheable layers from smallest to largest
124 // would improve the packing and reduce the number of swaps
125 // TODO: another optimization would be to make a first pass to
126 // lock any required layer that is already in the atlas
127 GrLayerHoister::FindLayersToAtlas(context, data.fPicture,
129 &atlasedNeedRendering, &atlasedRecycled);
133 GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering);
135 SkTDArray<GrHoistedLayer> needRendering, recycled;
138 for (int i = 0; i < count; ++i) {
139 const DrawData& data = fGPUDrawData[i];
140 SkCanvas* canvas = data.fCanvas;
141 const SkPicture* picture = data.fPicture;
143 #if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
144 if (!data.fPaint && data.fMatrix.isIdentity()) {
147 if (!canvas->getClipBounds(&clipBounds)) {
151 // Find the layers required by this canvas. It will return atlased
152 // layers in the 'recycled' list since they have already been drawn.
153 GrLayerHoister::FindLayersToHoist(context, picture,
154 clipBounds, &needRendering, &recycled);
156 GrLayerHoister::DrawLayers(context, needRendering);
158 GrReplacements replacements;
160 GrLayerHoister::ConvertLayersToReplacements(needRendering, &replacements);
161 GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements);
163 const SkMatrix initialMatrix = canvas->getTotalMatrix();
165 // Render the entire picture using new layers
166 GrRecordReplaceDraw(picture, canvas, &replacements, initialMatrix, NULL);
168 GrLayerHoister::UnlockLayers(context, needRendering);
169 GrLayerHoister::UnlockLayers(context, recycled);
171 needRendering.rewind();
176 canvas->drawPicture(picture, &data.fMatrix, data.fPaint);
180 #if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
181 GrLayerHoister::UnlockLayers(context, atlasedNeedRendering);
182 GrLayerHoister::UnlockLayers(context, atlasedRecycled);
183 #if !GR_CACHE_HOISTED_LAYERS
184 GrLayerHoister::PurgeCache(context);