RenderNode.cpp revision 52622668f91c69ec718b356d2e0f8555fc88435f
1/*
2 * Copyright (C) 2014 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 "RenderNode.h"
20
21#include <algorithm>
22
23#include <SkCanvas.h>
24#include <algorithm>
25
26#include <utils/Trace.h>
27
28#include "Debug.h"
29#include "DisplayListOp.h"
30#include "DisplayListLogBuffer.h"
31#include "utils/MathUtils.h"
32
33namespace android {
34namespace uirenderer {
35
36void RenderNode::outputLogBuffer(int fd) {
37    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
38    if (logBuffer.isEmpty()) {
39        return;
40    }
41
42    FILE *file = fdopen(fd, "a");
43
44    fprintf(file, "\nRecent DisplayList operations\n");
45    logBuffer.outputCommands(file);
46
47    String8 cachesLog;
48    Caches::getInstance().dumpMemoryUsage(cachesLog);
49    fprintf(file, "\nCaches:\n%s", cachesLog.string());
50    fprintf(file, "\n");
51
52    fflush(file);
53}
54
55RenderNode::RenderNode()
56        : mNeedsPropertiesSync(false)
57        , mNeedsDisplayListDataSync(false)
58        , mDisplayListData(0)
59        , mStagingDisplayListData(0)
60        , mNeedsAnimatorsSync(false) {
61}
62
63RenderNode::~RenderNode() {
64    delete mDisplayListData;
65    delete mStagingDisplayListData;
66}
67
68void RenderNode::setStagingDisplayList(DisplayListData* data) {
69    mNeedsDisplayListDataSync = true;
70    delete mStagingDisplayListData;
71    mStagingDisplayListData = data;
72    if (mStagingDisplayListData) {
73        Caches::getInstance().registerFunctors(mStagingDisplayListData->functorCount);
74    }
75}
76
77/**
78 * This function is a simplified version of replay(), where we simply retrieve and log the
79 * display list. This function should remain in sync with the replay() function.
80 */
81void RenderNode::output(uint32_t level) {
82    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
83            getName(), isRenderable());
84    ALOGD("%*s%s %d", level * 2, "", "Save",
85            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
86
87    properties().debugOutputProperties(level);
88    int flags = DisplayListOp::kOpLogFlag_Recurse;
89    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
90        mDisplayListData->displayListOps[i]->output(level, flags);
91    }
92
93    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
94}
95
96void RenderNode::prepareTree(TreeInfo& info) {
97    ATRACE_CALL();
98
99    prepareTreeImpl(info);
100}
101
102void RenderNode::prepareTreeImpl(TreeInfo& info) {
103    if (info.performStagingPush) {
104        pushStagingChanges(info);
105    }
106    if (info.evaluateAnimations) {
107        evaluateAnimations(info);
108    }
109    prepareSubTree(info, mDisplayListData);
110}
111
112static bool is_finished(const sp<RenderPropertyAnimator>& animator) {
113    return animator->isFinished();
114}
115
116void RenderNode::pushStagingChanges(TreeInfo& info) {
117    if (mNeedsPropertiesSync) {
118        mNeedsPropertiesSync = false;
119        mProperties = mStagingProperties;
120    }
121    if (mNeedsAnimatorsSync) {
122        mAnimators.resize(mStagingAnimators.size());
123        std::vector< sp<RenderPropertyAnimator> >::iterator it;
124        // hint: this means copy_if_not()
125        it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(),
126                mAnimators.begin(), is_finished);
127        mAnimators.resize(std::distance(mAnimators.begin(), it));
128    }
129    if (mNeedsDisplayListDataSync) {
130        mNeedsDisplayListDataSync = false;
131        // Do a push pass on the old tree to handle freeing DisplayListData
132        // that are no longer used
133        TreeInfo oldTreeInfo;
134        prepareSubTree(oldTreeInfo, mDisplayListData);
135        // TODO: The damage for the old tree should be accounted for
136        delete mDisplayListData;
137        mDisplayListData = mStagingDisplayListData;
138        mStagingDisplayListData = 0;
139    }
140}
141
142class AnimateFunctor {
143public:
144    AnimateFunctor(RenderProperties* target, TreeInfo& info)
145            : mTarget(target), mInfo(info) {}
146
147    bool operator() (sp<RenderPropertyAnimator>& animator) {
148        bool finished = animator->animate(mTarget, mInfo);
149        if (finished && mInfo.animationListener) {
150            mInfo.animationListener->onAnimationFinished(animator);
151        }
152        return finished;
153    }
154private:
155    RenderProperties* mTarget;
156    TreeInfo& mInfo;
157};
158
159void RenderNode::evaluateAnimations(TreeInfo& info) {
160    if (!mAnimators.size()) return;
161
162    AnimateFunctor functor(&mProperties, info);
163    std::vector< sp<RenderPropertyAnimator> >::iterator newEnd;
164    newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
165    mAnimators.erase(newEnd, mAnimators.end());
166    mProperties.updateMatrix();
167    info.hasAnimations |= mAnimators.size();
168}
169
170void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
171    if (subtree) {
172        TextureCache& cache = Caches::getInstance().textureCache;
173        info.hasFunctors |= subtree->functorCount;
174        // TODO: Fix ownedBitmapResources to not require disabling prepareTextures
175        // and thus falling out of async drawing path.
176        if (subtree->ownedBitmapResources.size()) {
177            info.prepareTextures = false;
178        }
179        for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
180            info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
181        }
182        for (size_t i = 0; i < subtree->children().size(); i++) {
183            RenderNode* childNode = subtree->children()[i]->mDisplayList;
184            childNode->prepareTreeImpl(info);
185        }
186    }
187}
188
189/*
190 * For property operations, we pass a savecount of 0, since the operations aren't part of the
191 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
192 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount())
193 */
194#define PROPERTY_SAVECOUNT 0
195
196template <class T>
197void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
198#if DEBUG_DISPLAY_LIST
199    properties().debugOutputProperties(handler.level() + 1);
200#endif
201    if (properties().getLeft() != 0 || properties().getTop() != 0) {
202        renderer.translate(properties().getLeft(), properties().getTop());
203    }
204    if (properties().getStaticMatrix()) {
205        renderer.concatMatrix(properties().getStaticMatrix());
206    } else if (properties().getAnimationMatrix()) {
207        renderer.concatMatrix(properties().getAnimationMatrix());
208    }
209    if (properties().hasTransformMatrix()) {
210        if (properties().isTransformTranslateOnly()) {
211            renderer.translate(properties().getTranslationX(), properties().getTranslationY());
212        } else {
213            renderer.concatMatrix(*properties().getTransformMatrix());
214        }
215    }
216    bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds();
217    if (properties().getAlpha() < 1) {
218        if (properties().getCaching()) {
219            renderer.setOverrideLayerAlpha(properties().getAlpha());
220        } else if (!properties().getHasOverlappingRendering()) {
221            renderer.scaleAlpha(properties().getAlpha());
222        } else {
223            // TODO: should be able to store the size of a DL at record time and not
224            // have to pass it into this call. In fact, this information might be in the
225            // location/size info that we store with the new native transform data.
226            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
227            if (clipToBoundsNeeded) {
228                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
229                clipToBoundsNeeded = false; // clipping done by saveLayer
230            }
231
232            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
233                    0, 0, properties().getWidth(), properties().getHeight(),
234                    properties().getAlpha() * 255, saveFlags);
235            handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
236        }
237    }
238    if (clipToBoundsNeeded) {
239        ClipRectOp* op = new (handler.allocator()) ClipRectOp(
240                0, 0, properties().getWidth(), properties().getHeight(), SkRegion::kIntersect_Op);
241        handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
242    }
243
244    if (CC_UNLIKELY(properties().hasClippingPath())) {
245        // TODO: optimize for round rect/circle clipping
246        const SkPath* path = properties().getClippingPath();
247        ClipPathOp* op = new (handler.allocator()) ClipPathOp(path, SkRegion::kIntersect_Op);
248        handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
249    }
250}
251
252/**
253 * Apply property-based transformations to input matrix
254 *
255 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
256 * matrix computation instead of the Skia 3x3 matrix + camera hackery.
257 */
258void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
259    if (properties().getLeft() != 0 || properties().getTop() != 0) {
260        matrix.translate(properties().getLeft(), properties().getTop());
261    }
262    if (properties().getStaticMatrix()) {
263        mat4 stat(*properties().getStaticMatrix());
264        matrix.multiply(stat);
265    } else if (properties().getAnimationMatrix()) {
266        mat4 anim(*properties().getAnimationMatrix());
267        matrix.multiply(anim);
268    }
269
270    bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
271    if (properties().hasTransformMatrix() || applyTranslationZ) {
272        if (properties().isTransformTranslateOnly()) {
273            matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
274                    true3dTransform ? properties().getZ() : 0.0f);
275        } else {
276            if (!true3dTransform) {
277                matrix.multiply(*properties().getTransformMatrix());
278            } else {
279                mat4 true3dMat;
280                true3dMat.loadTranslate(
281                        properties().getPivotX() + properties().getTranslationX(),
282                        properties().getPivotY() + properties().getTranslationY(),
283                        properties().getZ());
284                true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
285                true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
286                true3dMat.rotate(properties().getRotation(), 0, 0, 1);
287                true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
288                true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
289
290                matrix.multiply(true3dMat);
291            }
292        }
293    }
294}
295
296/**
297 * Organizes the DisplayList hierarchy to prepare for background projection reordering.
298 *
299 * This should be called before a call to defer() or drawDisplayList()
300 *
301 * Each DisplayList that serves as a 3d root builds its list of composited children,
302 * which are flagged to not draw in the standard draw loop.
303 */
304void RenderNode::computeOrdering() {
305    ATRACE_CALL();
306    mProjectedNodes.clear();
307
308    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
309    // transform properties are applied correctly to top level children
310    if (mDisplayListData == NULL) return;
311    for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
312        DrawDisplayListOp* childOp = mDisplayListData->children()[i];
313        childOp->mDisplayList->computeOrderingImpl(childOp,
314                properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
315    }
316}
317
318void RenderNode::computeOrderingImpl(
319        DrawDisplayListOp* opState,
320        const SkPath* outlineOfProjectionSurface,
321        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
322        const mat4* transformFromProjectionSurface) {
323    mProjectedNodes.clear();
324    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
325
326    // TODO: should avoid this calculation in most cases
327    // TODO: just calculate single matrix, down to all leaf composited elements
328    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
329    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
330
331    if (properties().getProjectBackwards()) {
332        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
333        opState->mSkipInOrderDraw = true;
334        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
335        compositedChildrenOfProjectionSurface->add(opState);
336    } else {
337        // standard in order draw
338        opState->mSkipInOrderDraw = false;
339    }
340
341    if (mDisplayListData->children().size() > 0) {
342        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
343        bool haveAppliedPropertiesToProjection = false;
344        for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
345            DrawDisplayListOp* childOp = mDisplayListData->children()[i];
346            RenderNode* child = childOp->mDisplayList;
347
348            const SkPath* projectionOutline = NULL;
349            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
350            const mat4* projectionTransform = NULL;
351            if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
352                // if receiving projections, collect projecting descendent
353
354                // Note that if a direct descendent is projecting backwards, we pass it's
355                // grandparent projection collection, since it shouldn't project onto it's
356                // parent, where it will already be drawing.
357                projectionOutline = properties().getOutline().getPath();
358                projectionChildren = &mProjectedNodes;
359                projectionTransform = &mat4::identity();
360            } else {
361                if (!haveAppliedPropertiesToProjection) {
362                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
363                    haveAppliedPropertiesToProjection = true;
364                }
365                projectionOutline = outlineOfProjectionSurface;
366                projectionChildren = compositedChildrenOfProjectionSurface;
367                projectionTransform = &localTransformFromProjectionSurface;
368            }
369            child->computeOrderingImpl(childOp,
370                    projectionOutline, projectionChildren, projectionTransform);
371        }
372    }
373}
374
375class DeferOperationHandler {
376public:
377    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
378        : mDeferStruct(deferStruct), mLevel(level) {}
379    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
380        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
381    }
382    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
383    inline void startMark(const char* name) {} // do nothing
384    inline void endMark() {}
385    inline int level() { return mLevel; }
386    inline int replayFlags() { return mDeferStruct.mReplayFlags; }
387
388private:
389    DeferStateStruct& mDeferStruct;
390    const int mLevel;
391};
392
393void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) {
394    DeferOperationHandler handler(deferStruct, 0);
395    if (MathUtils::isPositive(properties().getZ())) {
396        issueDrawShadowOperation(Matrix4::identity(), handler);
397    }
398    issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
399}
400
401void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) {
402    DeferOperationHandler handler(deferStruct, level);
403    issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
404}
405
406class ReplayOperationHandler {
407public:
408    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
409        : mReplayStruct(replayStruct), mLevel(level) {}
410    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
411#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
412        mReplayStruct.mRenderer.eventMark(operation->name());
413#endif
414        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
415    }
416    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
417    inline void startMark(const char* name) {
418        mReplayStruct.mRenderer.startMark(name);
419    }
420    inline void endMark() {
421        mReplayStruct.mRenderer.endMark();
422    }
423    inline int level() { return mLevel; }
424    inline int replayFlags() { return mReplayStruct.mReplayFlags; }
425
426private:
427    ReplayStateStruct& mReplayStruct;
428    const int mLevel;
429};
430
431void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) {
432    ReplayOperationHandler handler(replayStruct, 0);
433    if (MathUtils::isPositive(properties().getZ())) {
434        issueDrawShadowOperation(Matrix4::identity(), handler);
435    }
436    issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
437}
438
439void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) {
440    ReplayOperationHandler handler(replayStruct, level);
441    issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
442}
443
444void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
445    if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return;
446
447    for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
448        DrawDisplayListOp* childOp = mDisplayListData->children()[i];
449        RenderNode* child = childOp->mDisplayList;
450        float childZ = child->properties().getZ();
451
452        if (!MathUtils::isZero(childZ)) {
453            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
454            childOp->mSkipInOrderDraw = true;
455        } else if (!child->properties().getProjectBackwards()) {
456            // regular, in order drawing DisplayList
457            childOp->mSkipInOrderDraw = false;
458        }
459    }
460
461    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
462    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
463}
464
465template <class T>
466void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
467    if (properties().getAlpha() <= 0.0f) return;
468
469    mat4 shadowMatrixXY(transformFromParent);
470    applyViewPropertyTransforms(shadowMatrixXY);
471
472    // Z matrix needs actual 3d transformation, so mapped z values will be correct
473    mat4 shadowMatrixZ(transformFromParent);
474    applyViewPropertyTransforms(shadowMatrixZ, true);
475
476    const SkPath* outlinePath = properties().getOutline().getPath();
477    const RevealClip& revealClip = properties().getRevealClip();
478    const SkPath* revealClipPath = revealClip.hasConvexClip()
479            ?  revealClip.getPath() : NULL; // only pass the reveal clip's path if it's convex
480
481    /**
482     * The drawing area of the caster is always the same as the its perimeter (which
483     * the shadow system uses) *except* in the inverse clip case. Inform the shadow
484     * system that the caster's drawing area (as opposed to its perimeter) has been
485     * clipped, so that it knows the caster can't be opaque.
486     */
487    bool casterUnclipped = !revealClip.willClip() || revealClip.hasConvexClip();
488
489    DisplayListOp* shadowOp  = new (handler.allocator()) DrawShadowOp(
490            shadowMatrixXY, shadowMatrixZ,
491            properties().getAlpha(), casterUnclipped,
492            properties().getWidth(), properties().getHeight(),
493            outlinePath, revealClipPath);
494    handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
495}
496
497#define SHADOW_DELTA 0.1f
498
499template <class T>
500void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
501        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
502    const int size = zTranslatedNodes.size();
503    if (size == 0
504            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
505            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
506        // no 3d children to draw
507        return;
508    }
509
510    /**
511     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
512     * with very similar Z heights to draw together.
513     *
514     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
515     * underneath both, and neither's shadow is drawn on top of the other.
516     */
517    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
518    size_t drawIndex, shadowIndex, endIndex;
519    if (mode == kNegativeZChildren) {
520        drawIndex = 0;
521        endIndex = nonNegativeIndex;
522        shadowIndex = endIndex; // draw no shadows
523    } else {
524        drawIndex = nonNegativeIndex;
525        endIndex = size;
526        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
527    }
528
529    DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
530            endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
531
532    float lastCasterZ = 0.0f;
533    while (shadowIndex < endIndex || drawIndex < endIndex) {
534        if (shadowIndex < endIndex) {
535            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
536            RenderNode* caster = casterOp->mDisplayList;
537            const float casterZ = zTranslatedNodes[shadowIndex].key;
538            // attempt to render the shadow if the caster about to be drawn is its caster,
539            // OR if its caster's Z value is similar to the previous potential caster
540            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
541                caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler);
542
543                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
544                shadowIndex++;
545                continue;
546            }
547        }
548
549        // only the actual child DL draw needs to be in save/restore,
550        // since it modifies the renderer's matrix
551        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
552
553        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
554        RenderNode* child = childOp->mDisplayList;
555
556        renderer.concatMatrix(childOp->mTransformFromParent);
557        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
558        handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
559        childOp->mSkipInOrderDraw = true;
560
561        renderer.restoreToCount(restoreTo);
562        drawIndex++;
563    }
564}
565
566template <class T>
567void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
568    DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
569    const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
570    bool maskProjecteesWithPath = projectionReceiverOutline != NULL
571            && !projectionReceiverOutline->isRect(NULL);
572    int restoreTo = renderer.getSaveCount();
573
574    // If the projection reciever has an outline, we mask each of the projected rendernodes to it
575    // Either with clipRect, or special saveLayer masking
576    LinearAllocator& alloc = handler.allocator();
577    if (projectionReceiverOutline != NULL) {
578        const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
579        if (projectionReceiverOutline->isRect(NULL)) {
580            // mask to the rect outline simply with clipRect
581            handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
582                    PROPERTY_SAVECOUNT, properties().getClipToBounds());
583            ClipRectOp* clipOp = new (alloc) ClipRectOp(
584                    outlineBounds.left(), outlineBounds.top(),
585                    outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
586            handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
587        } else {
588            // wrap the projected RenderNodes with a SaveLayer that will mask to the outline
589            SaveLayerOp* op = new (alloc) SaveLayerOp(
590                    outlineBounds.left(), outlineBounds.top(),
591                    outlineBounds.right(), outlineBounds.bottom(),
592                    255, SkCanvas::kARGB_ClipLayer_SaveFlag);
593            op->setMask(projectionReceiverOutline);
594            handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
595
596            /* TODO: add optimizations here to take advantage of placement/size of projected
597             * children (which may shrink saveLayer area significantly). This is dependent on
598             * passing actual drawing/dirtying bounds of projected content down to native.
599             */
600        }
601    }
602
603    // draw projected nodes
604    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
605        DrawDisplayListOp* childOp = mProjectedNodes[i];
606
607        // matrix save, concat, and restore can be done safely without allocating operations
608        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
609        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
610        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
611        handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
612        childOp->mSkipInOrderDraw = true;
613        renderer.restoreToCount(restoreTo);
614    }
615
616    if (projectionReceiverOutline != NULL) {
617        handler(new (alloc) RestoreToCountOp(restoreTo),
618                PROPERTY_SAVECOUNT, properties().getClipToBounds());
619    }
620}
621
622/**
623 * This function serves both defer and replay modes, and will organize the displayList's component
624 * operations for a single frame:
625 *
626 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
627 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
628 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
629 * defer vs replay logic, per operation
630 */
631template <class T>
632void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
633    const int level = handler.level();
634    if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
635        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
636        return;
637    }
638
639    handler.startMark(getName());
640
641#if DEBUG_DISPLAY_LIST
642    const Rect& clipRect = renderer.getLocalClipBounds();
643    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
644            level * 2, "", this, getName(),
645            clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
646#endif
647
648    LinearAllocator& alloc = handler.allocator();
649    int restoreTo = renderer.getSaveCount();
650    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
651            PROPERTY_SAVECOUNT, properties().getClipToBounds());
652
653    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
654            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
655
656    setViewProperties<T>(renderer, handler);
657
658    bool quickRejected = properties().getClipToBounds()
659            && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
660    if (!quickRejected) {
661        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
662        buildZSortedChildList(zTranslatedNodes);
663
664        // for 3d root, draw children with negative z values
665        issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
666
667        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
668        const int saveCountOffset = renderer.getSaveCount() - 1;
669        const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
670        for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
671            DisplayListOp *op = mDisplayListData->displayListOps[i];
672
673#if DEBUG_DISPLAY_LIST
674            op->output(level + 1);
675#endif
676            logBuffer.writeCommand(level, op->name());
677            handler(op, saveCountOffset, properties().getClipToBounds());
678
679            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
680                issueOperationsOfProjectedChildren(renderer, handler);
681            }
682        }
683
684        // for 3d root, draw children with positive z values
685        issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
686    }
687
688    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
689    handler(new (alloc) RestoreToCountOp(restoreTo),
690            PROPERTY_SAVECOUNT, properties().getClipToBounds());
691    renderer.setOverrideLayerAlpha(1.0f);
692
693    DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName());
694    handler.endMark();
695}
696
697} /* namespace uirenderer */
698} /* namespace android */
699