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