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