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