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