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