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