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#include "RenderNode.h"
18
19#include "BakedOpRenderer.h"
20#include "DamageAccumulator.h"
21#include "Debug.h"
22#include "RecordedOp.h"
23#include "TreeInfo.h"
24#include "utils/FatVector.h"
25#include "utils/MathUtils.h"
26#include "utils/StringUtils.h"
27#include "utils/TraceUtils.h"
28#include "VectorDrawable.h"
29#include "renderstate/RenderState.h"
30#include "renderthread/CanvasContext.h"
31
32#include "protos/hwui.pb.h"
33#include "protos/ProtoHelpers.h"
34
35#include <algorithm>
36#include <sstream>
37#include <string>
38
39namespace android {
40namespace uirenderer {
41
42// Used for tree mutations that are purely destructive.
43// Generic tree mutations should use MarkAndSweepObserver instead
44class ImmediateRemoved : public TreeObserver {
45public:
46    explicit ImmediateRemoved(TreeInfo* info) : mTreeInfo(info) {}
47
48    void onMaybeRemovedFromTree(RenderNode* node) override {
49        node->onRemovedFromTree(mTreeInfo);
50    }
51
52private:
53    TreeInfo* mTreeInfo;
54};
55
56RenderNode::RenderNode()
57        : mDirtyPropertyFields(0)
58        , mNeedsDisplayListSync(false)
59        , mDisplayList(nullptr)
60        , mStagingDisplayList(nullptr)
61        , mAnimatorManager(*this)
62        , mParentCount(0) {
63}
64
65RenderNode::~RenderNode() {
66    ImmediateRemoved observer(nullptr);
67    deleteDisplayList(observer);
68    delete mStagingDisplayList;
69    LOG_ALWAYS_FATAL_IF(hasLayer(), "layer missed detachment!");
70}
71
72void RenderNode::setStagingDisplayList(DisplayList* displayList) {
73    mValid = (displayList != nullptr);
74    mNeedsDisplayListSync = true;
75    delete mStagingDisplayList;
76    mStagingDisplayList = displayList;
77}
78
79/**
80 * This function is a simplified version of replay(), where we simply retrieve and log the
81 * display list. This function should remain in sync with the replay() function.
82 */
83void RenderNode::output() {
84    LogcatStream strout;
85    strout << "Root";
86    output(strout, 0);
87}
88
89void RenderNode::output(std::ostream& output, uint32_t level) {
90    output << "  (" << getName() << " " << this
91            << (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : "")
92            << (properties().hasShadow() ? ", casting shadow" : "")
93            << (isRenderable() ? "" : ", empty")
94            << (properties().getProjectBackwards() ? ", projected" : "")
95            << (hasLayer() ? ", on HW Layer" : "")
96            << ")" << std::endl;
97
98    properties().debugOutputProperties(output, level + 1);
99
100    if (mDisplayList) {
101        mDisplayList->output(output, level);
102    }
103    output << std::string(level * 2, ' ') << "/RenderNode(" << getName() << " " << this << ")";
104    output << std::endl;
105}
106
107void RenderNode::copyTo(proto::RenderNode *pnode) {
108    pnode->set_id(static_cast<uint64_t>(
109            reinterpret_cast<uintptr_t>(this)));
110    pnode->set_name(mName.string(), mName.length());
111
112    proto::RenderProperties* pprops = pnode->mutable_properties();
113    pprops->set_left(properties().getLeft());
114    pprops->set_top(properties().getTop());
115    pprops->set_right(properties().getRight());
116    pprops->set_bottom(properties().getBottom());
117    pprops->set_clip_flags(properties().getClippingFlags());
118    pprops->set_alpha(properties().getAlpha());
119    pprops->set_translation_x(properties().getTranslationX());
120    pprops->set_translation_y(properties().getTranslationY());
121    pprops->set_translation_z(properties().getTranslationZ());
122    pprops->set_elevation(properties().getElevation());
123    pprops->set_rotation(properties().getRotation());
124    pprops->set_rotation_x(properties().getRotationX());
125    pprops->set_rotation_y(properties().getRotationY());
126    pprops->set_scale_x(properties().getScaleX());
127    pprops->set_scale_y(properties().getScaleY());
128    pprops->set_pivot_x(properties().getPivotX());
129    pprops->set_pivot_y(properties().getPivotY());
130    pprops->set_has_overlapping_rendering(properties().getHasOverlappingRendering());
131    pprops->set_pivot_explicitly_set(properties().isPivotExplicitlySet());
132    pprops->set_project_backwards(properties().getProjectBackwards());
133    pprops->set_projection_receiver(properties().isProjectionReceiver());
134    set(pprops->mutable_clip_bounds(), properties().getClipBounds());
135
136    const Outline& outline = properties().getOutline();
137    if (outline.getType() != Outline::Type::None) {
138        proto::Outline* poutline = pprops->mutable_outline();
139        poutline->clear_path();
140        if (outline.getType() == Outline::Type::Empty) {
141            poutline->set_type(proto::Outline_Type_Empty);
142        } else if (outline.getType() == Outline::Type::ConvexPath) {
143            poutline->set_type(proto::Outline_Type_ConvexPath);
144            if (const SkPath* path = outline.getPath()) {
145                set(poutline->mutable_path(), *path);
146            }
147        } else if (outline.getType() == Outline::Type::RoundRect) {
148            poutline->set_type(proto::Outline_Type_RoundRect);
149        } else {
150            ALOGW("Uknown outline type! %d", static_cast<int>(outline.getType()));
151            poutline->set_type(proto::Outline_Type_None);
152        }
153        poutline->set_should_clip(outline.getShouldClip());
154        poutline->set_alpha(outline.getAlpha());
155        poutline->set_radius(outline.getRadius());
156        set(poutline->mutable_bounds(), outline.getBounds());
157    } else {
158        pprops->clear_outline();
159    }
160
161    const RevealClip& revealClip = properties().getRevealClip();
162    if (revealClip.willClip()) {
163        proto::RevealClip* prevealClip = pprops->mutable_reveal_clip();
164        prevealClip->set_x(revealClip.getX());
165        prevealClip->set_y(revealClip.getY());
166        prevealClip->set_radius(revealClip.getRadius());
167    } else {
168        pprops->clear_reveal_clip();
169    }
170
171    pnode->clear_children();
172    if (mDisplayList) {
173        for (auto&& child : mDisplayList->getChildren()) {
174            child->renderNode->copyTo(pnode->add_children());
175        }
176    }
177}
178
179int RenderNode::getDebugSize() {
180    int size = sizeof(RenderNode);
181    if (mStagingDisplayList) {
182        size += mStagingDisplayList->getUsedSize();
183    }
184    if (mDisplayList && mDisplayList != mStagingDisplayList) {
185        size += mDisplayList->getUsedSize();
186    }
187    return size;
188}
189
190void RenderNode::prepareTree(TreeInfo& info) {
191    ATRACE_CALL();
192    LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
193    MarkAndSweepRemoved observer(&info);
194
195    // The OpenGL renderer reserves the stencil buffer for overdraw debugging.  Functors
196    // will need to be drawn in a layer.
197    bool functorsNeedLayer = Properties::debugOverdraw && !Properties::isSkiaEnabled();
198
199    prepareTreeImpl(observer, info, functorsNeedLayer);
200}
201
202void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
203    mAnimatorManager.addAnimator(animator);
204}
205
206void RenderNode::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
207    mAnimatorManager.removeAnimator(animator);
208}
209
210void RenderNode::damageSelf(TreeInfo& info) {
211    if (isRenderable()) {
212        if (properties().getClipDamageToBounds()) {
213            info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
214        } else {
215            // Hope this is big enough?
216            // TODO: Get this from the display list ops or something
217            info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
218        }
219    }
220}
221
222void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
223    LayerType layerType = properties().effectiveLayerType();
224    if (CC_UNLIKELY(layerType == LayerType::RenderLayer)) {
225        // Damage applied so far needs to affect our parent, but does not require
226        // the layer to be updated. So we pop/push here to clear out the current
227        // damage and get a clean state for display list or children updates to
228        // affect, which will require the layer to be updated
229        info.damageAccumulator->popTransform();
230        info.damageAccumulator->pushTransform(this);
231        if (dirtyMask & DISPLAY_LIST) {
232            damageSelf(info);
233        }
234    }
235}
236
237void RenderNode::pushLayerUpdate(TreeInfo& info) {
238    LayerType layerType = properties().effectiveLayerType();
239    // If we are not a layer OR we cannot be rendered (eg, view was detached)
240    // we need to destroy any Layers we may have had previously
241    if (CC_LIKELY(layerType != LayerType::RenderLayer)
242            || CC_UNLIKELY(!isRenderable())
243            || CC_UNLIKELY(properties().getWidth() == 0)
244            || CC_UNLIKELY(properties().getHeight() == 0)) {
245        if (CC_UNLIKELY(hasLayer())) {
246            renderthread::CanvasContext::destroyLayer(this);
247        }
248        return;
249    }
250
251    if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) {
252        damageSelf(info);
253    }
254
255    if (!hasLayer()) {
256        Caches::getInstance().dumpMemoryUsage();
257        if (info.errorHandler) {
258            std::ostringstream err;
259            err << "Unable to create layer for " << getName();
260            const int maxTextureSize = Caches::getInstance().maxTextureSize;
261            if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
262                err << ", size " << getWidth() << "x" << getHeight()
263                        << " exceeds max size " << maxTextureSize;
264            } else {
265                err << ", see logcat for more info";
266            }
267            info.errorHandler->onError(err.str());
268        }
269        return;
270    }
271
272    SkRect dirty;
273    info.damageAccumulator->peekAtDirty(&dirty);
274    info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
275
276    // There might be prefetched layers that need to be accounted for.
277    // That might be us, so tell CanvasContext that this layer is in the
278    // tree and should not be destroyed.
279    info.canvasContext.markLayerInUse(this);
280}
281
282/**
283 * Traverse down the the draw tree to prepare for a frame.
284 *
285 * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven
286 *
287 * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
288 * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
289 */
290void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
291    info.damageAccumulator->pushTransform(this);
292
293    if (info.mode == TreeInfo::MODE_FULL) {
294        pushStagingPropertiesChanges(info);
295    }
296    uint32_t animatorDirtyMask = 0;
297    if (CC_LIKELY(info.runAnimations)) {
298        animatorDirtyMask = mAnimatorManager.animate(info);
299    }
300
301    bool willHaveFunctor = false;
302    if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
303        willHaveFunctor = mStagingDisplayList->hasFunctor();
304    } else if (mDisplayList) {
305        willHaveFunctor = mDisplayList->hasFunctor();
306    }
307    bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
308            willHaveFunctor, functorsNeedLayer);
309
310    if (CC_UNLIKELY(mPositionListener.get())) {
311        mPositionListener->onPositionUpdated(*this, info);
312    }
313
314    prepareLayer(info, animatorDirtyMask);
315    if (info.mode == TreeInfo::MODE_FULL) {
316        pushStagingDisplayListChanges(observer, info);
317    }
318
319    if (mDisplayList) {
320        info.out.hasFunctors |= mDisplayList->hasFunctor();
321        bool isDirty = mDisplayList->prepareListAndChildren(observer, info, childFunctorsNeedLayer,
322                [](RenderNode* child, TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
323            child->prepareTreeImpl(observer, info, functorsNeedLayer);
324        });
325        if (isDirty) {
326            damageSelf(info);
327        }
328    }
329    pushLayerUpdate(info);
330
331    info.damageAccumulator->popTransform();
332}
333
334void RenderNode::syncProperties() {
335    mProperties = mStagingProperties;
336}
337
338void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
339    // Push the animators first so that setupStartValueIfNecessary() is called
340    // before properties() is trampled by stagingProperties(), as they are
341    // required by some animators.
342    if (CC_LIKELY(info.runAnimations)) {
343        mAnimatorManager.pushStaging();
344    }
345    if (mDirtyPropertyFields) {
346        mDirtyPropertyFields = 0;
347        damageSelf(info);
348        info.damageAccumulator->popTransform();
349        syncProperties();
350        // We could try to be clever and only re-damage if the matrix changed.
351        // However, we don't need to worry about that. The cost of over-damaging
352        // here is only going to be a single additional map rect of this node
353        // plus a rect join(). The parent's transform (and up) will only be
354        // performed once.
355        info.damageAccumulator->pushTransform(this);
356        damageSelf(info);
357    }
358}
359
360void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
361    // Make sure we inc first so that we don't fluctuate between 0 and 1,
362    // which would thrash the layer cache
363    if (mStagingDisplayList) {
364        mStagingDisplayList->updateChildren([](RenderNode* child) {
365            child->incParentRefCount();
366        });
367    }
368    deleteDisplayList(observer, info);
369    mDisplayList = mStagingDisplayList;
370    mStagingDisplayList = nullptr;
371    if (mDisplayList) {
372        mDisplayList->syncContents();
373    }
374}
375
376void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
377    if (mNeedsDisplayListSync) {
378        mNeedsDisplayListSync = false;
379        // Damage with the old display list first then the new one to catch any
380        // changes in isRenderable or, in the future, bounds
381        damageSelf(info);
382        syncDisplayList(observer, &info);
383        damageSelf(info);
384    }
385}
386
387void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) {
388    if (mDisplayList) {
389        mDisplayList->updateChildren([&observer, info](RenderNode* child) {
390            child->decParentRefCount(observer, info);
391        });
392        if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) {
393            delete mDisplayList;
394        }
395    }
396    mDisplayList = nullptr;
397}
398
399void RenderNode::destroyHardwareResources(TreeInfo* info) {
400    if (hasLayer()) {
401        renderthread::CanvasContext::destroyLayer(this);
402    }
403    setStagingDisplayList(nullptr);
404
405    ImmediateRemoved observer(info);
406    deleteDisplayList(observer, info);
407}
408
409void RenderNode::destroyLayers() {
410    if (hasLayer()) {
411        renderthread::CanvasContext::destroyLayer(this);
412    }
413    if (mDisplayList) {
414        mDisplayList->updateChildren([](RenderNode* child) {
415            child->destroyLayers();
416        });
417    }
418}
419
420void RenderNode::decParentRefCount(TreeObserver& observer, TreeInfo* info) {
421    LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
422    mParentCount--;
423    if (!mParentCount) {
424        observer.onMaybeRemovedFromTree(this);
425        if (CC_UNLIKELY(mPositionListener.get())) {
426            mPositionListener->onPositionLost(*this, info);
427        }
428    }
429}
430
431void RenderNode::onRemovedFromTree(TreeInfo* info) {
432    destroyHardwareResources(info);
433}
434
435void RenderNode::clearRoot() {
436    ImmediateRemoved observer(nullptr);
437    decParentRefCount(observer);
438}
439
440/**
441 * Apply property-based transformations to input matrix
442 *
443 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
444 * matrix computation instead of the Skia 3x3 matrix + camera hackery.
445 */
446void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const {
447    if (properties().getLeft() != 0 || properties().getTop() != 0) {
448        matrix.translate(properties().getLeft(), properties().getTop());
449    }
450    if (properties().getStaticMatrix()) {
451        mat4 stat(*properties().getStaticMatrix());
452        matrix.multiply(stat);
453    } else if (properties().getAnimationMatrix()) {
454        mat4 anim(*properties().getAnimationMatrix());
455        matrix.multiply(anim);
456    }
457
458    bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
459    if (properties().hasTransformMatrix() || applyTranslationZ) {
460        if (properties().isTransformTranslateOnly()) {
461            matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
462                    true3dTransform ? properties().getZ() : 0.0f);
463        } else {
464            if (!true3dTransform) {
465                matrix.multiply(*properties().getTransformMatrix());
466            } else {
467                mat4 true3dMat;
468                true3dMat.loadTranslate(
469                        properties().getPivotX() + properties().getTranslationX(),
470                        properties().getPivotY() + properties().getTranslationY(),
471                        properties().getZ());
472                true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
473                true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
474                true3dMat.rotate(properties().getRotation(), 0, 0, 1);
475                true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
476                true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
477
478                matrix.multiply(true3dMat);
479            }
480        }
481    }
482}
483
484/**
485 * Organizes the DisplayList hierarchy to prepare for background projection reordering.
486 *
487 * This should be called before a call to defer() or drawDisplayList()
488 *
489 * Each DisplayList that serves as a 3d root builds its list of composited children,
490 * which are flagged to not draw in the standard draw loop.
491 */
492void RenderNode::computeOrdering() {
493    ATRACE_CALL();
494    mProjectedNodes.clear();
495
496    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
497    // transform properties are applied correctly to top level children
498    if (mDisplayList == nullptr) return;
499    for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
500        RenderNodeOp* childOp = mDisplayList->getChildren()[i];
501        childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
502    }
503}
504
505void RenderNode::computeOrderingImpl(
506        RenderNodeOp* opState,
507        std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
508        const mat4* transformFromProjectionSurface) {
509    mProjectedNodes.clear();
510    if (mDisplayList == nullptr || mDisplayList->isEmpty()) return;
511
512    // TODO: should avoid this calculation in most cases
513    // TODO: just calculate single matrix, down to all leaf composited elements
514    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
515    localTransformFromProjectionSurface.multiply(opState->localMatrix);
516
517    if (properties().getProjectBackwards()) {
518        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
519        opState->skipInOrderDraw = true;
520        opState->transformFromCompositingAncestor = localTransformFromProjectionSurface;
521        compositedChildrenOfProjectionSurface->push_back(opState);
522    } else {
523        // standard in order draw
524        opState->skipInOrderDraw = false;
525    }
526
527    if (mDisplayList->getChildren().size() > 0) {
528        const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
529        bool haveAppliedPropertiesToProjection = false;
530        for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
531            RenderNodeOp* childOp = mDisplayList->getChildren()[i];
532            RenderNode* child = childOp->renderNode;
533
534            std::vector<RenderNodeOp*>* projectionChildren = nullptr;
535            const mat4* projectionTransform = nullptr;
536            if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
537                // if receiving projections, collect projecting descendant
538
539                // Note that if a direct descendant is projecting backwards, we pass its
540                // grandparent projection collection, since it shouldn't project onto its
541                // parent, where it will already be drawing.
542                projectionChildren = &mProjectedNodes;
543                projectionTransform = &mat4::identity();
544            } else {
545                if (!haveAppliedPropertiesToProjection) {
546                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
547                    haveAppliedPropertiesToProjection = true;
548                }
549                projectionChildren = compositedChildrenOfProjectionSurface;
550                projectionTransform = &localTransformFromProjectionSurface;
551            }
552            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
553        }
554    }
555}
556
557} /* namespace uirenderer */
558} /* namespace android */
559