SkMultiPictureDraw.cpp revision 6d5b5455743414ddb11d2b8c1fe9d7959f2b853d
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
17SkMultiPictureDraw::SkMultiPictureDraw(int reserve) {
18    if (reserve > 0) {
19        fDrawData.setReserve(reserve);
20    }
21}
22
23void SkMultiPictureDraw::reset() {
24    for (int i = 0; i < fDrawData.count(); ++i) {
25        fDrawData[i].picture->unref();
26        fDrawData[i].canvas->unref();
27        SkDELETE(fDrawData[i].paint);
28    }
29
30    fDrawData.rewind();
31}
32
33void SkMultiPictureDraw::add(SkCanvas* canvas,
34                             const SkPicture* picture,
35                             const SkMatrix* matrix,
36                             const SkPaint* paint) {
37    if (NULL == canvas || NULL == picture) {
38        SkDEBUGFAIL("parameters to SkMultiPictureDraw::add should be non-NULL");
39        return;
40    }
41
42    DrawData* data = fDrawData.append();
43
44    data->picture = SkRef(picture);
45    data->canvas = SkRef(canvas);
46    if (matrix) {
47        data->matrix = *matrix;
48    } else {
49        data->matrix.setIdentity();
50    }
51    if (paint) {
52        data->paint = SkNEW_ARGS(SkPaint, (*paint));
53    } else {
54        data->paint = NULL;
55    }
56}
57
58#undef SK_IGNORE_GPU_LAYER_HOISTING
59#define SK_IGNORE_GPU_LAYER_HOISTING 1
60
61void SkMultiPictureDraw::draw() {
62
63#ifndef SK_IGNORE_GPU_LAYER_HOISTING
64    GrContext* context = NULL;
65
66    // Start by collecting all the layers that are going to be atlased and render
67    // them (if necessary). Hoisting the free floating layers is deferred until
68    // drawing the canvas that requires them.
69    SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled;
70
71    for (int i = 0; i < fDrawData.count(); ++i) {
72        if (fDrawData[i].canvas->getGrContext() &&
73            !fDrawData[i].paint && fDrawData[i].matrix.isIdentity()) {
74            SkASSERT(NULL == context || context == fDrawData[i].canvas->getGrContext());
75            context = fDrawData[i].canvas->getGrContext();
76
77            // TODO: this path always tries to optimize pictures. Should we
78            // switch to this API approach (vs. SkCanvas::EXPERIMENTAL_optimize)?
79            fDrawData[i].canvas->EXPERIMENTAL_optimize(fDrawData[i].picture);
80
81            SkRect clipBounds;
82            if (!fDrawData[i].canvas->getClipBounds(&clipBounds)) {
83                continue;
84            }
85
86            // TODO: sorting the cacheable layers from smallest to largest
87            // would improve the packing and reduce the number of swaps
88            // TODO: another optimization would be to make a first pass to
89            // lock any required layer that is already in the atlas
90            GrLayerHoister::FindLayersToAtlas(context, fDrawData[i].picture,
91                                              clipBounds,
92                                              &atlasedNeedRendering, &atlasedRecycled);
93        }
94    }
95
96    if (NULL != context) {
97        GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering);
98    }
99
100    SkTDArray<GrHoistedLayer> needRendering, recycled;
101#endif
102
103    for (int i = 0; i < fDrawData.count(); ++i) {
104#ifndef SK_IGNORE_GPU_LAYER_HOISTING
105        if (fDrawData[i].canvas->getGrContext() &&
106            !fDrawData[i].paint && fDrawData[i].matrix.isIdentity()) {
107
108            SkRect clipBounds;
109            if (!fDrawData[i].canvas->getClipBounds(&clipBounds)) {
110                continue;
111            }
112
113            // Find the layers required by this canvas. It will return atlased
114            // layers in the 'recycled' list since they have already been drawn.
115            GrLayerHoister::FindLayersToHoist(context, fDrawData[i].picture,
116                                              clipBounds, &needRendering, &recycled);
117
118            GrLayerHoister::DrawLayers(context, needRendering);
119
120            GrReplacements replacements;
121
122            GrLayerHoister::ConvertLayersToReplacements(needRendering, &replacements);
123            GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements);
124
125            const SkMatrix initialMatrix = fDrawData[i].canvas->getTotalMatrix();
126
127            // Render the entire picture using new layers
128            GrRecordReplaceDraw(fDrawData[i].picture, fDrawData[i].canvas,
129                                &replacements, initialMatrix, NULL);
130
131            GrLayerHoister::UnlockLayers(context, needRendering);
132            GrLayerHoister::UnlockLayers(context, recycled);
133
134            needRendering.rewind();
135            recycled.rewind();
136        } else
137#endif
138        {
139            fDrawData[i].canvas->drawPicture(fDrawData[i].picture,
140                                             &fDrawData[i].matrix,
141                                             fDrawData[i].paint);
142        }
143    }
144
145#ifndef SK_IGNORE_GPU_LAYER_HOISTING
146    if (NULL != context) {
147        GrLayerHoister::UnlockLayers(context, atlasedNeedRendering);
148        GrLayerHoister::UnlockLayers(context, atlasedRecycled);
149#if !GR_CACHE_HOISTED_LAYERS
150        GrLayerHoister::PurgeCache(context);
151#endif
152    }
153#endif
154
155    this->reset();
156}
157
158