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