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 24 void SkMultiPictureDraw::DrawData::draw() { 25 fCanvas->drawPicture(fPicture, &fMatrix, fPaint); 26 } 27 28 void 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 44 void 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 55 SkMultiPictureDraw::SkMultiPictureDraw(int reserve) { 56 if (reserve > 0) { 57 fGPUDrawData.setReserve(reserve); 58 fThreadSafeDrawData.setReserve(reserve); 59 } 60 } 61 62 void SkMultiPictureDraw::reset() { 63 DrawData::Reset(fGPUDrawData); 64 DrawData::Reset(fThreadSafeDrawData); 65 } 66 67 void 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 80 class AutoMPDReset : SkNoncopyable { 81 SkMultiPictureDraw* fMPD; 82 public: 83 AutoMPDReset(SkMultiPictureDraw* mpd) : fMPD(mpd) {} 84 ~AutoMPDReset() { fMPD->reset(); } 85 }; 86 87 //#define FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING 88 89 void 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