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