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