Home | History | Annotate | Download | only in core
      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