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