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