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