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