DisplayList.cpp revision d218a92c0afb8c0d98135b20b52ac87236e1c935
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
21#include <utils/Trace.h>
22
23#include "Debug.h"
24#include "DisplayList.h"
25#include "DisplayListOp.h"
26#include "DisplayListLogBuffer.h"
27
28namespace android {
29namespace uirenderer {
30
31void DisplayList::outputLogBuffer(int fd) {
32    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
33    if (logBuffer.isEmpty()) {
34        return;
35    }
36
37    FILE *file = fdopen(fd, "a");
38
39    fprintf(file, "\nRecent DisplayList operations\n");
40    logBuffer.outputCommands(file);
41
42    String8 cachesLog;
43    Caches::getInstance().dumpMemoryUsage(cachesLog);
44    fprintf(file, "\nCaches:\n%s", cachesLog.string());
45    fprintf(file, "\n");
46
47    fflush(file);
48}
49
50DisplayList::DisplayList(const DisplayListRenderer& recorder) :
51    mDestroyed(false), mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
52    mStaticMatrix(NULL), mAnimationMatrix(NULL) {
53
54    initFromDisplayListRenderer(recorder);
55}
56
57DisplayList::~DisplayList() {
58    mDestroyed = true;
59    clearResources();
60}
61
62void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
63    if (displayList) {
64        DISPLAY_LIST_LOGD("Deferring display list destruction");
65        Caches::getInstance().deleteDisplayListDeferred(displayList);
66    }
67}
68
69void DisplayList::clearResources() {
70    mDisplayListData = NULL;
71
72    delete mTransformMatrix;
73    delete mTransformCamera;
74    delete mTransformMatrix3D;
75    delete mStaticMatrix;
76    delete mAnimationMatrix;
77
78    mTransformMatrix = NULL;
79    mTransformCamera = NULL;
80    mTransformMatrix3D = NULL;
81    mStaticMatrix = NULL;
82    mAnimationMatrix = NULL;
83
84    Caches& caches = Caches::getInstance();
85    caches.unregisterFunctors(mFunctorCount);
86    caches.resourceCache.lock();
87
88    for (size_t i = 0; i < mBitmapResources.size(); i++) {
89        caches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
90    }
91
92    for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
93        const SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
94        caches.resourceCache.decrementRefcountLocked(bitmap);
95        caches.resourceCache.destructorLocked(bitmap);
96    }
97
98    for (size_t i = 0; i < mFilterResources.size(); i++) {
99        caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
100    }
101
102    for (size_t i = 0; i < mPatchResources.size(); i++) {
103        caches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i));
104    }
105
106    for (size_t i = 0; i < mShaders.size(); i++) {
107        caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
108        caches.resourceCache.destructorLocked(mShaders.itemAt(i));
109    }
110
111    for (size_t i = 0; i < mSourcePaths.size(); i++) {
112        caches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
113    }
114
115    for (size_t i = 0; i < mLayers.size(); i++) {
116        caches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
117    }
118
119    caches.resourceCache.unlock();
120
121    for (size_t i = 0; i < mPaints.size(); i++) {
122        delete mPaints.itemAt(i);
123    }
124
125    for (size_t i = 0; i < mRegions.size(); i++) {
126        delete mRegions.itemAt(i);
127    }
128
129    for (size_t i = 0; i < mPaths.size(); i++) {
130        delete mPaths.itemAt(i);
131    }
132
133    for (size_t i = 0; i < mMatrices.size(); i++) {
134        delete mMatrices.itemAt(i);
135    }
136
137    mBitmapResources.clear();
138    mOwnedBitmapResources.clear();
139    mFilterResources.clear();
140    mPatchResources.clear();
141    mShaders.clear();
142    mSourcePaths.clear();
143    mPaints.clear();
144    mRegions.clear();
145    mPaths.clear();
146    mMatrices.clear();
147    mLayers.clear();
148}
149
150void DisplayList::reset() {
151    clearResources();
152    init();
153}
154
155void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
156    if (reusing) {
157        // re-using display list - clear out previous allocations
158        clearResources();
159    }
160
161    init();
162
163    mDisplayListData = recorder.getDisplayListData();
164    mSize = mDisplayListData->allocator.usedSize();
165
166    if (mSize == 0) {
167        return;
168    }
169
170    mFunctorCount = recorder.getFunctorCount();
171
172    Caches& caches = Caches::getInstance();
173    caches.registerFunctors(mFunctorCount);
174    caches.resourceCache.lock();
175
176    const Vector<const SkBitmap*>& bitmapResources = recorder.getBitmapResources();
177    for (size_t i = 0; i < bitmapResources.size(); i++) {
178        const SkBitmap* resource = bitmapResources.itemAt(i);
179        mBitmapResources.add(resource);
180        caches.resourceCache.incrementRefcountLocked(resource);
181    }
182
183    const Vector<const SkBitmap*>& ownedBitmapResources = recorder.getOwnedBitmapResources();
184    for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
185        const SkBitmap* resource = ownedBitmapResources.itemAt(i);
186        mOwnedBitmapResources.add(resource);
187        caches.resourceCache.incrementRefcountLocked(resource);
188    }
189
190    const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources();
191    for (size_t i = 0; i < filterResources.size(); i++) {
192        SkiaColorFilter* resource = filterResources.itemAt(i);
193        mFilterResources.add(resource);
194        caches.resourceCache.incrementRefcountLocked(resource);
195    }
196
197    const Vector<const Res_png_9patch*>& patchResources = recorder.getPatchResources();
198    for (size_t i = 0; i < patchResources.size(); i++) {
199        const Res_png_9patch* resource = patchResources.itemAt(i);
200        mPatchResources.add(resource);
201        caches.resourceCache.incrementRefcountLocked(resource);
202    }
203
204    const Vector<SkiaShader*>& shaders = recorder.getShaders();
205    for (size_t i = 0; i < shaders.size(); i++) {
206        SkiaShader* resource = shaders.itemAt(i);
207        mShaders.add(resource);
208        caches.resourceCache.incrementRefcountLocked(resource);
209    }
210
211    const SortedVector<const SkPath*>& sourcePaths = recorder.getSourcePaths();
212    for (size_t i = 0; i < sourcePaths.size(); i++) {
213        mSourcePaths.add(sourcePaths.itemAt(i));
214        caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i));
215    }
216
217    const Vector<Layer*>& layers = recorder.getLayers();
218    for (size_t i = 0; i < layers.size(); i++) {
219        mLayers.add(layers.itemAt(i));
220        caches.resourceCache.incrementRefcountLocked(layers.itemAt(i));
221    }
222
223    caches.resourceCache.unlock();
224
225    mPaints.appendVector(recorder.getPaints());
226    mRegions.appendVector(recorder.getRegions());
227    mPaths.appendVector(recorder.getPaths());
228    mMatrices.appendVector(recorder.getMatrices());
229}
230
231void DisplayList::init() {
232    mSize = 0;
233    mIsRenderable = true;
234    mFunctorCount = 0;
235    mLeft = 0;
236    mTop = 0;
237    mRight = 0;
238    mBottom = 0;
239    mClipToBounds = true;
240    mIsContainedVolume = true;
241    mAlpha = 1;
242    mHasOverlappingRendering = true;
243    mTranslationX = 0;
244    mTranslationY = 0;
245    mTranslationZ = 0;
246    mRotation = 0;
247    mRotationX = 0;
248    mRotationY= 0;
249    mScaleX = 1;
250    mScaleY = 1;
251    mPivotX = 0;
252    mPivotY = 0;
253    mCameraDistance = 0;
254    mMatrixDirty = false;
255    mMatrixFlags = 0;
256    mPrevWidth = -1;
257    mPrevHeight = -1;
258    mWidth = 0;
259    mHeight = 0;
260    mPivotExplicitlySet = false;
261    mCaching = false;
262}
263
264size_t DisplayList::getSize() {
265    return mSize;
266}
267
268/**
269 * This function is a simplified version of replay(), where we simply retrieve and log the
270 * display list. This function should remain in sync with the replay() function.
271 */
272void DisplayList::output(uint32_t level) {
273    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
274            mName.string(), isRenderable());
275    ALOGD("%*s%s %d", level * 2, "", "Save",
276            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
277
278    outputViewProperties(level);
279    int flags = DisplayListOp::kOpLogFlag_Recurse;
280    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
281        mDisplayListData->displayListOps[i]->output(level, flags);
282    }
283
284    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
285}
286
287float DisplayList::getPivotX() {
288    updateMatrix();
289    return mPivotX;
290}
291
292float DisplayList::getPivotY() {
293    updateMatrix();
294    return mPivotY;
295}
296
297void DisplayList::updateMatrix() {
298    if (mMatrixDirty) {
299        if (!mTransformMatrix) {
300            mTransformMatrix = new SkMatrix();
301        }
302        if (mMatrixFlags == 0 || mMatrixFlags == TRANSLATION) {
303            mTransformMatrix->reset();
304        } else {
305            if (!mPivotExplicitlySet) {
306                if (mWidth != mPrevWidth || mHeight != mPrevHeight) {
307                    mPrevWidth = mWidth;
308                    mPrevHeight = mHeight;
309                    mPivotX = mPrevWidth / 2.0f;
310                    mPivotY = mPrevHeight / 2.0f;
311                }
312            }
313            if (!Caches::getInstance().propertyEnable3d && (mMatrixFlags & ROTATION_3D) == 0) {
314                mTransformMatrix->setTranslate(mTranslationX, mTranslationY);
315                mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY);
316                mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
317            } else {
318                if (Caches::getInstance().propertyEnable3d) {
319                    mTransform.loadTranslate(mPivotX + mTranslationX, mPivotY + mTranslationY,
320                            mTranslationZ);
321                    mTransform.rotate(mRotationX, 1, 0, 0);
322                    mTransform.rotate(mRotationY, 0, 1, 0);
323                    mTransform.rotate(mRotation, 0, 0, 1);
324                    mTransform.scale(mScaleX, mScaleY, 1);
325                    mTransform.translate(-mPivotX, -mPivotY);
326                } else {
327                    /* TODO: support this old transform approach, based on API level */
328                    if (!mTransformCamera) {
329                        mTransformCamera = new Sk3DView();
330                        mTransformMatrix3D = new SkMatrix();
331                    }
332                    mTransformMatrix->reset();
333                    mTransformCamera->save();
334                    mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
335                    mTransformCamera->rotateX(mRotationX);
336                    mTransformCamera->rotateY(mRotationY);
337                    mTransformCamera->rotateZ(-mRotation);
338                    mTransformCamera->getMatrix(mTransformMatrix3D);
339                    mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
340                    mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
341                            mPivotY + mTranslationY);
342                    mTransformMatrix->postConcat(*mTransformMatrix3D);
343                    mTransformCamera->restore();
344                }
345            }
346        }
347        mMatrixDirty = false;
348    }
349}
350
351void DisplayList::outputViewProperties(const int level) {
352    updateMatrix();
353    if (mLeft != 0 || mTop != 0) {
354        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
355    }
356    if (mStaticMatrix) {
357        ALOGD("%*sConcatMatrix (static) %p: " MATRIX_STRING,
358                level * 2, "", mStaticMatrix, MATRIX_ARGS(mStaticMatrix));
359    }
360    if (mAnimationMatrix) {
361        ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING,
362                level * 2, "", mAnimationMatrix, MATRIX_ARGS(mAnimationMatrix));
363    }
364    if (mMatrixFlags != 0) {
365        if (mMatrixFlags == TRANSLATION) {
366            ALOGD("%*sTranslate %f, %f", level * 2, "", mTranslationX, mTranslationY);
367        } else {
368            ALOGD("%*sConcatMatrix %p: " MATRIX_STRING,
369                    level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
370        }
371    }
372
373    bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
374    if (mAlpha < 1) {
375        if (mCaching) {
376            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha);
377        } else if (!mHasOverlappingRendering) {
378            ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
379        } else {
380            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
381            if (clipToBoundsNeeded) {
382                flags |= SkCanvas::kClipToLayer_SaveFlag;
383                clipToBoundsNeeded = false; // clipping done by save layer
384            }
385            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
386                    (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
387                    (int)(mAlpha * 255), flags);
388        }
389    }
390    if (clipToBoundsNeeded) {
391        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
392                (float) mRight - mLeft, (float) mBottom - mTop);
393    }
394}
395
396/*
397 * For property operations, we pass a savecount of 0, since the operations aren't part of the
398 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
399 * base saveCount (i.e., how RestoreToCount uses saveCount + mCount)
400 */
401#define PROPERTY_SAVECOUNT 0
402
403template <class T>
404void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
405        const int level) {
406#if DEBUG_DISPLAY_LIST
407    outputViewProperties(level);
408#endif
409    updateMatrix();
410    if (mLeft != 0 || mTop != 0) {
411        renderer.translate(mLeft, mTop);
412    }
413    if (mStaticMatrix) {
414        renderer.concatMatrix(mStaticMatrix);
415    } else if (mAnimationMatrix) {
416        renderer.concatMatrix(mAnimationMatrix);
417    }
418    if (mMatrixFlags != 0) {
419        if (mMatrixFlags == TRANSLATION) {
420            renderer.translate(mTranslationX, mTranslationY, mTranslationZ);
421        } else {
422            if (Caches::getInstance().propertyEnable3d) {
423                renderer.concatMatrix(mTransform);
424            } else {
425                renderer.concatMatrix(mTransformMatrix);
426            }
427        }
428    }
429    bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
430    if (mAlpha < 1) {
431        if (mCaching) {
432            renderer.setOverrideLayerAlpha(mAlpha);
433        } else if (!mHasOverlappingRendering) {
434            renderer.scaleAlpha(mAlpha);
435        } else {
436            // TODO: should be able to store the size of a DL at record time and not
437            // have to pass it into this call. In fact, this information might be in the
438            // location/size info that we store with the new native transform data.
439            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
440            if (clipToBoundsNeeded) {
441                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
442                clipToBoundsNeeded = false; // clipping done by saveLayer
443            }
444
445            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
446                    0, 0, mRight - mLeft, mBottom - mTop,
447                    mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags);
448            handler(op, PROPERTY_SAVECOUNT, mClipToBounds);
449        }
450    }
451    if (clipToBoundsNeeded) {
452        ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
453                mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op);
454        handler(op, PROPERTY_SAVECOUNT, mClipToBounds);
455    }
456}
457
458/**
459 * Apply property-based transformations to input matrix
460 */
461void DisplayList::applyViewPropertyTransforms(mat4& matrix) {
462    if (mLeft != 0 || mTop != 0) {
463        matrix.translate(mLeft, mTop);
464    }
465    if (mStaticMatrix) {
466        mat4 stat(*mStaticMatrix);
467        matrix.multiply(stat);
468    } else if (mAnimationMatrix) {
469        mat4 anim(*mAnimationMatrix);
470        matrix.multiply(anim);
471    }
472    if (mMatrixFlags != 0) {
473        if (mMatrixFlags == TRANSLATION) {
474            matrix.translate(mTranslationX, mTranslationY, mTranslationZ);
475        } else {
476            if (Caches::getInstance().propertyEnable3d) {
477                matrix.multiply(mTransform);
478            } else {
479                mat4 temp(*mTransformMatrix);
480                matrix.multiply(temp);
481            }
482        }
483    }
484}
485
486/**
487 * Organizes the DisplayList hierarchy to prepare for Z-based draw order.
488 *
489 * This should be called before a call to defer() or drawDisplayList()
490 *
491 * Each DisplayList that serves as a 3d root builds its list of composited children,
492 * which are flagged to not draw in the standard draw loop.
493 */
494void DisplayList::computeOrdering() {
495    ATRACE_CALL();
496    if (mDisplayListData == NULL) return;
497
498    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
499        DrawDisplayListOp* childOp = mDisplayListData->children[i];
500        childOp->mDisplayList->computeOrderingImpl(childOp, &m3dNodes, &mat4::identity());
501    }
502}
503
504void DisplayList::computeOrderingImpl(
505        DrawDisplayListOp* opState,
506        KeyedVector<float, Vector<DrawDisplayListOp*> >* compositedChildrenOf3dRoot,
507        const mat4* transformFrom3dRoot) {
508
509    // TODO: should avoid this calculation in most cases
510    opState->mTransformFrom3dRoot.load(*transformFrom3dRoot);
511    opState->mTransformFrom3dRoot.multiply(opState->mTransformFromParent);
512
513    if (mTranslationZ != 0.0f) { // TODO: other signals, such as custom 4x4 matrix
514        // composited layer, insert into current 3d root and flag for out of order draw
515        opState->mSkipInOrderDraw = true;
516
517        Vector3 pivot(mPivotX, mPivotY, 0.0f);
518        mat4 totalTransform(opState->mTransformFrom3dRoot);
519        applyViewPropertyTransforms(totalTransform);
520        totalTransform.mapPoint3d(pivot);
521        const float key = pivot.z;
522
523        if (compositedChildrenOf3dRoot->indexOfKey(key) < 0) {
524            compositedChildrenOf3dRoot->add(key, Vector<DrawDisplayListOp*>());
525        }
526        compositedChildrenOf3dRoot->editValueFor(key).push(opState);
527    } else {
528        // standard in order draw
529        opState->mSkipInOrderDraw = false;
530    }
531
532    m3dNodes.clear();
533    if (mIsContainedVolume) {
534        // create a new 3d space for children by separating their ordering
535        compositedChildrenOf3dRoot = &m3dNodes;
536        transformFrom3dRoot = &mat4::identity();
537    } else {
538        transformFrom3dRoot = &(opState->mTransformFrom3dRoot);
539    }
540
541    if (mDisplayListData != NULL && mDisplayListData->children.size() > 0) {
542        for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
543            DrawDisplayListOp* childOp = mDisplayListData->children[i];
544            childOp->mDisplayList->computeOrderingImpl(childOp,
545                    compositedChildrenOf3dRoot, transformFrom3dRoot);
546        }
547    }
548}
549
550class DeferOperationHandler {
551public:
552    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
553        : mDeferStruct(deferStruct), mLevel(level) {}
554    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
555        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
556    }
557    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
558
559private:
560    DeferStateStruct& mDeferStruct;
561    const int mLevel;
562};
563
564void DisplayList::defer(DeferStateStruct& deferStruct, const int level) {
565    DeferOperationHandler handler(deferStruct, level);
566    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
567}
568
569class ReplayOperationHandler {
570public:
571    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
572        : mReplayStruct(replayStruct), mLevel(level) {}
573    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
574#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
575        mReplayStruct.mRenderer.eventMark(operation->name());
576#endif
577        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
578    }
579    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
580
581private:
582    ReplayStateStruct& mReplayStruct;
583    const int mLevel;
584};
585
586void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) {
587    ReplayOperationHandler handler(replayStruct, level);
588
589    replayStruct.mRenderer.startMark(mName.string());
590    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
591    replayStruct.mRenderer.endMark();
592
593    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
594            replayStruct.mDrawGlStatus);
595}
596
597template <class T>
598void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
599        T& handler, const int level) {
600    if (m3dNodes.size() == 0 ||
601            (mode == kNegativeZChildren && m3dNodes.keyAt(0) > 0.0f) ||
602            (mode == kPositiveZChildren && m3dNodes.keyAt(m3dNodes.size() - 1) < 0.0f)) {
603        // nothing to draw
604        return;
605    }
606
607    LinearAllocator& alloc = handler.allocator();
608    ClipRectOp* op = new (alloc) ClipRectOp(0, 0, mWidth, mHeight,
609            SkRegion::kIntersect_Op); // clip to 3d root bounds for now
610    handler(op, PROPERTY_SAVECOUNT, mClipToBounds);
611    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
612
613    for (size_t i = 0; i < m3dNodes.size(); i++) {
614        const float zValue = m3dNodes.keyAt(i);
615
616        if (mode == kPositiveZChildren && zValue < 0.0f) continue;
617        if (mode == kNegativeZChildren && zValue > 0.0f) break;
618
619        const Vector<DrawDisplayListOp*>& nodesAtZ = m3dNodes[i];
620        for (size_t j = 0; j < nodesAtZ.size(); j++) {
621            DrawDisplayListOp* op = nodesAtZ[j];
622            if (mode == kPositiveZChildren) {
623                /* draw shadow on renderer with parent matrix applied, passing in the child's total matrix
624                 *
625                 * TODO:
626                 * -determine and pass background shape (and possibly drawable alpha)
627                 * -view must opt-in to shadows
628                 * -consider shadows for other content
629                 */
630                mat4 shadowMatrix(op->mTransformFrom3dRoot);
631                op->mDisplayList->applyViewPropertyTransforms(shadowMatrix);
632                DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(shadowMatrix, op->mDisplayList->mAlpha,
633                        op->mDisplayList->getWidth(), op->mDisplayList->getHeight());
634                handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds);
635            }
636
637            renderer.concatMatrix(op->mTransformFrom3dRoot);
638            op->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
639            handler(op, renderer.getSaveCount() - 1, mClipToBounds);
640            op->mSkipInOrderDraw = true;
641        }
642    }
643    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
644}
645
646/**
647 * This function serves both defer and replay modes, and will organize the displayList's component
648 * operations for a single frame:
649 *
650 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
651 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
652 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
653 * defer vs replay logic, per operation
654 */
655template <class T>
656void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
657    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
658        ALOGW("Error: %s is drawing after destruction, size %d", getName(), mSize);
659        CRASH();
660    }
661    if (mSize == 0 || mAlpha <= 0) {
662        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
663        return;
664    }
665
666#if DEBUG_DISPLAY_LIST
667    Rect* clipRect = renderer.getClipRect();
668    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
669            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
670            clipRect->right, clipRect->bottom);
671#endif
672
673    LinearAllocator& alloc = handler.allocator();
674    int restoreTo = renderer.getSaveCount();
675    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
676            PROPERTY_SAVECOUNT, mClipToBounds);
677
678    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
679            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
680
681    setViewProperties<T>(renderer, handler, level + 1);
682
683    bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight);
684    if (!quickRejected) {
685        // for 3d root, draw children with negative z values
686        iterate3dChildren(kNegativeZChildren, renderer, handler, level);
687
688        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
689        const int saveCountOffset = renderer.getSaveCount() - 1;
690        for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
691            DisplayListOp *op = mDisplayListData->displayListOps[i];
692
693#if DEBUG_DISPLAY_LIST
694            op->output(level + 1);
695#endif
696
697            logBuffer.writeCommand(level, op->name());
698            handler(op, saveCountOffset, mClipToBounds);
699        }
700
701        // for 3d root, draw children with positive z values
702        iterate3dChildren(kPositiveZChildren, renderer, handler, level);
703    }
704
705    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
706    handler(new (alloc) RestoreToCountOp(restoreTo),
707            PROPERTY_SAVECOUNT, mClipToBounds);
708    renderer.setOverrideLayerAlpha(1.0f);
709}
710
711}; // namespace uirenderer
712}; // namespace android
713