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