Home | History | Annotate | Download | only in hwui
      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 "DamageAccumulator.h"
     20 #include "Debug.h"
     21 #include "TreeInfo.h"
     22 #include "VectorDrawable.h"
     23 #include "renderstate/RenderState.h"
     24 #include "renderthread/CanvasContext.h"
     25 #include "utils/FatVector.h"
     26 #include "utils/MathUtils.h"
     27 #include "utils/StringUtils.h"
     28 #include "utils/TraceUtils.h"
     29 
     30 #include <SkPathOps.h>
     31 #include <algorithm>
     32 #include <atomic>
     33 #include <sstream>
     34 #include <string>
     35 
     36 namespace android {
     37 namespace uirenderer {
     38 
     39 // Used for tree mutations that are purely destructive.
     40 // Generic tree mutations should use MarkAndSweepObserver instead
     41 class ImmediateRemoved : public TreeObserver {
     42 public:
     43     explicit ImmediateRemoved(TreeInfo* info) : mTreeInfo(info) {}
     44 
     45     void onMaybeRemovedFromTree(RenderNode* node) override { node->onRemovedFromTree(mTreeInfo); }
     46 
     47 private:
     48     TreeInfo* mTreeInfo;
     49 };
     50 
     51 static int64_t generateId() {
     52     static std::atomic<int64_t> sNextId{1};
     53     return sNextId++;
     54 }
     55 
     56 RenderNode::RenderNode()
     57         : mUniqueId(generateId())
     58         , mDirtyPropertyFields(0)
     59         , mNeedsDisplayListSync(false)
     60         , mDisplayList(nullptr)
     61         , mStagingDisplayList(nullptr)
     62         , mAnimatorManager(*this)
     63         , mParentCount(0) {}
     64 
     65 RenderNode::~RenderNode() {
     66     ImmediateRemoved observer(nullptr);
     67     deleteDisplayList(observer);
     68     delete mStagingDisplayList;
     69     LOG_ALWAYS_FATAL_IF(hasLayer(), "layer missed detachment!");
     70 }
     71 
     72 void 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  */
     83 void RenderNode::output() {
     84     LogcatStream strout;
     85     strout << "Root";
     86     output(strout, 0);
     87 }
     88 
     89 void 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" : "") << ")" << std::endl;
     96 
     97     properties().debugOutputProperties(output, level + 1);
     98 
     99     if (mDisplayList) {
    100         mDisplayList->output(output, level);
    101     }
    102     output << std::string(level * 2, ' ') << "/RenderNode(" << getName() << " " << this << ")";
    103     output << std::endl;
    104 }
    105 
    106 int RenderNode::getDebugSize() {
    107     int size = sizeof(RenderNode);
    108     if (mStagingDisplayList) {
    109         size += mStagingDisplayList->getUsedSize();
    110     }
    111     if (mDisplayList && mDisplayList != mStagingDisplayList) {
    112         size += mDisplayList->getUsedSize();
    113     }
    114     return size;
    115 }
    116 
    117 void RenderNode::prepareTree(TreeInfo& info) {
    118     ATRACE_CALL();
    119     LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
    120     MarkAndSweepRemoved observer(&info);
    121 
    122     const int before = info.disableForceDark;
    123     prepareTreeImpl(observer, info, false);
    124     LOG_ALWAYS_FATAL_IF(before != info.disableForceDark, "Mis-matched force dark");
    125 }
    126 
    127 void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
    128     mAnimatorManager.addAnimator(animator);
    129 }
    130 
    131 void RenderNode::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
    132     mAnimatorManager.removeAnimator(animator);
    133 }
    134 
    135 void RenderNode::damageSelf(TreeInfo& info) {
    136     if (isRenderable()) {
    137         mDamageGenerationId = info.damageGenerationId;
    138         if (properties().getClipDamageToBounds()) {
    139             info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
    140         } else {
    141             // Hope this is big enough?
    142             // TODO: Get this from the display list ops or something
    143             info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    144         }
    145     }
    146 }
    147 
    148 void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
    149     LayerType layerType = properties().effectiveLayerType();
    150     if (CC_UNLIKELY(layerType == LayerType::RenderLayer)) {
    151         // Damage applied so far needs to affect our parent, but does not require
    152         // the layer to be updated. So we pop/push here to clear out the current
    153         // damage and get a clean state for display list or children updates to
    154         // affect, which will require the layer to be updated
    155         info.damageAccumulator->popTransform();
    156         info.damageAccumulator->pushTransform(this);
    157         if (dirtyMask & DISPLAY_LIST) {
    158             damageSelf(info);
    159         }
    160     }
    161 }
    162 
    163 void RenderNode::pushLayerUpdate(TreeInfo& info) {
    164     LayerType layerType = properties().effectiveLayerType();
    165     // If we are not a layer OR we cannot be rendered (eg, view was detached)
    166     // we need to destroy any Layers we may have had previously
    167     if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable()) ||
    168         CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
    169         CC_UNLIKELY(!properties().fitsOnLayer())) {
    170         if (CC_UNLIKELY(hasLayer())) {
    171             this->setLayerSurface(nullptr);
    172         }
    173         return;
    174     }
    175 
    176     if (info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator, info.errorHandler)) {
    177         damageSelf(info);
    178     }
    179 
    180     if (!hasLayer()) {
    181         return;
    182     }
    183 
    184     SkRect dirty;
    185     info.damageAccumulator->peekAtDirty(&dirty);
    186     info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
    187 
    188     // There might be prefetched layers that need to be accounted for.
    189     // That might be us, so tell CanvasContext that this layer is in the
    190     // tree and should not be destroyed.
    191     info.canvasContext.markLayerInUse(this);
    192 }
    193 
    194 /**
    195  * Traverse down the the draw tree to prepare for a frame.
    196  *
    197  * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven
    198  *
    199  * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
    200  * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
    201  */
    202 void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
    203     if (mDamageGenerationId == info.damageGenerationId) {
    204         // We hit the same node a second time in the same tree. We don't know the minimal
    205         // damage rect anymore, so just push the biggest we can onto our parent's transform
    206         // We push directly onto parent in case we are clipped to bounds but have moved position.
    207         info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    208     }
    209     info.damageAccumulator->pushTransform(this);
    210 
    211     if (info.mode == TreeInfo::MODE_FULL) {
    212         pushStagingPropertiesChanges(info);
    213     }
    214 
    215     if (!mProperties.getAllowForceDark()) {
    216         info.disableForceDark++;
    217     }
    218 
    219     uint32_t animatorDirtyMask = 0;
    220     if (CC_LIKELY(info.runAnimations)) {
    221         animatorDirtyMask = mAnimatorManager.animate(info);
    222     }
    223 
    224     bool willHaveFunctor = false;
    225     if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
    226         willHaveFunctor = mStagingDisplayList->hasFunctor();
    227     } else if (mDisplayList) {
    228         willHaveFunctor = mDisplayList->hasFunctor();
    229     }
    230     bool childFunctorsNeedLayer =
    231             mProperties.prepareForFunctorPresence(willHaveFunctor, functorsNeedLayer);
    232 
    233     if (CC_UNLIKELY(mPositionListener.get())) {
    234         mPositionListener->onPositionUpdated(*this, info);
    235     }
    236 
    237     prepareLayer(info, animatorDirtyMask);
    238     if (info.mode == TreeInfo::MODE_FULL) {
    239         pushStagingDisplayListChanges(observer, info);
    240     }
    241 
    242     if (mDisplayList) {
    243         info.out.hasFunctors |= mDisplayList->hasFunctor();
    244         bool isDirty = mDisplayList->prepareListAndChildren(
    245                 observer, info, childFunctorsNeedLayer,
    246                 [](RenderNode* child, TreeObserver& observer, TreeInfo& info,
    247                    bool functorsNeedLayer) {
    248                     child->prepareTreeImpl(observer, info, functorsNeedLayer);
    249                 });
    250         if (isDirty) {
    251             damageSelf(info);
    252         }
    253     }
    254     pushLayerUpdate(info);
    255 
    256     if (!mProperties.getAllowForceDark()) {
    257         info.disableForceDark--;
    258     }
    259     info.damageAccumulator->popTransform();
    260 }
    261 
    262 void RenderNode::syncProperties() {
    263     mProperties = mStagingProperties;
    264 }
    265 
    266 void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
    267     if (mPositionListenerDirty) {
    268         mPositionListener = std::move(mStagingPositionListener);
    269         mStagingPositionListener = nullptr;
    270         mPositionListenerDirty = false;
    271     }
    272 
    273     // Push the animators first so that setupStartValueIfNecessary() is called
    274     // before properties() is trampled by stagingProperties(), as they are
    275     // required by some animators.
    276     if (CC_LIKELY(info.runAnimations)) {
    277         mAnimatorManager.pushStaging();
    278     }
    279     if (mDirtyPropertyFields) {
    280         mDirtyPropertyFields = 0;
    281         damageSelf(info);
    282         info.damageAccumulator->popTransform();
    283         syncProperties();
    284         // We could try to be clever and only re-damage if the matrix changed.
    285         // However, we don't need to worry about that. The cost of over-damaging
    286         // here is only going to be a single additional map rect of this node
    287         // plus a rect join(). The parent's transform (and up) will only be
    288         // performed once.
    289         info.damageAccumulator->pushTransform(this);
    290         damageSelf(info);
    291     }
    292 }
    293 
    294 void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
    295     // Make sure we inc first so that we don't fluctuate between 0 and 1,
    296     // which would thrash the layer cache
    297     if (mStagingDisplayList) {
    298         mStagingDisplayList->updateChildren([](RenderNode* child) { child->incParentRefCount(); });
    299     }
    300     deleteDisplayList(observer, info);
    301     mDisplayList = mStagingDisplayList;
    302     mStagingDisplayList = nullptr;
    303     if (mDisplayList) {
    304         WebViewSyncData syncData {
    305             .applyForceDark = info && !info->disableForceDark
    306         };
    307         mDisplayList->syncContents(syncData);
    308         handleForceDark(info);
    309     }
    310 }
    311 
    312 void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
    313     if (CC_LIKELY(!info || info->disableForceDark)) {
    314         return;
    315     }
    316     auto usage = usageHint();
    317     const auto& children = mDisplayList->mChildNodes;
    318     if (mDisplayList->hasText()) {
    319         usage = UsageHint::Foreground;
    320     }
    321     if (usage == UsageHint::Unknown) {
    322         if (children.size() > 1) {
    323             usage = UsageHint::Background;
    324         } else if (children.size() == 1 &&
    325                 children.front().getRenderNode()->usageHint() !=
    326                         UsageHint::Background) {
    327             usage = UsageHint::Background;
    328         }
    329     }
    330     if (children.size() > 1) {
    331         // Crude overlap check
    332         SkRect drawn = SkRect::MakeEmpty();
    333         for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
    334             const auto& child = iter->getRenderNode();
    335             // We use stagingProperties here because we haven't yet sync'd the children
    336             SkRect bounds = SkRect::MakeXYWH(child->stagingProperties().getX(), child->stagingProperties().getY(),
    337                     child->stagingProperties().getWidth(), child->stagingProperties().getHeight());
    338             if (bounds.contains(drawn)) {
    339                 // This contains everything drawn after it, so make it a background
    340                 child->setUsageHint(UsageHint::Background);
    341             }
    342             drawn.join(bounds);
    343         }
    344     }
    345     mDisplayList->mDisplayList.applyColorTransform(
    346             usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
    347 }
    348 
    349 void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
    350     if (mNeedsDisplayListSync) {
    351         mNeedsDisplayListSync = false;
    352         // Damage with the old display list first then the new one to catch any
    353         // changes in isRenderable or, in the future, bounds
    354         damageSelf(info);
    355         syncDisplayList(observer, &info);
    356         damageSelf(info);
    357     }
    358 }
    359 
    360 void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) {
    361     if (mDisplayList) {
    362         mDisplayList->updateChildren(
    363                 [&observer, info](RenderNode* child) { child->decParentRefCount(observer, info); });
    364         if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) {
    365             delete mDisplayList;
    366         }
    367     }
    368     mDisplayList = nullptr;
    369 }
    370 
    371 void RenderNode::destroyHardwareResources(TreeInfo* info) {
    372     if (hasLayer()) {
    373         this->setLayerSurface(nullptr);
    374     }
    375     setStagingDisplayList(nullptr);
    376 
    377     ImmediateRemoved observer(info);
    378     deleteDisplayList(observer, info);
    379 }
    380 
    381 void RenderNode::destroyLayers() {
    382     if (hasLayer()) {
    383         this->setLayerSurface(nullptr);
    384     }
    385     if (mDisplayList) {
    386         mDisplayList->updateChildren([](RenderNode* child) { child->destroyLayers(); });
    387     }
    388 }
    389 
    390 void RenderNode::decParentRefCount(TreeObserver& observer, TreeInfo* info) {
    391     LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
    392     mParentCount--;
    393     if (!mParentCount) {
    394         observer.onMaybeRemovedFromTree(this);
    395         if (CC_UNLIKELY(mPositionListener.get())) {
    396             mPositionListener->onPositionLost(*this, info);
    397         }
    398     }
    399 }
    400 
    401 void RenderNode::onRemovedFromTree(TreeInfo* info) {
    402     destroyHardwareResources(info);
    403 }
    404 
    405 void RenderNode::clearRoot() {
    406     ImmediateRemoved observer(nullptr);
    407     decParentRefCount(observer);
    408 }
    409 
    410 /**
    411  * Apply property-based transformations to input matrix
    412  *
    413  * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
    414  * matrix computation instead of the Skia 3x3 matrix + camera hackery.
    415  */
    416 void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const {
    417     if (properties().getLeft() != 0 || properties().getTop() != 0) {
    418         matrix.translate(properties().getLeft(), properties().getTop());
    419     }
    420     if (properties().getStaticMatrix()) {
    421         mat4 stat(*properties().getStaticMatrix());
    422         matrix.multiply(stat);
    423     } else if (properties().getAnimationMatrix()) {
    424         mat4 anim(*properties().getAnimationMatrix());
    425         matrix.multiply(anim);
    426     }
    427 
    428     bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
    429     if (properties().hasTransformMatrix() || applyTranslationZ) {
    430         if (properties().isTransformTranslateOnly()) {
    431             matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
    432                              true3dTransform ? properties().getZ() : 0.0f);
    433         } else {
    434             if (!true3dTransform) {
    435                 matrix.multiply(*properties().getTransformMatrix());
    436             } else {
    437                 mat4 true3dMat;
    438                 true3dMat.loadTranslate(properties().getPivotX() + properties().getTranslationX(),
    439                                         properties().getPivotY() + properties().getTranslationY(),
    440                                         properties().getZ());
    441                 true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
    442                 true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
    443                 true3dMat.rotate(properties().getRotation(), 0, 0, 1);
    444                 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
    445                 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
    446 
    447                 matrix.multiply(true3dMat);
    448             }
    449         }
    450     }
    451 }
    452 
    453 const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const {
    454     const SkPath* outlinePath = properties().getOutline().getPath();
    455     const uint32_t outlineID = outlinePath->getGenerationID();
    456 
    457     if (outlineID != mClippedOutlineCache.outlineID || clipRect != mClippedOutlineCache.clipRect) {
    458         // update the cache keys
    459         mClippedOutlineCache.outlineID = outlineID;
    460         mClippedOutlineCache.clipRect = clipRect;
    461 
    462         // update the cache value by recomputing a new path
    463         SkPath clipPath;
    464         clipPath.addRect(clipRect);
    465         Op(*outlinePath, clipPath, kIntersect_SkPathOp, &mClippedOutlineCache.clippedOutline);
    466     }
    467     return &mClippedOutlineCache.clippedOutline;
    468 }
    469 
    470 using StringBuffer = FatVector<char, 128>;
    471 
    472 template <typename... T>
    473 // TODO:__printflike(2, 3)
    474 // Doesn't work because the warning doesn't understand string_view and doesn't like that
    475 // it's not a C-style variadic function.
    476 static void format(StringBuffer& buffer, const std::string_view& format, T... args) {
    477     buffer.resize(buffer.capacity());
    478     while (1) {
    479         int needed = snprintf(buffer.data(), buffer.size(),
    480                 format.data(), std::forward<T>(args)...);
    481         if (needed < 0) {
    482             buffer[0] = '\0';
    483             buffer.resize(1);
    484             return;
    485         }
    486         if (needed < buffer.size()) {
    487             buffer.resize(needed + 1);
    488             return;
    489         }
    490         // If we're doing a heap alloc anyway might as well give it some slop
    491         buffer.resize(needed + 100);
    492     }
    493 }
    494 
    495 void RenderNode::markDrawStart(SkCanvas& canvas) {
    496     StringBuffer buffer;
    497     format(buffer, "RenderNode(id=%" PRId64 ", name='%s')", uniqueId(), getName());
    498     canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
    499 }
    500 
    501 void RenderNode::markDrawEnd(SkCanvas& canvas) {
    502     StringBuffer buffer;
    503     format(buffer, "/RenderNode(id=%" PRId64 ", name='%s')", uniqueId(), getName());
    504     canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
    505 }
    506 
    507 } /* namespace uirenderer */
    508 } /* namespace android */
    509