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