RenderNode.cpp revision 80068b735eb4ef26f0d9dbcb0fbc5e4982c5c215
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 both reveal clip and outline clip simultaneously
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/**
428 * Apply property-based transformations to input matrix
429 *
430 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
431 * matrix computation instead of the Skia 3x3 matrix + camera hackery.
432 */
433void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const {
434    if (properties().getLeft() != 0 || properties().getTop() != 0) {
435        matrix.translate(properties().getLeft(), properties().getTop());
436    }
437    if (properties().getStaticMatrix()) {
438        mat4 stat(*properties().getStaticMatrix());
439        matrix.multiply(stat);
440    } else if (properties().getAnimationMatrix()) {
441        mat4 anim(*properties().getAnimationMatrix());
442        matrix.multiply(anim);
443    }
444
445    bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
446    if (properties().hasTransformMatrix() || applyTranslationZ) {
447        if (properties().isTransformTranslateOnly()) {
448            matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
449                    true3dTransform ? properties().getZ() : 0.0f);
450        } else {
451            if (!true3dTransform) {
452                matrix.multiply(*properties().getTransformMatrix());
453            } else {
454                mat4 true3dMat;
455                true3dMat.loadTranslate(
456                        properties().getPivotX() + properties().getTranslationX(),
457                        properties().getPivotY() + properties().getTranslationY(),
458                        properties().getZ());
459                true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
460                true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
461                true3dMat.rotate(properties().getRotation(), 0, 0, 1);
462                true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
463                true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
464
465                matrix.multiply(true3dMat);
466            }
467        }
468    }
469}
470
471/**
472 * Organizes the DisplayList hierarchy to prepare for background projection reordering.
473 *
474 * This should be called before a call to defer() or drawDisplayList()
475 *
476 * Each DisplayList that serves as a 3d root builds its list of composited children,
477 * which are flagged to not draw in the standard draw loop.
478 */
479void RenderNode::computeOrdering() {
480    ATRACE_CALL();
481    mProjectedNodes.clear();
482
483    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
484    // transform properties are applied correctly to top level children
485    if (mDisplayListData == NULL) return;
486    for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
487        DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
488        childOp->mRenderNode->computeOrderingImpl(childOp,
489                properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
490    }
491}
492
493void RenderNode::computeOrderingImpl(
494        DrawRenderNodeOp* opState,
495        const SkPath* outlineOfProjectionSurface,
496        Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
497        const mat4* transformFromProjectionSurface) {
498    mProjectedNodes.clear();
499    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
500
501    // TODO: should avoid this calculation in most cases
502    // TODO: just calculate single matrix, down to all leaf composited elements
503    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
504    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
505
506    if (properties().getProjectBackwards()) {
507        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
508        opState->mSkipInOrderDraw = true;
509        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
510        compositedChildrenOfProjectionSurface->add(opState);
511    } else {
512        // standard in order draw
513        opState->mSkipInOrderDraw = false;
514    }
515
516    if (mDisplayListData->children().size() > 0) {
517        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
518        bool haveAppliedPropertiesToProjection = false;
519        for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
520            DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
521            RenderNode* child = childOp->mRenderNode;
522
523            const SkPath* projectionOutline = NULL;
524            Vector<DrawRenderNodeOp*>* projectionChildren = NULL;
525            const mat4* projectionTransform = NULL;
526            if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
527                // if receiving projections, collect projecting descendent
528
529                // Note that if a direct descendent is projecting backwards, we pass it's
530                // grandparent projection collection, since it shouldn't project onto it's
531                // parent, where it will already be drawing.
532                projectionOutline = properties().getOutline().getPath();
533                projectionChildren = &mProjectedNodes;
534                projectionTransform = &mat4::identity();
535            } else {
536                if (!haveAppliedPropertiesToProjection) {
537                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
538                    haveAppliedPropertiesToProjection = true;
539                }
540                projectionOutline = outlineOfProjectionSurface;
541                projectionChildren = compositedChildrenOfProjectionSurface;
542                projectionTransform = &localTransformFromProjectionSurface;
543            }
544            child->computeOrderingImpl(childOp,
545                    projectionOutline, projectionChildren, projectionTransform);
546        }
547    }
548}
549
550class DeferOperationHandler {
551public:
552    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
553        : mDeferStruct(deferStruct), mLevel(level) {}
554    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
555        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
556    }
557    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
558    inline void startMark(const char* name) {} // do nothing
559    inline void endMark() {}
560    inline int level() { return mLevel; }
561    inline int replayFlags() { return mDeferStruct.mReplayFlags; }
562    inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); }
563
564private:
565    DeferStateStruct& mDeferStruct;
566    const int mLevel;
567};
568
569void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
570    DeferOperationHandler handler(deferStruct, level);
571    issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
572}
573
574class ReplayOperationHandler {
575public:
576    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
577        : mReplayStruct(replayStruct), mLevel(level) {}
578    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
579#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
580        mReplayStruct.mRenderer.eventMark(operation->name());
581#endif
582        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
583    }
584    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
585    inline void startMark(const char* name) {
586        mReplayStruct.mRenderer.startMark(name);
587    }
588    inline void endMark() {
589        mReplayStruct.mRenderer.endMark();
590    }
591    inline int level() { return mLevel; }
592    inline int replayFlags() { return mReplayStruct.mReplayFlags; }
593    inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); }
594
595private:
596    ReplayStateStruct& mReplayStruct;
597    const int mLevel;
598};
599
600void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
601    ReplayOperationHandler handler(replayStruct, level);
602    issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
603}
604
605void RenderNode::buildZSortedChildList(const DisplayListData::Chunk& chunk,
606        Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
607    if (chunk.beginChildIndex == chunk.endChildIndex) return;
608
609    for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
610        DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
611        RenderNode* child = childOp->mRenderNode;
612        float childZ = child->properties().getZ();
613
614        if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
615            zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
616            childOp->mSkipInOrderDraw = true;
617        } else if (!child->properties().getProjectBackwards()) {
618            // regular, in order drawing DisplayList
619            childOp->mSkipInOrderDraw = false;
620        }
621    }
622
623    // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
624    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
625}
626
627template <class T>
628void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
629    if (properties().getAlpha() <= 0.0f
630            || properties().getOutline().getAlpha() <= 0.0f
631            || !properties().getOutline().getPath()) {
632        // no shadow to draw
633        return;
634    }
635
636    mat4 shadowMatrixXY(transformFromParent);
637    applyViewPropertyTransforms(shadowMatrixXY);
638
639    // Z matrix needs actual 3d transformation, so mapped z values will be correct
640    mat4 shadowMatrixZ(transformFromParent);
641    applyViewPropertyTransforms(shadowMatrixZ, true);
642
643    const SkPath* casterOutlinePath = properties().getOutline().getPath();
644    const SkPath* revealClipPath = properties().getRevealClip().getPath();
645    if (revealClipPath && revealClipPath->isEmpty()) return;
646
647    float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha();
648
649    const SkPath* outlinePath = casterOutlinePath;
650    if (revealClipPath) {
651        // if we can't simply use the caster's path directly, create a temporary one
652        SkPath* frameAllocatedPath = handler.allocPathForFrame();
653
654        // intersect the outline with the convex reveal clip
655        Op(*casterOutlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath);
656        outlinePath = frameAllocatedPath;
657    }
658
659    DisplayListOp* shadowOp  = new (handler.allocator()) DrawShadowOp(
660            shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath);
661    handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
662}
663
664#define SHADOW_DELTA 0.1f
665
666template <class T>
667void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
668        const Matrix4& initialTransform, const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
669        OpenGLRenderer& renderer, T& handler) {
670    const int size = zTranslatedNodes.size();
671    if (size == 0
672            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
673            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
674        // no 3d children to draw
675        return;
676    }
677
678    // Apply the base transform of the parent of the 3d children. This isolates
679    // 3d children of the current chunk from transformations made in previous chunks.
680    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
681    renderer.setMatrix(initialTransform);
682
683    /**
684     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
685     * with very similar Z heights to draw together.
686     *
687     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
688     * underneath both, and neither's shadow is drawn on top of the other.
689     */
690    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
691    size_t drawIndex, shadowIndex, endIndex;
692    if (mode == kNegativeZChildren) {
693        drawIndex = 0;
694        endIndex = nonNegativeIndex;
695        shadowIndex = endIndex; // draw no shadows
696    } else {
697        drawIndex = nonNegativeIndex;
698        endIndex = size;
699        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
700    }
701
702    DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
703            endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
704
705    float lastCasterZ = 0.0f;
706    while (shadowIndex < endIndex || drawIndex < endIndex) {
707        if (shadowIndex < endIndex) {
708            DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
709            RenderNode* caster = casterOp->mRenderNode;
710            const float casterZ = zTranslatedNodes[shadowIndex].key;
711            // attempt to render the shadow if the caster about to be drawn is its caster,
712            // OR if its caster's Z value is similar to the previous potential caster
713            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
714                caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler);
715
716                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
717                shadowIndex++;
718                continue;
719            }
720        }
721
722        // only the actual child DL draw needs to be in save/restore,
723        // since it modifies the renderer's matrix
724        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
725
726        DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
727        RenderNode* child = childOp->mRenderNode;
728
729        renderer.concatMatrix(childOp->mTransformFromParent);
730        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
731        handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
732        childOp->mSkipInOrderDraw = true;
733
734        renderer.restoreToCount(restoreTo);
735        drawIndex++;
736    }
737    renderer.restoreToCount(rootRestoreTo);
738}
739
740template <class T>
741void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
742    DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
743    const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
744    int restoreTo = renderer.getSaveCount();
745
746    LinearAllocator& alloc = handler.allocator();
747    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
748            PROPERTY_SAVECOUNT, properties().getClipToBounds());
749
750    // Transform renderer to match background we're projecting onto
751    // (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
752    const DisplayListOp* op =
753            (mDisplayListData->displayListOps[mDisplayListData->projectionReceiveIndex]);
754    const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op);
755    const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties();
756    renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
757
758    // If the projection reciever has an outline, we mask each of the projected rendernodes to it
759    // Either with clipRect, or special saveLayer masking
760    if (projectionReceiverOutline != NULL) {
761        const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
762        if (projectionReceiverOutline->isRect(NULL)) {
763            // mask to the rect outline simply with clipRect
764            ClipRectOp* clipOp = new (alloc) ClipRectOp(
765                    outlineBounds.left(), outlineBounds.top(),
766                    outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
767            handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
768        } else {
769            // wrap the projected RenderNodes with a SaveLayer that will mask to the outline
770            SaveLayerOp* op = new (alloc) SaveLayerOp(
771                    outlineBounds.left(), outlineBounds.top(),
772                    outlineBounds.right(), outlineBounds.bottom(),
773                    255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag);
774            op->setMask(projectionReceiverOutline);
775            handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
776
777            /* TODO: add optimizations here to take advantage of placement/size of projected
778             * children (which may shrink saveLayer area significantly). This is dependent on
779             * passing actual drawing/dirtying bounds of projected content down to native.
780             */
781        }
782    }
783
784    // draw projected nodes
785    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
786        DrawRenderNodeOp* childOp = mProjectedNodes[i];
787
788        // matrix save, concat, and restore can be done safely without allocating operations
789        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
790        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
791        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
792        handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
793        childOp->mSkipInOrderDraw = true;
794        renderer.restoreToCount(restoreTo);
795    }
796
797    if (projectionReceiverOutline != NULL) {
798        handler(new (alloc) RestoreToCountOp(restoreTo),
799                PROPERTY_SAVECOUNT, properties().getClipToBounds());
800    }
801}
802
803/**
804 * This function serves both defer and replay modes, and will organize the displayList's component
805 * operations for a single frame:
806 *
807 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
808 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
809 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
810 * defer vs replay logic, per operation
811 */
812template <class T>
813void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
814    const int level = handler.level();
815    if (mDisplayListData->isEmpty()) {
816        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
817        return;
818    }
819
820    const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
821    // If we are updating the contents of mLayer, we don't want to apply any of
822    // the RenderNode's properties to this issueOperations pass. Those will all
823    // be applied when the layer is drawn, aka when this is true.
824    const bool useViewProperties = (!mLayer || drawLayer);
825    if (useViewProperties) {
826        const Outline& outline = properties().getOutline();
827        if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
828            DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", level * 2, "", this, getName());
829            return;
830        }
831    }
832
833    handler.startMark(getName());
834
835#if DEBUG_DISPLAY_LIST
836    const Rect& clipRect = renderer.getLocalClipBounds();
837    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
838            level * 2, "", this, getName(),
839            clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
840#endif
841
842    LinearAllocator& alloc = handler.allocator();
843    int restoreTo = renderer.getSaveCount();
844    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
845            PROPERTY_SAVECOUNT, properties().getClipToBounds());
846
847    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
848            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
849
850    if (useViewProperties) {
851        setViewProperties<T>(renderer, handler);
852    }
853
854    bool quickRejected = properties().getClipToBounds()
855            && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
856    if (!quickRejected) {
857        Matrix4 initialTransform(*(renderer.currentTransform()));
858
859        if (drawLayer) {
860            handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
861                    renderer.getSaveCount() - 1, properties().getClipToBounds());
862        } else {
863            DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
864            for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
865                const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
866
867                Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
868                buildZSortedChildList(chunk, zTranslatedNodes);
869
870                issueOperationsOf3dChildren(kNegativeZChildren,
871                        initialTransform, zTranslatedNodes, renderer, handler);
872
873                const int saveCountOffset = renderer.getSaveCount() - 1;
874                const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
875
876                for (int opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
877                    DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
878#if DEBUG_DISPLAY_LIST
879                    op->output(level + 1);
880#endif
881                    logBuffer.writeCommand(level, op->name());
882                    handler(op, saveCountOffset, properties().getClipToBounds());
883
884                    if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && opIndex == projectionReceiveIndex)) {
885                        issueOperationsOfProjectedChildren(renderer, handler);
886                    }
887                }
888
889                issueOperationsOf3dChildren(kPositiveZChildren,
890                        initialTransform, zTranslatedNodes, renderer, handler);
891            }
892        }
893    }
894
895    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
896    handler(new (alloc) RestoreToCountOp(restoreTo),
897            PROPERTY_SAVECOUNT, properties().getClipToBounds());
898    renderer.setOverrideLayerAlpha(1.0f);
899
900    DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName());
901    handler.endMark();
902}
903
904} /* namespace uirenderer */
905} /* namespace android */
906