DisplayList.cpp revision 618236fe886b84f99cd7c48ece96b16f82a9d2b2
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define ATRACE_TAG ATRACE_TAG_VIEW
18
19#include <SkCanvas.h>
20#include <algorithm>
21
22#include <utils/Trace.h>
23
24#include "Debug.h"
25#include "DisplayList.h"
26#include "DisplayListOp.h"
27#include "DisplayListLogBuffer.h"
28
29namespace android {
30namespace uirenderer {
31
32void RenderNode::outputLogBuffer(int fd) {
33    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
34    if (logBuffer.isEmpty()) {
35        return;
36    }
37
38    FILE *file = fdopen(fd, "a");
39
40    fprintf(file, "\nRecent DisplayList operations\n");
41    logBuffer.outputCommands(file);
42
43    String8 cachesLog;
44    Caches::getInstance().dumpMemoryUsage(cachesLog);
45    fprintf(file, "\nCaches:\n%s", cachesLog.string());
46    fprintf(file, "\n");
47
48    fflush(file);
49}
50
51RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
52}
53
54RenderNode::~RenderNode() {
55    LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
56
57    mDestroyed = true;
58    delete mDisplayListData;
59}
60
61void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
62    if (displayList) {
63        DISPLAY_LIST_LOGD("Deferring display list destruction");
64        Caches::getInstance().deleteDisplayListDeferred(displayList);
65    }
66}
67
68void RenderNode::setData(DisplayListData* data) {
69    delete mDisplayListData;
70    mDisplayListData = data;
71    if (mDisplayListData) {
72        Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
73    }
74}
75
76/**
77 * This function is a simplified version of replay(), where we simply retrieve and log the
78 * display list. This function should remain in sync with the replay() function.
79 */
80void RenderNode::output(uint32_t level) {
81    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
82            mName.string(), isRenderable());
83    ALOGD("%*s%s %d", level * 2, "", "Save",
84            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
85
86    outputViewProperties(level);
87    int flags = DisplayListOp::kOpLogFlag_Recurse;
88    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
89        mDisplayListData->displayListOps[i]->output(level, flags);
90    }
91
92    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
93}
94
95void RenderNode::outputViewProperties(const int level) {
96    properties().updateMatrix();
97    if (properties().mLeft != 0 || properties().mTop != 0) {
98        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
99    }
100    if (properties().mStaticMatrix) {
101        ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
102                level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
103    }
104    if (properties().mAnimationMatrix) {
105        ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
106                level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
107    }
108    if (properties().mMatrixFlags != 0) {
109        if (properties().mMatrixFlags == TRANSLATION) {
110            ALOGD("%*sTranslate %.2f, %.2f, %.2f",
111                    level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
112        } else {
113            ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
114                    level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
115        }
116    }
117
118    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
119    if (properties().mAlpha < 1) {
120        if (properties().mCaching) {
121            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
122        } else if (!properties().mHasOverlappingRendering) {
123            ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
124        } else {
125            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
126            if (clipToBoundsNeeded) {
127                flags |= SkCanvas::kClipToLayer_SaveFlag;
128                clipToBoundsNeeded = false; // clipping done by save layer
129            }
130            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
131                    (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
132                    (int)(properties().mAlpha * 255), flags);
133        }
134    }
135    if (clipToBoundsNeeded) {
136        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
137                (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
138    }
139}
140
141/*
142 * For property operations, we pass a savecount of 0, since the operations aren't part of the
143 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
144 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
145 */
146#define PROPERTY_SAVECOUNT 0
147
148template <class T>
149void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
150        const int level) {
151#if DEBUG_DISPLAY_LIST
152    outputViewProperties(level);
153#endif
154    properties().updateMatrix();
155    if (properties().mLeft != 0 || properties().mTop != 0) {
156        renderer.translate(properties().mLeft, properties().mTop);
157    }
158    if (properties().mStaticMatrix) {
159        renderer.concatMatrix(properties().mStaticMatrix);
160    } else if (properties().mAnimationMatrix) {
161        renderer.concatMatrix(properties().mAnimationMatrix);
162    }
163    if (properties().mMatrixFlags != 0) {
164        if (properties().mMatrixFlags == TRANSLATION) {
165            renderer.translate(properties().mTranslationX, properties().mTranslationY);
166        } else {
167            renderer.concatMatrix(*properties().mTransformMatrix);
168        }
169    }
170    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
171    if (properties().mAlpha < 1) {
172        if (properties().mCaching) {
173            renderer.setOverrideLayerAlpha(properties().mAlpha);
174        } else if (!properties().mHasOverlappingRendering) {
175            renderer.scaleAlpha(properties().mAlpha);
176        } else {
177            // TODO: should be able to store the size of a DL at record time and not
178            // have to pass it into this call. In fact, this information might be in the
179            // location/size info that we store with the new native transform data.
180            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
181            if (clipToBoundsNeeded) {
182                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
183                clipToBoundsNeeded = false; // clipping done by saveLayer
184            }
185
186            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
187                    0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
188            handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
189        }
190    }
191    if (clipToBoundsNeeded) {
192        ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
193                properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
194        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
195    }
196    if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
197        ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
198        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
199    }
200}
201
202/**
203 * Apply property-based transformations to input matrix
204 *
205 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
206 * matrix computation instead of the Skia 3x3 matrix + camera hackery.
207 */
208void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
209    if (properties().mLeft != 0 || properties().mTop != 0) {
210        matrix.translate(properties().mLeft, properties().mTop);
211    }
212    if (properties().mStaticMatrix) {
213        mat4 stat(*properties().mStaticMatrix);
214        matrix.multiply(stat);
215    } else if (properties().mAnimationMatrix) {
216        mat4 anim(*properties().mAnimationMatrix);
217        matrix.multiply(anim);
218    }
219    if (properties().mMatrixFlags != 0) {
220        properties().updateMatrix();
221        if (properties().mMatrixFlags == TRANSLATION) {
222            matrix.translate(properties().mTranslationX, properties().mTranslationY,
223                    true3dTransform ? properties().mTranslationZ : 0.0f);
224        } else {
225            if (!true3dTransform) {
226                matrix.multiply(*properties().mTransformMatrix);
227            } else {
228                mat4 true3dMat;
229                true3dMat.loadTranslate(
230                        properties().mPivotX + properties().mTranslationX,
231                        properties().mPivotY + properties().mTranslationY,
232                        properties().mTranslationZ);
233                true3dMat.rotate(properties().mRotationX, 1, 0, 0);
234                true3dMat.rotate(properties().mRotationY, 0, 1, 0);
235                true3dMat.rotate(properties().mRotation, 0, 0, 1);
236                true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
237                true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
238
239                matrix.multiply(true3dMat);
240            }
241        }
242    }
243}
244
245/**
246 * Organizes the DisplayList hierarchy to prepare for background projection reordering.
247 *
248 * This should be called before a call to defer() or drawDisplayList()
249 *
250 * Each DisplayList that serves as a 3d root builds its list of composited children,
251 * which are flagged to not draw in the standard draw loop.
252 */
253void RenderNode::computeOrdering() {
254    ATRACE_CALL();
255    mProjectedNodes.clear();
256
257    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
258    // transform properties are applied correctly to top level children
259    if (mDisplayListData == NULL) return;
260    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
261        DrawDisplayListOp* childOp = mDisplayListData->children[i];
262        childOp->mDisplayList->computeOrderingImpl(childOp,
263                &mProjectedNodes, &mat4::identity());
264    }
265}
266
267void RenderNode::computeOrderingImpl(
268        DrawDisplayListOp* opState,
269        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
270        const mat4* transformFromProjectionSurface) {
271    mProjectedNodes.clear();
272    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
273
274    // TODO: should avoid this calculation in most cases
275    // TODO: just calculate single matrix, down to all leaf composited elements
276    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
277    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
278
279    if (properties().mProjectBackwards) {
280        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
281        opState->mSkipInOrderDraw = true;
282        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
283        compositedChildrenOfProjectionSurface->add(opState);
284    } else {
285        // standard in order draw
286        opState->mSkipInOrderDraw = false;
287    }
288
289    if (mDisplayListData->children.size() > 0) {
290        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
291        bool haveAppliedPropertiesToProjection = false;
292        for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
293            DrawDisplayListOp* childOp = mDisplayListData->children[i];
294            RenderNode* child = childOp->mDisplayList;
295
296            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
297            const mat4* projectionTransform = NULL;
298            if (isProjectionReceiver && !child->properties().mProjectBackwards) {
299                // if receiving projections, collect projecting descendent
300
301                // Note that if a direct descendent is projecting backwards, we pass it's
302                // grandparent projection collection, since it shouldn't project onto it's
303                // parent, where it will already be drawing.
304                projectionChildren = &mProjectedNodes;
305                projectionTransform = &mat4::identity();
306            } else {
307                if (!haveAppliedPropertiesToProjection) {
308                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
309                    haveAppliedPropertiesToProjection = true;
310                }
311                projectionChildren = compositedChildrenOfProjectionSurface;
312                projectionTransform = &localTransformFromProjectionSurface;
313            }
314            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
315        }
316    }
317
318}
319
320class DeferOperationHandler {
321public:
322    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
323        : mDeferStruct(deferStruct), mLevel(level) {}
324    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
325        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
326    }
327    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
328
329private:
330    DeferStateStruct& mDeferStruct;
331    const int mLevel;
332};
333
334void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
335    DeferOperationHandler handler(deferStruct, level);
336    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
337}
338
339class ReplayOperationHandler {
340public:
341    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
342        : mReplayStruct(replayStruct), mLevel(level) {}
343    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
344#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
345        properties().mReplayStruct.mRenderer.eventMark(operation->name());
346#endif
347        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
348    }
349    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
350
351private:
352    ReplayStateStruct& mReplayStruct;
353    const int mLevel;
354};
355
356void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
357    ReplayOperationHandler handler(replayStruct, level);
358
359    replayStruct.mRenderer.startMark(mName.string());
360    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
361    replayStruct.mRenderer.endMark();
362
363    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
364            replayStruct.mDrawGlStatus);
365}
366
367void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
368    if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
369
370    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
371        DrawDisplayListOp* childOp = mDisplayListData->children[i];
372        RenderNode* child = childOp->mDisplayList;
373        float childZ = child->properties().mTranslationZ;
374
375        if (childZ != 0.0f) {
376            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
377            childOp->mSkipInOrderDraw = true;
378        } else if (!child->properties().mProjectBackwards) {
379            // regular, in order drawing DisplayList
380            childOp->mSkipInOrderDraw = false;
381        }
382    }
383
384    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
385    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
386}
387
388#define SHADOW_DELTA 0.1f
389
390template <class T>
391void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
392        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
393    const int size = zTranslatedNodes.size();
394    if (size == 0
395            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
396            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
397        // no 3d children to draw
398        return;
399    }
400
401    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
402    LinearAllocator& alloc = handler.allocator();
403    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
404            SkRegion::kIntersect_Op); // clip to 3d root bounds
405    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
406
407    /**
408     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
409     * with very similar Z heights to draw together.
410     *
411     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
412     * underneath both, and neither's shadow is drawn on top of the other.
413     */
414    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
415    size_t drawIndex, shadowIndex, endIndex;
416    if (mode == kNegativeZChildren) {
417        drawIndex = 0;
418        endIndex = nonNegativeIndex;
419        shadowIndex = endIndex; // draw no shadows
420    } else {
421        drawIndex = nonNegativeIndex;
422        endIndex = size;
423        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
424    }
425    float lastCasterZ = 0.0f;
426    while (shadowIndex < endIndex || drawIndex < endIndex) {
427        if (shadowIndex < endIndex) {
428            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
429            RenderNode* caster = casterOp->mDisplayList;
430            const float casterZ = zTranslatedNodes[shadowIndex].key;
431            // attempt to render the shadow if the caster about to be drawn is its caster,
432            // OR if its caster's Z value is similar to the previous potential caster
433            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
434
435                if (caster->properties().mCastsShadow && caster->properties().mAlpha > 0.0f) {
436                    mat4 shadowMatrixXY(casterOp->mTransformFromParent);
437                    caster->applyViewPropertyTransforms(shadowMatrixXY);
438
439                    // Z matrix needs actual 3d transformation, so mapped z values will be correct
440                    mat4 shadowMatrixZ(casterOp->mTransformFromParent);
441                    caster->applyViewPropertyTransforms(shadowMatrixZ, true);
442
443                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(
444                            shadowMatrixXY, shadowMatrixZ,
445                            caster->properties().mAlpha, &(caster->properties().mOutline),
446                            caster->properties().mWidth, caster->properties().mHeight);
447                    handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
448                }
449
450                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
451                shadowIndex++;
452                continue;
453            }
454        }
455
456        // only the actual child DL draw needs to be in save/restore,
457        // since it modifies the renderer's matrix
458        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
459
460        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
461        RenderNode* child = childOp->mDisplayList;
462
463        renderer.concatMatrix(childOp->mTransformFromParent);
464        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
465        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
466        childOp->mSkipInOrderDraw = true;
467
468        renderer.restoreToCount(restoreTo);
469        drawIndex++;
470    }
471    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
472}
473
474template <class T>
475void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
476    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
477    LinearAllocator& alloc = handler.allocator();
478    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
479            SkRegion::kReplace_Op); // clip to projection surface root bounds
480    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
481
482    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
483        DrawDisplayListOp* childOp = mProjectedNodes[i];
484
485        // matrix save, concat, and restore can be done safely without allocating operations
486        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
487        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
488        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
489        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
490        childOp->mSkipInOrderDraw = true;
491        renderer.restoreToCount(restoreTo);
492    }
493    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
494}
495
496/**
497 * This function serves both defer and replay modes, and will organize the displayList's component
498 * operations for a single frame:
499 *
500 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
501 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
502 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
503 * defer vs replay logic, per operation
504 */
505template <class T>
506void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
507    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
508        ALOGW("Error: %s is drawing after destruction", mName.string());
509        CRASH();
510    }
511    if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
512        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
513        return;
514    }
515
516#if DEBUG_DISPLAY_LIST
517    Rect* clipRect = renderer.getClipRect();
518    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
519            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
520            clipRect->right, clipRect->bottom);
521#endif
522
523    LinearAllocator& alloc = handler.allocator();
524    int restoreTo = renderer.getSaveCount();
525    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
526            PROPERTY_SAVECOUNT, properties().mClipToBounds);
527
528    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
529            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
530
531    setViewProperties<T>(renderer, handler, level + 1);
532
533    bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
534    if (!quickRejected) {
535        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
536        buildZSortedChildList(zTranslatedNodes);
537
538        // for 3d root, draw children with negative z values
539        iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
540
541        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
542        const int saveCountOffset = renderer.getSaveCount() - 1;
543        const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
544        for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
545            DisplayListOp *op = mDisplayListData->displayListOps[i];
546
547#if DEBUG_DISPLAY_LIST
548            op->output(level + 1);
549#endif
550
551            logBuffer.writeCommand(level, op->name());
552            handler(op, saveCountOffset, properties().mClipToBounds);
553
554            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
555                iterateProjectedChildren(renderer, handler, level);
556            }
557        }
558
559        // for 3d root, draw children with positive z values
560        iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
561    }
562
563    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
564    handler(new (alloc) RestoreToCountOp(restoreTo),
565            PROPERTY_SAVECOUNT, properties().mClipToBounds);
566    renderer.setOverrideLayerAlpha(1.0f);
567}
568
569void DisplayListData::cleanupResources() {
570    Caches& caches = Caches::getInstance();
571    caches.unregisterFunctors(functorCount);
572    caches.resourceCache.lock();
573
574    for (size_t i = 0; i < bitmapResources.size(); i++) {
575        caches.resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
576    }
577
578    for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
579        const SkBitmap* bitmap = ownedBitmapResources.itemAt(i);
580        caches.resourceCache.decrementRefcountLocked(bitmap);
581        caches.resourceCache.destructorLocked(bitmap);
582    }
583
584    for (size_t i = 0; i < patchResources.size(); i++) {
585        caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
586    }
587
588    for (size_t i = 0; i < shaders.size(); i++) {
589        caches.resourceCache.decrementRefcountLocked(shaders.itemAt(i));
590        caches.resourceCache.destructorLocked(shaders.itemAt(i));
591    }
592
593    for (size_t i = 0; i < sourcePaths.size(); i++) {
594        caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
595    }
596
597    for (size_t i = 0; i < layers.size(); i++) {
598        caches.resourceCache.decrementRefcountLocked(layers.itemAt(i));
599    }
600
601    caches.resourceCache.unlock();
602
603    for (size_t i = 0; i < paints.size(); i++) {
604        delete paints.itemAt(i);
605    }
606
607    for (size_t i = 0; i < regions.size(); i++) {
608        delete regions.itemAt(i);
609    }
610
611    for (size_t i = 0; i < paths.size(); i++) {
612        delete paths.itemAt(i);
613    }
614
615    for (size_t i = 0; i < matrices.size(); i++) {
616        delete matrices.itemAt(i);
617    }
618
619    bitmapResources.clear();
620    ownedBitmapResources.clear();
621    patchResources.clear();
622    shaders.clear();
623    sourcePaths.clear();
624    paints.clear();
625    regions.clear();
626    paths.clear();
627    matrices.clear();
628    layers.clear();
629}
630
631}; // namespace uirenderer
632}; // namespace android
633