DisplayList.cpp revision 726118b35240957710d4d85fb5747e2ba8b934f7
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 DisplayList::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
51DisplayList::DisplayList() :
52        mDisplayListData(0), mDestroyed(false), mTransformMatrix(NULL), mTransformCamera(NULL),
53        mTransformMatrix3D(NULL), mStaticMatrix(NULL), mAnimationMatrix(NULL) {
54
55    mLeft = 0;
56    mTop = 0;
57    mRight = 0;
58    mBottom = 0;
59    mClipToBounds = true;
60    mIsolatedZVolume = true;
61    mProjectBackwards = false;
62    mProjectionReceiver = false;
63    mOutline.rewind();
64    mClipToOutline = false;
65    mCastsShadow = false;
66    mUsesGlobalCamera = false;
67    mAlpha = 1;
68    mHasOverlappingRendering = true;
69    mTranslationX = 0;
70    mTranslationY = 0;
71    mTranslationZ = 0;
72    mRotation = 0;
73    mRotationX = 0;
74    mRotationY= 0;
75    mScaleX = 1;
76    mScaleY = 1;
77    mPivotX = 0;
78    mPivotY = 0;
79    mCameraDistance = 0;
80    mMatrixDirty = false;
81    mMatrixFlags = 0;
82    mPrevWidth = -1;
83    mPrevHeight = -1;
84    mWidth = 0;
85    mHeight = 0;
86    mPivotExplicitlySet = false;
87    mCaching = false;
88}
89
90DisplayList::~DisplayList() {
91    LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
92
93    mDestroyed = true;
94    delete mDisplayListData;
95    delete mTransformMatrix;
96    delete mTransformCamera;
97    delete mTransformMatrix3D;
98    delete mStaticMatrix;
99    delete mAnimationMatrix;
100}
101
102void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
103    if (displayList) {
104        DISPLAY_LIST_LOGD("Deferring display list destruction");
105        Caches::getInstance().deleteDisplayListDeferred(displayList);
106    }
107}
108
109void DisplayList::setData(DisplayListData* data) {
110    delete mDisplayListData;
111    mDisplayListData = data;
112    if (mDisplayListData) {
113        Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
114    }
115}
116
117/**
118 * This function is a simplified version of replay(), where we simply retrieve and log the
119 * display list. This function should remain in sync with the replay() function.
120 */
121void DisplayList::output(uint32_t level) {
122    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
123            mName.string(), isRenderable());
124    ALOGD("%*s%s %d", level * 2, "", "Save",
125            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
126
127    outputViewProperties(level);
128    int flags = DisplayListOp::kOpLogFlag_Recurse;
129    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
130        mDisplayListData->displayListOps[i]->output(level, flags);
131    }
132
133    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
134}
135
136float DisplayList::getPivotX() {
137    updateMatrix();
138    return mPivotX;
139}
140
141float DisplayList::getPivotY() {
142    updateMatrix();
143    return mPivotY;
144}
145
146void DisplayList::updateMatrix() {
147    if (mMatrixDirty) {
148        // NOTE: mTransformMatrix won't be up to date if a DisplayList goes from a complex transform
149        // to a pure translate. This is safe because the matrix isn't read in pure translate cases.
150        if (mMatrixFlags && mMatrixFlags != TRANSLATION) {
151            if (!mTransformMatrix) {
152                // only allocate a matrix if we have a complex transform
153                mTransformMatrix = new Matrix4();
154            }
155            if (!mPivotExplicitlySet) {
156                if (mWidth != mPrevWidth || mHeight != mPrevHeight) {
157                    mPrevWidth = mWidth;
158                    mPrevHeight = mHeight;
159                    mPivotX = mPrevWidth / 2.0f;
160                    mPivotY = mPrevHeight / 2.0f;
161                }
162            }
163            const bool perspectiveEnabled = Caches::getInstance().propertyEnable3d;
164            if (!perspectiveEnabled && (mMatrixFlags & ROTATION_3D) == 0) {
165                mTransformMatrix->loadTranslate(
166                        mPivotX + mTranslationX,
167                        mPivotY + mTranslationY,
168                        0);
169                mTransformMatrix->rotate(mRotation, 0, 0, 1);
170                mTransformMatrix->scale(mScaleX, mScaleY, 1);
171                mTransformMatrix->translate(-mPivotX, -mPivotY);
172            } else {
173                if (perspectiveEnabled) {
174                    mTransformMatrix->loadTranslate(
175                            mPivotX + mTranslationX,
176                            mPivotY + mTranslationY,
177                            mTranslationZ);
178                    mTransformMatrix->rotate(mRotationX, 1, 0, 0);
179                    mTransformMatrix->rotate(mRotationY, 0, 1, 0);
180                    mTransformMatrix->rotate(mRotation, 0, 0, 1);
181                    mTransformMatrix->scale(mScaleX, mScaleY, 1);
182                    mTransformMatrix->translate(-mPivotX, -mPivotY);
183                } else {
184                    /* TODO: support this old transform approach, based on API level */
185                    if (!mTransformCamera) {
186                        mTransformCamera = new Sk3DView();
187                        mTransformMatrix3D = new SkMatrix();
188                    }
189                    SkMatrix transformMatrix;
190                    transformMatrix.reset();
191                    mTransformCamera->save();
192                    transformMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
193                    mTransformCamera->rotateX(mRotationX);
194                    mTransformCamera->rotateY(mRotationY);
195                    mTransformCamera->rotateZ(-mRotation);
196                    mTransformCamera->getMatrix(mTransformMatrix3D);
197                    mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
198                    mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
199                            mPivotY + mTranslationY);
200                    transformMatrix.postConcat(*mTransformMatrix3D);
201                    mTransformCamera->restore();
202
203                    mTransformMatrix->load(transformMatrix);
204                }
205            }
206        }
207        mMatrixDirty = false;
208    }
209}
210
211void DisplayList::outputViewProperties(const int level) {
212    updateMatrix();
213    if (mLeft != 0 || mTop != 0) {
214        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
215    }
216    if (mStaticMatrix) {
217        ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
218                level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix));
219    }
220    if (mAnimationMatrix) {
221        ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
222                level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix));
223    }
224    if (mMatrixFlags != 0) {
225        if (mMatrixFlags == TRANSLATION) {
226            ALOGD("%*sTranslate %.2f, %.2f, %.2f",
227                    level * 2, "", mTranslationX, mTranslationY, mTranslationZ);
228        } else {
229            ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
230                    level * 2, "", mTransformMatrix, MATRIX_4_ARGS(mTransformMatrix));
231        }
232    }
233
234    bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
235    if (mAlpha < 1) {
236        if (mCaching) {
237            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha);
238        } else if (!mHasOverlappingRendering) {
239            ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
240        } else {
241            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
242            if (clipToBoundsNeeded) {
243                flags |= SkCanvas::kClipToLayer_SaveFlag;
244                clipToBoundsNeeded = false; // clipping done by save layer
245            }
246            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
247                    (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
248                    (int)(mAlpha * 255), flags);
249        }
250    }
251    if (clipToBoundsNeeded) {
252        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
253                (float) mRight - mLeft, (float) mBottom - mTop);
254    }
255}
256
257/*
258 * For property operations, we pass a savecount of 0, since the operations aren't part of the
259 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
260 * base saveCount (i.e., how RestoreToCount uses saveCount + mCount)
261 */
262#define PROPERTY_SAVECOUNT 0
263
264template <class T>
265void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
266        const int level) {
267#if DEBUG_DISPLAY_LIST
268    outputViewProperties(level);
269#endif
270    updateMatrix();
271    if (mLeft != 0 || mTop != 0) {
272        renderer.translate(mLeft, mTop);
273    }
274    if (mStaticMatrix) {
275        renderer.concatMatrix(mStaticMatrix);
276    } else if (mAnimationMatrix) {
277        renderer.concatMatrix(mAnimationMatrix);
278    }
279    if (mMatrixFlags != 0) {
280        if (mMatrixFlags == TRANSLATION) {
281            renderer.translate(mTranslationX, mTranslationY,
282                    Caches::getInstance().propertyEnable3d ? mTranslationZ : 0.0f); // TODO: necessary?
283        } else {
284            renderer.concatMatrix(*mTransformMatrix);
285        }
286    }
287    bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
288    if (mAlpha < 1) {
289        if (mCaching) {
290            renderer.setOverrideLayerAlpha(mAlpha);
291        } else if (!mHasOverlappingRendering) {
292            renderer.scaleAlpha(mAlpha);
293        } else {
294            // TODO: should be able to store the size of a DL at record time and not
295            // have to pass it into this call. In fact, this information might be in the
296            // location/size info that we store with the new native transform data.
297            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
298            if (clipToBoundsNeeded) {
299                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
300                clipToBoundsNeeded = false; // clipping done by saveLayer
301            }
302
303            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
304                    0, 0, mRight - mLeft, mBottom - mTop, mAlpha * 255, saveFlags);
305            handler(op, PROPERTY_SAVECOUNT, mClipToBounds);
306        }
307    }
308    if (clipToBoundsNeeded) {
309        ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
310                mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op);
311        handler(op, PROPERTY_SAVECOUNT, mClipToBounds);
312    }
313    if (CC_UNLIKELY(mClipToOutline && !mOutline.isEmpty())) {
314        ClipPathOp* op = new (handler.allocator()) ClipPathOp(&mOutline, SkRegion::kIntersect_Op);
315        handler(op, PROPERTY_SAVECOUNT, mClipToBounds);
316    }
317}
318
319/**
320 * Apply property-based transformations to input matrix
321 */
322void DisplayList::applyViewPropertyTransforms(mat4& matrix) {
323    if (mLeft != 0 || mTop != 0) {
324        matrix.translate(mLeft, mTop);
325    }
326    if (mStaticMatrix) {
327        mat4 stat(*mStaticMatrix);
328        matrix.multiply(stat);
329    } else if (mAnimationMatrix) {
330        mat4 anim(*mAnimationMatrix);
331        matrix.multiply(anim);
332    }
333    if (mMatrixFlags != 0) {
334        updateMatrix();
335        if (mMatrixFlags == TRANSLATION) {
336            matrix.translate(mTranslationX, mTranslationY, mTranslationZ);
337        } else {
338            matrix.multiply(*mTransformMatrix);
339        }
340    }
341}
342
343/**
344 * Organizes the DisplayList hierarchy to prepare for Z-based draw order.
345 *
346 * This should be called before a call to defer() or drawDisplayList()
347 *
348 * Each DisplayList that serves as a 3d root builds its list of composited children,
349 * which are flagged to not draw in the standard draw loop.
350 */
351void DisplayList::computeOrdering() {
352    ATRACE_CALL();
353    m3dNodes.clear();
354    mProjectedNodes.clear();
355
356    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
357    // transform properties are applied correctly to top level children
358    if (mDisplayListData == NULL) return;
359    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
360        DrawDisplayListOp* childOp = mDisplayListData->children[i];
361        childOp->mDisplayList->computeOrderingImpl(childOp,
362                &m3dNodes, &mat4::identity(),
363                &mProjectedNodes, &mat4::identity());
364    }
365}
366
367void DisplayList::computeOrderingImpl(
368        DrawDisplayListOp* opState,
369        Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot,
370        const mat4* transformFrom3dRoot,
371        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
372        const mat4* transformFromProjectionSurface) {
373    m3dNodes.clear();
374    mProjectedNodes.clear();
375    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
376
377    // TODO: should avoid this calculation in most cases
378    // TODO: just calculate single matrix, down to all leaf composited elements
379    Matrix4 localTransformFrom3dRoot(*transformFrom3dRoot);
380    localTransformFrom3dRoot.multiply(opState->mTransformFromParent);
381    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
382    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
383
384    if (mTranslationZ != 0.0f) { // TODO: other signals for 3d compositing, such as custom matrix4
385        // composited 3d layer, flag for out of order draw and save matrix...
386        opState->mSkipInOrderDraw = true;
387        opState->mTransformFromCompositingAncestor.load(localTransformFrom3dRoot);
388
389        // ... and insert into current 3d root, keyed with pivot z for later sorting
390        Vector3 pivot(mPivotX, mPivotY, 0.0f);
391        mat4 totalTransform(localTransformFrom3dRoot);
392        applyViewPropertyTransforms(totalTransform);
393        totalTransform.mapPoint3d(pivot);
394        compositedChildrenOf3dRoot->add(ZDrawDisplayListOpPair(pivot.z, opState));
395    } else if (mProjectBackwards) {
396        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
397        opState->mSkipInOrderDraw = true;
398        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
399        compositedChildrenOfProjectionSurface->add(opState);
400    } else {
401        // standard in order draw
402        opState->mSkipInOrderDraw = false;
403    }
404
405    if (mDisplayListData->children.size() > 0) {
406        if (mIsolatedZVolume) {
407            // create a new 3d space for descendents by collecting them
408            compositedChildrenOf3dRoot = &m3dNodes;
409            transformFrom3dRoot = &mat4::identity();
410        } else {
411            applyViewPropertyTransforms(localTransformFrom3dRoot);
412            transformFrom3dRoot = &localTransformFrom3dRoot;
413        }
414
415        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
416        bool haveAppliedPropertiesToProjection = false;
417        for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
418            DrawDisplayListOp* childOp = mDisplayListData->children[i];
419            DisplayList* child = childOp->mDisplayList;
420
421            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
422            const mat4* projectionTransform = NULL;
423            if (isProjectionReceiver && !child->mProjectBackwards) {
424                // if receiving projections, collect projecting descendent
425
426                // Note that if a direct descendent is projecting backwards, we pass it's
427                // grandparent projection collection, since it shouldn't project onto it's
428                // parent, where it will already be drawing.
429                projectionChildren = &mProjectedNodes;
430                projectionTransform = &mat4::identity();
431            } else {
432                if (!haveAppliedPropertiesToProjection) {
433                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
434                    haveAppliedPropertiesToProjection = true;
435                }
436                projectionChildren = compositedChildrenOfProjectionSurface;
437                projectionTransform = &localTransformFromProjectionSurface;
438            }
439            child->computeOrderingImpl(childOp,
440                    compositedChildrenOf3dRoot, transformFrom3dRoot,
441                    projectionChildren, projectionTransform);
442        }
443    }
444
445}
446
447class DeferOperationHandler {
448public:
449    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
450        : mDeferStruct(deferStruct), mLevel(level) {}
451    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
452        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
453    }
454    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
455
456private:
457    DeferStateStruct& mDeferStruct;
458    const int mLevel;
459};
460
461void DisplayList::defer(DeferStateStruct& deferStruct, const int level) {
462    DeferOperationHandler handler(deferStruct, level);
463    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
464}
465
466class ReplayOperationHandler {
467public:
468    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
469        : mReplayStruct(replayStruct), mLevel(level) {}
470    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
471#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
472        mReplayStruct.mRenderer.eventMark(operation->name());
473#endif
474        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
475    }
476    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
477
478private:
479    ReplayStateStruct& mReplayStruct;
480    const int mLevel;
481};
482
483void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) {
484    ReplayOperationHandler handler(replayStruct, level);
485
486    replayStruct.mRenderer.startMark(mName.string());
487    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
488    replayStruct.mRenderer.endMark();
489
490    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
491            replayStruct.mDrawGlStatus);
492}
493
494#define SHADOW_DELTA 0.1f
495
496template <class T>
497void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
498        T& handler, const int level) {
499    if (m3dNodes.size() == 0 ||
500            (mode == kNegativeZChildren && m3dNodes[0].key > 0.0f) ||
501            (mode == kPositiveZChildren && m3dNodes[m3dNodes.size() - 1].key < 0.0f)) {
502        // no 3d children to draw
503        return;
504    }
505
506    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
507    LinearAllocator& alloc = handler.allocator();
508    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, mWidth, mHeight,
509            SkRegion::kIntersect_Op); // clip to 3d root bounds
510    handler(clipOp, PROPERTY_SAVECOUNT, mClipToBounds);
511
512    /**
513     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
514     * with very similar Z heights to draw together.
515     *
516     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
517     * underneath both, and neither's shadow is drawn on top of the other.
518     */
519    const size_t nonNegativeIndex = findNonNegativeIndex(m3dNodes);
520    size_t drawIndex, shadowIndex, endIndex;
521    if (mode == kNegativeZChildren) {
522        drawIndex = 0;
523        endIndex = nonNegativeIndex;
524        shadowIndex = endIndex; // draw no shadows
525    } else {
526        drawIndex = nonNegativeIndex;
527        endIndex = m3dNodes.size();
528        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
529    }
530    float lastCasterZ = 0.0f;
531    while (shadowIndex < endIndex || drawIndex < endIndex) {
532        if (shadowIndex < endIndex) {
533            DrawDisplayListOp* casterOp = m3dNodes[shadowIndex].value;
534            DisplayList* caster = casterOp->mDisplayList;
535            const float casterZ = m3dNodes[shadowIndex].key;
536            // attempt to render the shadow if the caster about to be drawn is its caster,
537            // OR if its caster's Z value is similar to the previous potential caster
538            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
539
540                if (caster->mCastsShadow && caster->mAlpha > 0.0f) {
541                    mat4 shadowMatrix(casterOp->mTransformFromCompositingAncestor);
542                    caster->applyViewPropertyTransforms(shadowMatrix);
543
544                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(shadowMatrix,
545                            caster->mAlpha, &(caster->mOutline), caster->mWidth, caster->mHeight);
546                    handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds);
547                }
548
549                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
550                shadowIndex++;
551                continue;
552            }
553        }
554
555        // only the actual child DL draw needs to be in save/restore,
556        // since it modifies the renderer's matrix
557        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
558
559        DrawDisplayListOp* childOp = m3dNodes[drawIndex].value;
560        DisplayList* child = childOp->mDisplayList;
561
562        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
563        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
564        handler(childOp, renderer.getSaveCount() - 1, mClipToBounds);
565        childOp->mSkipInOrderDraw = true;
566
567        renderer.restoreToCount(restoreTo);
568        drawIndex++;
569    }
570    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
571}
572
573template <class T>
574void DisplayList::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
575    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
576    LinearAllocator& alloc = handler.allocator();
577    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, mWidth, mHeight,
578            SkRegion::kReplace_Op); // clip to projection surface root bounds
579    handler(clipOp, PROPERTY_SAVECOUNT, mClipToBounds);
580
581    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
582        DrawDisplayListOp* childOp = mProjectedNodes[i];
583
584        // matrix save, concat, and restore can be done safely without allocating operations
585        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
586        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
587        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
588        handler(childOp, renderer.getSaveCount() - 1, mClipToBounds);
589        childOp->mSkipInOrderDraw = true;
590        renderer.restoreToCount(restoreTo);
591    }
592    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
593}
594
595/**
596 * This function serves both defer and replay modes, and will organize the displayList's component
597 * operations for a single frame:
598 *
599 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
600 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
601 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
602 * defer vs replay logic, per operation
603 */
604template <class T>
605void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
606    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
607        ALOGW("Error: %s is drawing after destruction", getName());
608        CRASH();
609    }
610    if (mDisplayListData->isEmpty() || mAlpha <= 0) {
611        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
612        return;
613    }
614
615#if DEBUG_DISPLAY_LIST
616    Rect* clipRect = renderer.getClipRect();
617    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
618            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
619            clipRect->right, clipRect->bottom);
620#endif
621
622    LinearAllocator& alloc = handler.allocator();
623    int restoreTo = renderer.getSaveCount();
624    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
625            PROPERTY_SAVECOUNT, mClipToBounds);
626
627    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
628            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
629
630    setViewProperties<T>(renderer, handler, level + 1);
631
632    bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight);
633    if (!quickRejected) {
634        // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
635        std::stable_sort(m3dNodes.begin(), m3dNodes.end());
636
637        // for 3d root, draw children with negative z values
638        iterate3dChildren(kNegativeZChildren, renderer, handler, level);
639
640        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
641        const int saveCountOffset = renderer.getSaveCount() - 1;
642        const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
643        for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
644            DisplayListOp *op = mDisplayListData->displayListOps[i];
645
646#if DEBUG_DISPLAY_LIST
647            op->output(level + 1);
648#endif
649
650            logBuffer.writeCommand(level, op->name());
651            handler(op, saveCountOffset, mClipToBounds);
652
653            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
654                iterateProjectedChildren(renderer, handler, level);
655            }
656        }
657
658        // for 3d root, draw children with positive z values
659        iterate3dChildren(kPositiveZChildren, renderer, handler, level);
660    }
661
662    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
663    handler(new (alloc) RestoreToCountOp(restoreTo),
664            PROPERTY_SAVECOUNT, mClipToBounds);
665    renderer.setOverrideLayerAlpha(1.0f);
666}
667
668void DisplayListData::cleanupResources() {
669    Caches& caches = Caches::getInstance();
670    caches.unregisterFunctors(functorCount);
671    caches.resourceCache.lock();
672
673    for (size_t i = 0; i < bitmapResources.size(); i++) {
674        caches.resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
675    }
676
677    for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
678        const SkBitmap* bitmap = ownedBitmapResources.itemAt(i);
679        caches.resourceCache.decrementRefcountLocked(bitmap);
680        caches.resourceCache.destructorLocked(bitmap);
681    }
682
683    for (size_t i = 0; i < patchResources.size(); i++) {
684        caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
685    }
686
687    for (size_t i = 0; i < shaders.size(); i++) {
688        caches.resourceCache.decrementRefcountLocked(shaders.itemAt(i));
689        caches.resourceCache.destructorLocked(shaders.itemAt(i));
690    }
691
692    for (size_t i = 0; i < sourcePaths.size(); i++) {
693        caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
694    }
695
696    for (size_t i = 0; i < layers.size(); i++) {
697        caches.resourceCache.decrementRefcountLocked(layers.itemAt(i));
698    }
699
700    caches.resourceCache.unlock();
701
702    for (size_t i = 0; i < paints.size(); i++) {
703        delete paints.itemAt(i);
704    }
705
706    for (size_t i = 0; i < regions.size(); i++) {
707        delete regions.itemAt(i);
708    }
709
710    for (size_t i = 0; i < paths.size(); i++) {
711        delete paths.itemAt(i);
712    }
713
714    for (size_t i = 0; i < matrices.size(); i++) {
715        delete matrices.itemAt(i);
716    }
717
718    bitmapResources.clear();
719    ownedBitmapResources.clear();
720    patchResources.clear();
721    shaders.clear();
722    sourcePaths.clear();
723    paints.clear();
724    regions.clear();
725    paths.clear();
726    matrices.clear();
727    layers.clear();
728}
729
730}; // namespace uirenderer
731}; // namespace android
732