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