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 #if HWUI_NEW_OPS
     22 #include "BakedOpRenderer.h"
     23 #include "RecordedOp.h"
     24 #include "OpDumper.h"
     25 #endif
     26 #include "DisplayListOp.h"
     27 #include "LayerRenderer.h"
     28 #include "OpenGLRenderer.h"
     29 #include "TreeInfo.h"
     30 #include "utils/MathUtils.h"
     31 #include "utils/TraceUtils.h"
     32 #include "renderthread/CanvasContext.h"
     33 
     34 #include "protos/hwui.pb.h"
     35 #include "protos/ProtoHelpers.h"
     36 
     37 #include <algorithm>
     38 #include <sstream>
     39 #include <string>
     40 
     41 namespace android {
     42 namespace uirenderer {
     43 
     44 void RenderNode::debugDumpLayers(const char* prefix) {
     45 #if HWUI_NEW_OPS
     46     LOG_ALWAYS_FATAL("TODO: dump layer");
     47 #else
     48     if (mLayer) {
     49         ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
     50                 prefix, this, getName(), mLayer, mLayer->getFbo(),
     51                 mLayer->wasBuildLayered ? "true" : "false");
     52     }
     53 #endif
     54     if (mDisplayList) {
     55         for (auto&& child : mDisplayList->getChildren()) {
     56             child->renderNode->debugDumpLayers(prefix);
     57         }
     58     }
     59 }
     60 
     61 RenderNode::RenderNode()
     62         : mDirtyPropertyFields(0)
     63         , mNeedsDisplayListSync(false)
     64         , mDisplayList(nullptr)
     65         , mStagingDisplayList(nullptr)
     66         , mAnimatorManager(*this)
     67         , mParentCount(0) {
     68 }
     69 
     70 RenderNode::~RenderNode() {
     71     deleteDisplayList(nullptr);
     72     delete mStagingDisplayList;
     73 #if HWUI_NEW_OPS
     74     LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
     75 #else
     76     if (mLayer) {
     77         ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
     78         mLayer->postDecStrong();
     79         mLayer = nullptr;
     80     }
     81 #endif
     82 }
     83 
     84 void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* observer) {
     85     mNeedsDisplayListSync = true;
     86     delete mStagingDisplayList;
     87     mStagingDisplayList = displayList;
     88     // If mParentCount == 0 we are the sole reference to this RenderNode,
     89     // so immediately free the old display list
     90     if (!mParentCount && !mStagingDisplayList) {
     91         deleteDisplayList(observer);
     92     }
     93 }
     94 
     95 /**
     96  * This function is a simplified version of replay(), where we simply retrieve and log the
     97  * display list. This function should remain in sync with the replay() function.
     98  */
     99 #if HWUI_NEW_OPS
    100 void RenderNode::output(uint32_t level, const char* label) {
    101     ALOGD("%s (%s %p%s%s%s%s%s)",
    102             label,
    103             getName(),
    104             this,
    105             (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""),
    106             (properties().hasShadow() ? ", casting shadow" : ""),
    107             (isRenderable() ? "" : ", empty"),
    108             (properties().getProjectBackwards() ? ", projected" : ""),
    109             (mLayer != nullptr ? ", on HW Layer" : ""));
    110     properties().debugOutputProperties(level + 1);
    111 
    112     if (mDisplayList) {
    113         for (auto&& op : mDisplayList->getOps()) {
    114             std::stringstream strout;
    115             OpDumper::dump(*op, strout, level + 1);
    116             if (op->opId == RecordedOpId::RenderNodeOp) {
    117                 auto rnOp = reinterpret_cast<const RenderNodeOp*>(op);
    118                 rnOp->renderNode->output(level + 1, strout.str().c_str());
    119             } else {
    120                 ALOGD("%s", strout.str().c_str());
    121             }
    122         }
    123     }
    124     ALOGD("%*s/RenderNode(%s %p)", level * 2, "", getName(), this);
    125 }
    126 #else
    127 void RenderNode::output(uint32_t level) {
    128     ALOGD("%*sStart display list (%p, %s%s%s%s%s%s)", (level - 1) * 2, "", this,
    129             getName(),
    130             (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""),
    131             (properties().hasShadow() ? ", casting shadow" : ""),
    132             (isRenderable() ? "" : ", empty"),
    133             (properties().getProjectBackwards() ? ", projected" : ""),
    134             (mLayer != nullptr ? ", on HW Layer" : ""));
    135     ALOGD("%*s%s %d", level * 2, "", "Save", SaveFlags::MatrixClip);
    136     properties().debugOutputProperties(level);
    137     if (mDisplayList) {
    138         // TODO: consider printing the chunk boundaries here
    139         for (auto&& op : mDisplayList->getOps()) {
    140             op->output(level, DisplayListOp::kOpLogFlag_Recurse);
    141         }
    142     }
    143     ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
    144     }
    145 #endif
    146 
    147 void RenderNode::copyTo(proto::RenderNode *pnode) {
    148     pnode->set_id(static_cast<uint64_t>(
    149             reinterpret_cast<uintptr_t>(this)));
    150     pnode->set_name(mName.string(), mName.length());
    151 
    152     proto::RenderProperties* pprops = pnode->mutable_properties();
    153     pprops->set_left(properties().getLeft());
    154     pprops->set_top(properties().getTop());
    155     pprops->set_right(properties().getRight());
    156     pprops->set_bottom(properties().getBottom());
    157     pprops->set_clip_flags(properties().getClippingFlags());
    158     pprops->set_alpha(properties().getAlpha());
    159     pprops->set_translation_x(properties().getTranslationX());
    160     pprops->set_translation_y(properties().getTranslationY());
    161     pprops->set_translation_z(properties().getTranslationZ());
    162     pprops->set_elevation(properties().getElevation());
    163     pprops->set_rotation(properties().getRotation());
    164     pprops->set_rotation_x(properties().getRotationX());
    165     pprops->set_rotation_y(properties().getRotationY());
    166     pprops->set_scale_x(properties().getScaleX());
    167     pprops->set_scale_y(properties().getScaleY());
    168     pprops->set_pivot_x(properties().getPivotX());
    169     pprops->set_pivot_y(properties().getPivotY());
    170     pprops->set_has_overlapping_rendering(properties().getHasOverlappingRendering());
    171     pprops->set_pivot_explicitly_set(properties().isPivotExplicitlySet());
    172     pprops->set_project_backwards(properties().getProjectBackwards());
    173     pprops->set_projection_receiver(properties().isProjectionReceiver());
    174     set(pprops->mutable_clip_bounds(), properties().getClipBounds());
    175 
    176     const Outline& outline = properties().getOutline();
    177     if (outline.getType() != Outline::Type::None) {
    178         proto::Outline* poutline = pprops->mutable_outline();
    179         poutline->clear_path();
    180         if (outline.getType() == Outline::Type::Empty) {
    181             poutline->set_type(proto::Outline_Type_Empty);
    182         } else if (outline.getType() == Outline::Type::ConvexPath) {
    183             poutline->set_type(proto::Outline_Type_ConvexPath);
    184             if (const SkPath* path = outline.getPath()) {
    185                 set(poutline->mutable_path(), *path);
    186             }
    187         } else if (outline.getType() == Outline::Type::RoundRect) {
    188             poutline->set_type(proto::Outline_Type_RoundRect);
    189         } else {
    190             ALOGW("Uknown outline type! %d", static_cast<int>(outline.getType()));
    191             poutline->set_type(proto::Outline_Type_None);
    192         }
    193         poutline->set_should_clip(outline.getShouldClip());
    194         poutline->set_alpha(outline.getAlpha());
    195         poutline->set_radius(outline.getRadius());
    196         set(poutline->mutable_bounds(), outline.getBounds());
    197     } else {
    198         pprops->clear_outline();
    199     }
    200 
    201     const RevealClip& revealClip = properties().getRevealClip();
    202     if (revealClip.willClip()) {
    203         proto::RevealClip* prevealClip = pprops->mutable_reveal_clip();
    204         prevealClip->set_x(revealClip.getX());
    205         prevealClip->set_y(revealClip.getY());
    206         prevealClip->set_radius(revealClip.getRadius());
    207     } else {
    208         pprops->clear_reveal_clip();
    209     }
    210 
    211     pnode->clear_children();
    212     if (mDisplayList) {
    213         for (auto&& child : mDisplayList->getChildren()) {
    214             child->renderNode->copyTo(pnode->add_children());
    215         }
    216     }
    217 }
    218 
    219 int RenderNode::getDebugSize() {
    220     int size = sizeof(RenderNode);
    221     if (mStagingDisplayList) {
    222         size += mStagingDisplayList->getUsedSize();
    223     }
    224     if (mDisplayList && mDisplayList != mStagingDisplayList) {
    225         size += mDisplayList->getUsedSize();
    226     }
    227     return size;
    228 }
    229 
    230 void RenderNode::prepareTree(TreeInfo& info) {
    231     ATRACE_CALL();
    232     LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
    233 
    234     // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer.
    235     bool functorsNeedLayer = Properties::debugOverdraw;
    236 
    237     prepareTreeImpl(info, functorsNeedLayer);
    238 }
    239 
    240 void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
    241     mAnimatorManager.addAnimator(animator);
    242 }
    243 
    244 void RenderNode::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
    245     mAnimatorManager.removeAnimator(animator);
    246 }
    247 
    248 void RenderNode::damageSelf(TreeInfo& info) {
    249     if (isRenderable()) {
    250         if (properties().getClipDamageToBounds()) {
    251             info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
    252         } else {
    253             // Hope this is big enough?
    254             // TODO: Get this from the display list ops or something
    255             info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
    256         }
    257     }
    258 }
    259 
    260 void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
    261     LayerType layerType = properties().effectiveLayerType();
    262     if (CC_UNLIKELY(layerType == LayerType::RenderLayer)) {
    263         // Damage applied so far needs to affect our parent, but does not require
    264         // the layer to be updated. So we pop/push here to clear out the current
    265         // damage and get a clean state for display list or children updates to
    266         // affect, which will require the layer to be updated
    267         info.damageAccumulator->popTransform();
    268         info.damageAccumulator->pushTransform(this);
    269         if (dirtyMask & DISPLAY_LIST) {
    270             damageSelf(info);
    271         }
    272     }
    273 }
    274 
    275 static layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
    276 #if HWUI_NEW_OPS
    277     return renderState.layerPool().get(renderState, width, height);
    278 #else
    279     return LayerRenderer::createRenderLayer(renderState, width, height);
    280 #endif
    281 }
    282 
    283 static void destroyLayer(layer_t* layer) {
    284 #if HWUI_NEW_OPS
    285     RenderState& renderState = layer->renderState;
    286     renderState.layerPool().putOrDelete(layer);
    287 #else
    288     LayerRenderer::destroyLayer(layer);
    289 #endif
    290 }
    291 
    292 static bool layerMatchesWidthAndHeight(layer_t* layer, int width, int height) {
    293 #if HWUI_NEW_OPS
    294     return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height;
    295 #else
    296     return layer->layer.getWidth() == width && layer->layer.getHeight() == height;
    297 #endif
    298 }
    299 
    300 void RenderNode::pushLayerUpdate(TreeInfo& info) {
    301     LayerType layerType = properties().effectiveLayerType();
    302     // If we are not a layer OR we cannot be rendered (eg, view was detached)
    303     // we need to destroy any Layers we may have had previously
    304     if (CC_LIKELY(layerType != LayerType::RenderLayer)
    305             || CC_UNLIKELY(!isRenderable())
    306             || CC_UNLIKELY(properties().getWidth() == 0)
    307             || CC_UNLIKELY(properties().getHeight() == 0)) {
    308         if (CC_UNLIKELY(mLayer)) {
    309             destroyLayer(mLayer);
    310             mLayer = nullptr;
    311         }
    312         return;
    313     }
    314 
    315     bool transformUpdateNeeded = false;
    316     if (!mLayer) {
    317         mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
    318 #if !HWUI_NEW_OPS
    319         applyLayerPropertiesToLayer(info);
    320 #endif
    321         damageSelf(info);
    322         transformUpdateNeeded = true;
    323     } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
    324 #if HWUI_NEW_OPS
    325         // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
    326         // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
    327         RenderState& renderState = mLayer->renderState;
    328         if (properties().fitsOnLayer()) {
    329             mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
    330         } else {
    331 #else
    332         if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
    333 #endif
    334             destroyLayer(mLayer);
    335             mLayer = nullptr;
    336         }
    337         damageSelf(info);
    338         transformUpdateNeeded = true;
    339     }
    340 
    341     SkRect dirty;
    342     info.damageAccumulator->peekAtDirty(&dirty);
    343 
    344     if (!mLayer) {
    345         Caches::getInstance().dumpMemoryUsage();
    346         if (info.errorHandler) {
    347             std::ostringstream err;
    348             err << "Unable to create layer for " << getName();
    349             const int maxTextureSize = Caches::getInstance().maxTextureSize;
    350             if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
    351                 err << ", size " << getWidth() << "x" << getHeight()
    352                         << " exceeds max size " << maxTextureSize;
    353             } else {
    354                 err << ", see logcat for more info";
    355             }
    356             info.errorHandler->onError(err.str());
    357         }
    358         return;
    359     }
    360 
    361     if (transformUpdateNeeded && mLayer) {
    362         // update the transform in window of the layer to reset its origin wrt light source position
    363         Matrix4 windowTransform;
    364         info.damageAccumulator->computeCurrentTransform(&windowTransform);
    365         mLayer->setWindowTransform(windowTransform);
    366     }
    367 
    368 #if HWUI_NEW_OPS
    369     info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
    370 #else
    371     if (dirty.intersect(0, 0, getWidth(), getHeight())) {
    372         dirty.roundOut(&dirty);
    373         mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
    374     }
    375     // This is not inside the above if because we may have called
    376     // updateDeferred on a previous prepare pass that didn't have a renderer
    377     if (info.renderer && mLayer->deferredUpdateScheduled) {
    378         info.renderer->pushLayerUpdate(mLayer);
    379     }
    380 #endif
    381 
    382     // There might be prefetched layers that need to be accounted for.
    383     // That might be us, so tell CanvasContext that this layer is in the
    384     // tree and should not be destroyed.
    385     info.canvasContext.markLayerInUse(this);
    386 }
    387 
    388 /**
    389  * Traverse down the the draw tree to prepare for a frame.
    390  *
    391  * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven
    392  *
    393  * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
    394  * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
    395  */
    396 void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
    397     info.damageAccumulator->pushTransform(this);
    398 
    399     if (info.mode == TreeInfo::MODE_FULL) {
    400         pushStagingPropertiesChanges(info);
    401     }
    402     uint32_t animatorDirtyMask = 0;
    403     if (CC_LIKELY(info.runAnimations)) {
    404         animatorDirtyMask = mAnimatorManager.animate(info);
    405     }
    406 
    407     bool willHaveFunctor = false;
    408     if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
    409         willHaveFunctor = !mStagingDisplayList->getFunctors().empty();
    410     } else if (mDisplayList) {
    411         willHaveFunctor = !mDisplayList->getFunctors().empty();
    412     }
    413     bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
    414             willHaveFunctor, functorsNeedLayer);
    415 
    416     if (CC_UNLIKELY(mPositionListener.get())) {
    417         mPositionListener->onPositionUpdated(*this, info);
    418     }
    419 
    420     prepareLayer(info, animatorDirtyMask);
    421     if (info.mode == TreeInfo::MODE_FULL) {
    422         pushStagingDisplayListChanges(info);
    423     }
    424     prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);
    425 
    426     if (mDisplayList) {
    427         for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
    428             // If any vector drawable in the display list needs update, damage the node.
    429             if (vectorDrawable->isDirty()) {
    430                 damageSelf(info);
    431             }
    432             vectorDrawable->setPropertyChangeWillBeConsumed(true);
    433         }
    434     }
    435     pushLayerUpdate(info);
    436 
    437     info.damageAccumulator->popTransform();
    438 }
    439 
    440 void RenderNode::syncProperties() {
    441     mProperties = mStagingProperties;
    442 }
    443 
    444 void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
    445     // Push the animators first so that setupStartValueIfNecessary() is called
    446     // before properties() is trampled by stagingProperties(), as they are
    447     // required by some animators.
    448     if (CC_LIKELY(info.runAnimations)) {
    449         mAnimatorManager.pushStaging();
    450     }
    451     if (mDirtyPropertyFields) {
    452         mDirtyPropertyFields = 0;
    453         damageSelf(info);
    454         info.damageAccumulator->popTransform();
    455         syncProperties();
    456 #if !HWUI_NEW_OPS
    457         applyLayerPropertiesToLayer(info);
    458 #endif
    459         // We could try to be clever and only re-damage if the matrix changed.
    460         // However, we don't need to worry about that. The cost of over-damaging
    461         // here is only going to be a single additional map rect of this node
    462         // plus a rect join(). The parent's transform (and up) will only be
    463         // performed once.
    464         info.damageAccumulator->pushTransform(this);
    465         damageSelf(info);
    466     }
    467 }
    468 
    469 #if !HWUI_NEW_OPS
    470 void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
    471     if (CC_LIKELY(!mLayer)) return;
    472 
    473     const LayerProperties& props = properties().layerProperties();
    474     mLayer->setAlpha(props.alpha(), props.xferMode());
    475     mLayer->setColorFilter(props.colorFilter());
    476     mLayer->setBlend(props.needsBlending());
    477 }
    478 #endif
    479 
    480 void RenderNode::syncDisplayList(TreeInfo* info) {
    481     // Make sure we inc first so that we don't fluctuate between 0 and 1,
    482     // which would thrash the layer cache
    483     if (mStagingDisplayList) {
    484         for (auto&& child : mStagingDisplayList->getChildren()) {
    485             child->renderNode->incParentRefCount();
    486         }
    487     }
    488     deleteDisplayList(info ? info->observer : nullptr, info);
    489     mDisplayList = mStagingDisplayList;
    490     mStagingDisplayList = nullptr;
    491     if (mDisplayList) {
    492         for (auto& iter : mDisplayList->getFunctors()) {
    493             (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
    494         }
    495         for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
    496             vectorDrawable->syncProperties();
    497         }
    498     }
    499 }
    500 
    501 void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
    502     if (mNeedsDisplayListSync) {
    503         mNeedsDisplayListSync = false;
    504         // Damage with the old display list first then the new one to catch any
    505         // changes in isRenderable or, in the future, bounds
    506         damageSelf(info);
    507         syncDisplayList(&info);
    508         damageSelf(info);
    509     }
    510 }
    511 
    512 void RenderNode::deleteDisplayList(TreeObserver* observer, TreeInfo* info) {
    513     if (mDisplayList) {
    514         for (auto&& child : mDisplayList->getChildren()) {
    515             child->renderNode->decParentRefCount(observer, info);
    516         }
    517     }
    518     delete mDisplayList;
    519     mDisplayList = nullptr;
    520 }
    521 
    522 void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) {
    523     if (subtree) {
    524         TextureCache& cache = Caches::getInstance().textureCache;
    525         info.out.hasFunctors |= subtree->getFunctors().size();
    526         for (auto&& bitmapResource : subtree->getBitmapResources()) {
    527             void* ownerToken = &info.canvasContext;
    528             info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
    529         }
    530         for (auto&& op : subtree->getChildren()) {
    531             RenderNode* childNode = op->renderNode;
    532 #if HWUI_NEW_OPS
    533             info.damageAccumulator->pushTransform(&op->localMatrix);
    534             bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
    535 #else
    536             info.damageAccumulator->pushTransform(&op->localMatrix);
    537             bool childFunctorsNeedLayer = functorsNeedLayer
    538                     // Recorded with non-rect clip, or canvas-rotated by parent
    539                     || op->mRecordedWithPotentialStencilClip;
    540 #endif
    541             childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
    542             info.damageAccumulator->popTransform();
    543         }
    544     }
    545 }
    546 
    547 void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
    548     if (mLayer) {
    549         destroyLayer(mLayer);
    550         mLayer = nullptr;
    551     }
    552     if (mDisplayList) {
    553         for (auto&& child : mDisplayList->getChildren()) {
    554             child->renderNode->destroyHardwareResources(observer, info);
    555         }
    556         if (mNeedsDisplayListSync) {
    557             // Next prepare tree we are going to push a new display list, so we can
    558             // drop our current one now
    559             deleteDisplayList(observer, info);
    560         }
    561     }
    562 }
    563 
    564 void RenderNode::decParentRefCount(TreeObserver* observer, TreeInfo* info) {
    565     LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
    566     mParentCount--;
    567     if (!mParentCount) {
    568         if (observer) {
    569             observer->onMaybeRemovedFromTree(this);
    570         }
    571         if (CC_UNLIKELY(mPositionListener.get())) {
    572             mPositionListener->onPositionLost(*this, info);
    573         }
    574         // If a child of ours is being attached to our parent then this will incorrectly
    575         // destroy its hardware resources. However, this situation is highly unlikely
    576         // and the failure is "just" that the layer is re-created, so this should
    577         // be safe enough
    578         destroyHardwareResources(observer, info);
    579     }
    580 }
    581 
    582 /*
    583  * For property operations, we pass a savecount of 0, since the operations aren't part of the
    584  * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
    585  * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount())
    586  */
    587 #define PROPERTY_SAVECOUNT 0
    588 
    589 template <class T>
    590 void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
    591 #if DEBUG_DISPLAY_LIST
    592     properties().debugOutputProperties(handler.level() + 1);
    593 #endif
    594     if (properties().getLeft() != 0 || properties().getTop() != 0) {
    595         renderer.translate(properties().getLeft(), properties().getTop());
    596     }
    597     if (properties().getStaticMatrix()) {
    598         renderer.concatMatrix(*properties().getStaticMatrix());
    599     } else if (properties().getAnimationMatrix()) {
    600         renderer.concatMatrix(*properties().getAnimationMatrix());
    601     }
    602     if (properties().hasTransformMatrix()) {
    603         if (properties().isTransformTranslateOnly()) {
    604             renderer.translate(properties().getTranslationX(), properties().getTranslationY());
    605         } else {
    606             renderer.concatMatrix(*properties().getTransformMatrix());
    607         }
    608     }
    609     const bool isLayer = properties().effectiveLayerType() != LayerType::None;
    610     int clipFlags = properties().getClippingFlags();
    611     if (properties().getAlpha() < 1) {
    612         if (isLayer) {
    613             clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
    614         }
    615         if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) {
    616             // simply scale rendering content's alpha
    617             renderer.scaleAlpha(properties().getAlpha());
    618         } else {
    619             // savelayer needed to create an offscreen buffer
    620             Rect layerBounds(0, 0, getWidth(), getHeight());
    621             if (clipFlags) {
    622                 properties().getClippingRectForFlags(clipFlags, &layerBounds);
    623                 clipFlags = 0; // all clipping done by savelayer
    624             }
    625             SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
    626                     layerBounds.left, layerBounds.top,
    627                     layerBounds.right, layerBounds.bottom,
    628                     (int) (properties().getAlpha() * 255),
    629                     SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
    630             handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
    631         }
    632 
    633         if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) {
    634             // pretend alpha always causes savelayer to warn about
    635             // performance problem affecting old versions
    636             ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(),
    637                     static_cast<int>(getWidth()),
    638                     static_cast<int>(getHeight()));
    639         }
    640     }
    641     if (clipFlags) {
    642         Rect clipRect;
    643         properties().getClippingRectForFlags(clipFlags, &clipRect);
    644         ClipRectOp* op = new (handler.allocator()) ClipRectOp(
    645                 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
    646                 SkRegion::kIntersect_Op);
    647         handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
    648     }
    649 
    650     // TODO: support nesting round rect clips
    651     if (mProperties.getRevealClip().willClip()) {
    652         Rect bounds;
    653         mProperties.getRevealClip().getBounds(&bounds);
    654         renderer.setClippingRoundRect(handler.allocator(), bounds, mProperties.getRevealClip().getRadius());
    655     } else if (mProperties.getOutline().willClip()) {
    656         renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline()));
    657     }
    658 }
    659 
    660 /**
    661  * Apply property-based transformations to input matrix
    662  *
    663  * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
    664  * matrix computation instead of the Skia 3x3 matrix + camera hackery.
    665  */
    666 void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const {
    667     if (properties().getLeft() != 0 || properties().getTop() != 0) {
    668         matrix.translate(properties().getLeft(), properties().getTop());
    669     }
    670     if (properties().getStaticMatrix()) {
    671         mat4 stat(*properties().getStaticMatrix());
    672         matrix.multiply(stat);
    673     } else if (properties().getAnimationMatrix()) {
    674         mat4 anim(*properties().getAnimationMatrix());
    675         matrix.multiply(anim);
    676     }
    677 
    678     bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
    679     if (properties().hasTransformMatrix() || applyTranslationZ) {
    680         if (properties().isTransformTranslateOnly()) {
    681             matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
    682                     true3dTransform ? properties().getZ() : 0.0f);
    683         } else {
    684             if (!true3dTransform) {
    685                 matrix.multiply(*properties().getTransformMatrix());
    686             } else {
    687                 mat4 true3dMat;
    688                 true3dMat.loadTranslate(
    689                         properties().getPivotX() + properties().getTranslationX(),
    690                         properties().getPivotY() + properties().getTranslationY(),
    691                         properties().getZ());
    692                 true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
    693                 true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
    694                 true3dMat.rotate(properties().getRotation(), 0, 0, 1);
    695                 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1);
    696                 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY());
    697 
    698                 matrix.multiply(true3dMat);
    699             }
    700         }
    701     }
    702 }
    703 
    704 /**
    705  * Organizes the DisplayList hierarchy to prepare for background projection reordering.
    706  *
    707  * This should be called before a call to defer() or drawDisplayList()
    708  *
    709  * Each DisplayList that serves as a 3d root builds its list of composited children,
    710  * which are flagged to not draw in the standard draw loop.
    711  */
    712 void RenderNode::computeOrdering() {
    713     ATRACE_CALL();
    714     mProjectedNodes.clear();
    715 
    716     // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
    717     // transform properties are applied correctly to top level children
    718     if (mDisplayList == nullptr) return;
    719     for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
    720         renderNodeOp_t* childOp = mDisplayList->getChildren()[i];
    721         childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
    722     }
    723 }
    724 
    725 void RenderNode::computeOrderingImpl(
    726         renderNodeOp_t* opState,
    727         std::vector<renderNodeOp_t*>* compositedChildrenOfProjectionSurface,
    728         const mat4* transformFromProjectionSurface) {
    729     mProjectedNodes.clear();
    730     if (mDisplayList == nullptr || mDisplayList->isEmpty()) return;
    731 
    732     // TODO: should avoid this calculation in most cases
    733     // TODO: just calculate single matrix, down to all leaf composited elements
    734     Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
    735     localTransformFromProjectionSurface.multiply(opState->localMatrix);
    736 
    737     if (properties().getProjectBackwards()) {
    738         // composited projectee, flag for out of order draw, save matrix, and store in proj surface
    739         opState->skipInOrderDraw = true;
    740         opState->transformFromCompositingAncestor = localTransformFromProjectionSurface;
    741         compositedChildrenOfProjectionSurface->push_back(opState);
    742     } else {
    743         // standard in order draw
    744         opState->skipInOrderDraw = false;
    745     }
    746 
    747     if (mDisplayList->getChildren().size() > 0) {
    748         const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
    749         bool haveAppliedPropertiesToProjection = false;
    750         for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
    751             renderNodeOp_t* childOp = mDisplayList->getChildren()[i];
    752             RenderNode* child = childOp->renderNode;
    753 
    754             std::vector<renderNodeOp_t*>* projectionChildren = nullptr;
    755             const mat4* projectionTransform = nullptr;
    756             if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
    757                 // if receiving projections, collect projecting descendant
    758 
    759                 // Note that if a direct descendant is projecting backwards, we pass its
    760                 // grandparent projection collection, since it shouldn't project onto its
    761                 // parent, where it will already be drawing.
    762                 projectionChildren = &mProjectedNodes;
    763                 projectionTransform = &mat4::identity();
    764             } else {
    765                 if (!haveAppliedPropertiesToProjection) {
    766                     applyViewPropertyTransforms(localTransformFromProjectionSurface);
    767                     haveAppliedPropertiesToProjection = true;
    768                 }
    769                 projectionChildren = compositedChildrenOfProjectionSurface;
    770                 projectionTransform = &localTransformFromProjectionSurface;
    771             }
    772             child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
    773         }
    774     }
    775 }
    776 
    777 class DeferOperationHandler {
    778 public:
    779     DeferOperationHandler(DeferStateStruct& deferStruct, int level)
    780         : mDeferStruct(deferStruct), mLevel(level) {}
    781     inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
    782         operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
    783     }
    784     inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
    785     inline void startMark(const char* name) {} // do nothing
    786     inline void endMark() {}
    787     inline int level() { return mLevel; }
    788     inline int replayFlags() { return mDeferStruct.mReplayFlags; }
    789     inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); }
    790 
    791 private:
    792     DeferStateStruct& mDeferStruct;
    793     const int mLevel;
    794 };
    795 
    796 void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
    797     DeferOperationHandler handler(deferStruct, level);
    798     issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
    799 }
    800 
    801 class ReplayOperationHandler {
    802 public:
    803     ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
    804         : mReplayStruct(replayStruct), mLevel(level) {}
    805     inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
    806 #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
    807         mReplayStruct.mRenderer.eventMark(operation->name());
    808 #endif
    809         operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
    810     }
    811     inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
    812     inline void startMark(const char* name) {
    813         mReplayStruct.mRenderer.startMark(name);
    814     }
    815     inline void endMark() {
    816         mReplayStruct.mRenderer.endMark();
    817     }
    818     inline int level() { return mLevel; }
    819     inline int replayFlags() { return mReplayStruct.mReplayFlags; }
    820     inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); }
    821 
    822 private:
    823     ReplayStateStruct& mReplayStruct;
    824     const int mLevel;
    825 };
    826 
    827 void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
    828     ReplayOperationHandler handler(replayStruct, level);
    829     issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
    830 }
    831 
    832 void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk,
    833         std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
    834 #if !HWUI_NEW_OPS
    835     if (chunk.beginChildIndex == chunk.endChildIndex) return;
    836 
    837     for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
    838         DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
    839         RenderNode* child = childOp->renderNode;
    840         float childZ = child->properties().getZ();
    841 
    842         if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
    843             zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp));
    844             childOp->skipInOrderDraw = true;
    845         } else if (!child->properties().getProjectBackwards()) {
    846             // regular, in order drawing DisplayList
    847             childOp->skipInOrderDraw = false;
    848         }
    849     }
    850 
    851     // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
    852     std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
    853 #endif
    854 }
    855 
    856 template <class T>
    857 void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
    858     if (properties().getAlpha() <= 0.0f
    859             || properties().getOutline().getAlpha() <= 0.0f
    860             || !properties().getOutline().getPath()
    861             || properties().getScaleX() == 0
    862             || properties().getScaleY() == 0) {
    863         // no shadow to draw
    864         return;
    865     }
    866 
    867     mat4 shadowMatrixXY(transformFromParent);
    868     applyViewPropertyTransforms(shadowMatrixXY);
    869 
    870     // Z matrix needs actual 3d transformation, so mapped z values will be correct
    871     mat4 shadowMatrixZ(transformFromParent);
    872     applyViewPropertyTransforms(shadowMatrixZ, true);
    873 
    874     const SkPath* casterOutlinePath = properties().getOutline().getPath();
    875     const SkPath* revealClipPath = properties().getRevealClip().getPath();
    876     if (revealClipPath && revealClipPath->isEmpty()) return;
    877 
    878     float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha();
    879 
    880 
    881     // holds temporary SkPath to store the result of intersections
    882     SkPath* frameAllocatedPath = nullptr;
    883     const SkPath* outlinePath = casterOutlinePath;
    884 
    885     // intersect the outline with the reveal clip, if present
    886     if (revealClipPath) {
    887         frameAllocatedPath = handler.allocPathForFrame();
    888 
    889         Op(*outlinePath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath);
    890         outlinePath = frameAllocatedPath;
    891     }
    892 
    893     // intersect the outline with the clipBounds, if present
    894     if (properties().getClippingFlags() & CLIP_TO_CLIP_BOUNDS) {
    895         if (!frameAllocatedPath) {
    896             frameAllocatedPath = handler.allocPathForFrame();
    897         }
    898 
    899         Rect clipBounds;
    900         properties().getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
    901         SkPath clipBoundsPath;
    902         clipBoundsPath.addRect(clipBounds.left, clipBounds.top,
    903                 clipBounds.right, clipBounds.bottom);
    904 
    905         Op(*outlinePath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath);
    906         outlinePath = frameAllocatedPath;
    907     }
    908 
    909     DisplayListOp* shadowOp  = new (handler.allocator()) DrawShadowOp(
    910             shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath);
    911     handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
    912 }
    913 
    914 #define SHADOW_DELTA 0.1f
    915 
    916 template <class T>
    917 void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
    918         const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
    919         OpenGLRenderer& renderer, T& handler) {
    920     const int size = zTranslatedNodes.size();
    921     if (size == 0
    922             || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f)
    923             || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
    924         // no 3d children to draw
    925         return;
    926     }
    927 
    928     // Apply the base transform of the parent of the 3d children. This isolates
    929     // 3d children of the current chunk from transformations made in previous chunks.
    930     int rootRestoreTo = renderer.save(SaveFlags::Matrix);
    931     renderer.setGlobalMatrix(initialTransform);
    932 
    933     /**
    934      * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
    935      * with very similar Z heights to draw together.
    936      *
    937      * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
    938      * underneath both, and neither's shadow is drawn on top of the other.
    939      */
    940     const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
    941     size_t drawIndex, shadowIndex, endIndex;
    942     if (mode == ChildrenSelectMode::NegativeZChildren) {
    943         drawIndex = 0;
    944         endIndex = nonNegativeIndex;
    945         shadowIndex = endIndex; // draw no shadows
    946     } else {
    947         drawIndex = nonNegativeIndex;
    948         endIndex = size;
    949         shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
    950     }
    951 
    952     DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
    953             endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
    954 
    955     float lastCasterZ = 0.0f;
    956     while (shadowIndex < endIndex || drawIndex < endIndex) {
    957         if (shadowIndex < endIndex) {
    958             DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
    959             RenderNode* caster = casterOp->renderNode;
    960             const float casterZ = zTranslatedNodes[shadowIndex].key;
    961             // attempt to render the shadow if the caster about to be drawn is its caster,
    962             // OR if its caster's Z value is similar to the previous potential caster
    963             if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
    964                 caster->issueDrawShadowOperation(casterOp->localMatrix, handler);
    965 
    966                 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
    967                 shadowIndex++;
    968                 continue;
    969             }
    970         }
    971 
    972         // only the actual child DL draw needs to be in save/restore,
    973         // since it modifies the renderer's matrix
    974         int restoreTo = renderer.save(SaveFlags::Matrix);
    975 
    976         DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
    977 
    978         renderer.concatMatrix(childOp->localMatrix);
    979         childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
    980         handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
    981         childOp->skipInOrderDraw = true;
    982 
    983         renderer.restoreToCount(restoreTo);
    984         drawIndex++;
    985     }
    986     renderer.restoreToCount(rootRestoreTo);
    987 }
    988 
    989 template <class T>
    990 void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
    991     DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
    992     const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
    993     int restoreTo = renderer.getSaveCount();
    994 
    995     LinearAllocator& alloc = handler.allocator();
    996     handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
    997             PROPERTY_SAVECOUNT, properties().getClipToBounds());
    998 
    999     // Transform renderer to match background we're projecting onto
   1000     // (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
   1001     const DisplayListOp* op =
   1002 #if HWUI_NEW_OPS
   1003             nullptr;
   1004     LOG_ALWAYS_FATAL("unsupported");
   1005 #else
   1006             (mDisplayList->getOps()[mDisplayList->projectionReceiveIndex]);
   1007 #endif
   1008     const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op);
   1009     const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();
   1010     renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
   1011 
   1012     // If the projection receiver has an outline, we mask projected content to it
   1013     // (which we know, apriori, are all tessellated paths)
   1014     renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
   1015 
   1016     // draw projected nodes
   1017     for (size_t i = 0; i < mProjectedNodes.size(); i++) {
   1018         renderNodeOp_t* childOp = mProjectedNodes[i];
   1019 
   1020         // matrix save, concat, and restore can be done safely without allocating operations
   1021         int restoreTo = renderer.save(SaveFlags::Matrix);
   1022         renderer.concatMatrix(childOp->transformFromCompositingAncestor);
   1023         childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
   1024         handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
   1025         childOp->skipInOrderDraw = true;
   1026         renderer.restoreToCount(restoreTo);
   1027     }
   1028 
   1029     handler(new (alloc) RestoreToCountOp(restoreTo),
   1030             PROPERTY_SAVECOUNT, properties().getClipToBounds());
   1031 }
   1032 
   1033 /**
   1034  * This function serves both defer and replay modes, and will organize the displayList's component
   1035  * operations for a single frame:
   1036  *
   1037  * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
   1038  * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
   1039  * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
   1040  * defer vs replay logic, per operation
   1041  */
   1042 template <class T>
   1043 void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
   1044     if (mDisplayList->isEmpty()) {
   1045         DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "",
   1046                 this, getName());
   1047         return;
   1048     }
   1049 
   1050 #if HWUI_NEW_OPS
   1051     const bool drawLayer = false;
   1052 #else
   1053     const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
   1054 #endif
   1055     // If we are updating the contents of mLayer, we don't want to apply any of
   1056     // the RenderNode's properties to this issueOperations pass. Those will all
   1057     // be applied when the layer is drawn, aka when this is true.
   1058     const bool useViewProperties = (!mLayer || drawLayer);
   1059     if (useViewProperties) {
   1060         const Outline& outline = properties().getOutline();
   1061         if (properties().getAlpha() <= 0
   1062                 || (outline.getShouldClip() && outline.isEmpty())
   1063                 || properties().getScaleX() == 0
   1064                 || properties().getScaleY() == 0) {
   1065             DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
   1066                     this, getName());
   1067             return;
   1068         }
   1069     }
   1070 
   1071     handler.startMark(getName());
   1072 
   1073 #if DEBUG_DISPLAY_LIST
   1074     const Rect& clipRect = renderer.getLocalClipBounds();
   1075     DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
   1076             handler.level() * 2, "", this, getName(),
   1077             clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
   1078 #endif
   1079 
   1080     LinearAllocator& alloc = handler.allocator();
   1081     int restoreTo = renderer.getSaveCount();
   1082     handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
   1083             PROPERTY_SAVECOUNT, properties().getClipToBounds());
   1084 
   1085     DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "",
   1086             SaveFlags::MatrixClip, restoreTo);
   1087 
   1088     if (useViewProperties) {
   1089         setViewProperties<T>(renderer, handler);
   1090     }
   1091 
   1092 #if HWUI_NEW_OPS
   1093     LOG_ALWAYS_FATAL("legacy op traversal not supported");
   1094 #else
   1095     bool quickRejected = properties().getClipToBounds()
   1096             && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
   1097     if (!quickRejected) {
   1098         Matrix4 initialTransform(*(renderer.currentTransform()));
   1099         renderer.setBaseTransform(initialTransform);
   1100 
   1101         if (drawLayer) {
   1102             handler(new (alloc) DrawLayerOp(mLayer),
   1103                     renderer.getSaveCount() - 1, properties().getClipToBounds());
   1104         } else {
   1105             const int saveCountOffset = renderer.getSaveCount() - 1;
   1106             const int projectionReceiveIndex = mDisplayList->projectionReceiveIndex;
   1107             for (size_t chunkIndex = 0; chunkIndex < mDisplayList->getChunks().size(); chunkIndex++) {
   1108                 const DisplayList::Chunk& chunk = mDisplayList->getChunks()[chunkIndex];
   1109 
   1110                 std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
   1111                 buildZSortedChildList(chunk, zTranslatedNodes);
   1112 
   1113                 issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren,
   1114                         initialTransform, zTranslatedNodes, renderer, handler);
   1115 
   1116                 for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
   1117                     DisplayListOp *op = mDisplayList->getOps()[opIndex];
   1118 #if DEBUG_DISPLAY_LIST
   1119                     op->output(handler.level() + 1);
   1120 #endif
   1121                     handler(op, saveCountOffset, properties().getClipToBounds());
   1122 
   1123                     if (CC_UNLIKELY(!mProjectedNodes.empty() && projectionReceiveIndex >= 0 &&
   1124                         opIndex == static_cast<size_t>(projectionReceiveIndex))) {
   1125                         issueOperationsOfProjectedChildren(renderer, handler);
   1126                     }
   1127                 }
   1128 
   1129                 issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren,
   1130                         initialTransform, zTranslatedNodes, renderer, handler);
   1131             }
   1132         }
   1133     }
   1134 #endif
   1135 
   1136     DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo);
   1137     handler(new (alloc) RestoreToCountOp(restoreTo),
   1138             PROPERTY_SAVECOUNT, properties().getClipToBounds());
   1139 
   1140     DISPLAY_LIST_LOGD("%*sDone (%p, %s)", handler.level() * 2, "", this, getName());
   1141     handler.endMark();
   1142 }
   1143 
   1144 } /* namespace uirenderer */
   1145 } /* namespace android */
   1146