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