• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
outputLogBuffer(int fd)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 
debugDumpLayers(const char * prefix)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 
RenderNode()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 
~RenderNode()84 RenderNode::~RenderNode() {
85     deleteDisplayListData();
86     delete mStagingDisplayListData;
87     LayerRenderer::destroyLayerDeferred(mLayer);
88 }
89 
setStagingDisplayList(DisplayListData * data)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  */
output(uint32_t level)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 
getDebugSize()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 
prepareTree(TreeInfo & info)132 void RenderNode::prepareTree(TreeInfo& info) {
133     ATRACE_CALL();
134     LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
135 
136     prepareTreeImpl(info);
137 }
138 
addAnimator(const sp<BaseRenderNodeAnimator> & animator)139 void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
140     mAnimatorManager.addAnimator(animator);
141 }
142 
damageSelf(TreeInfo & info)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 
prepareLayer(TreeInfo & info,uint32_t dirtyMask)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 
pushLayerUpdate(TreeInfo & info)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 
prepareTreeImpl(TreeInfo & info)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 
pushStagingPropertiesChanges(TreeInfo & info)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 
applyLayerPropertiesToLayer(TreeInfo & info)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 
pushStagingDisplayListChanges(TreeInfo & info)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 
deleteDisplayListData()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 
prepareSubTree(TreeInfo & info,DisplayListData * subtree)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 
destroyHardwareResources()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 
decParentRefCount()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>
setViewProperties(OpenGLRenderer & renderer,T & handler)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  */
applyViewPropertyTransforms(mat4 & matrix,bool true3dTransform) const445 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  */
computeOrdering()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 
computeOrderingImpl(DrawRenderNodeOp * opState,const SkPath * outlineOfProjectionSurface,Vector<DrawRenderNodeOp * > * compositedChildrenOfProjectionSurface,const mat4 * transformFromProjectionSurface)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:
DeferOperationHandler(DeferStateStruct & deferStruct,int level)564     DeferOperationHandler(DeferStateStruct& deferStruct, int level)
565         : mDeferStruct(deferStruct), mLevel(level) {}
operator ()(DisplayListOp * operation,int saveCount,bool clipToBounds)566     inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
567         operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
568     }
allocator()569     inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
startMark(const char * name)570     inline void startMark(const char* name) {} // do nothing
endMark()571     inline void endMark() {}
level()572     inline int level() { return mLevel; }
replayFlags()573     inline int replayFlags() { return mDeferStruct.mReplayFlags; }
allocPathForFrame()574     inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); }
575 
576 private:
577     DeferStateStruct& mDeferStruct;
578     const int mLevel;
579 };
580 
defer(DeferStateStruct & deferStruct,const int level)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:
ReplayOperationHandler(ReplayStateStruct & replayStruct,int level)588     ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
589         : mReplayStruct(replayStruct), mLevel(level) {}
operator ()(DisplayListOp * operation,int saveCount,bool clipToBounds)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     }
allocator()596     inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
startMark(const char * name)597     inline void startMark(const char* name) {
598         mReplayStruct.mRenderer.startMark(name);
599     }
endMark()600     inline void endMark() {
601         mReplayStruct.mRenderer.endMark();
602     }
level()603     inline int level() { return mLevel; }
replayFlags()604     inline int replayFlags() { return mReplayStruct.mReplayFlags; }
allocPathForFrame()605     inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); }
606 
607 private:
608     ReplayStateStruct& mReplayStruct;
609     const int mLevel;
610 };
611 
replay(ReplayStateStruct & replayStruct,const int level)612 void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
613     ReplayOperationHandler handler(replayStruct, level);
614     issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
615 }
616 
buildZSortedChildList(const DisplayListData::Chunk & chunk,Vector<ZDrawRenderNodeOpPair> & zTranslatedNodes)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>
issueDrawShadowOperation(const Matrix4 & transformFromParent,T & handler)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>
issueOperationsOf3dChildren(ChildrenSelectMode mode,const Matrix4 & initialTransform,const Vector<ZDrawRenderNodeOpPair> & zTranslatedNodes,OpenGLRenderer & renderer,T & handler)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>
issueOperationsOfProjectedChildren(OpenGLRenderer & renderer,T & handler)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>
issueOperations(OpenGLRenderer & renderer,T & handler)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