RenderNode.cpp revision 6a21ca5a4c6b62ae277ae6dcb14bc187460321a0
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 "BakedOpRenderer.h" 20#include "DamageAccumulator.h" 21#include "Debug.h" 22#include "OpDumper.h" 23#include "RecordedOp.h" 24#include "TreeInfo.h" 25#include "utils/MathUtils.h" 26#include "utils/StringUtils.h" 27#include "utils/TraceUtils.h" 28#include "VectorDrawable.h" 29#include "renderstate/RenderState.h" 30#include "renderthread/CanvasContext.h" 31 32#include "protos/hwui.pb.h" 33#include "protos/ProtoHelpers.h" 34 35#include <algorithm> 36#include <sstream> 37#include <string> 38 39namespace android { 40namespace uirenderer { 41 42RenderNode::RenderNode() 43 : mDirtyPropertyFields(0) 44 , mNeedsDisplayListSync(false) 45 , mDisplayList(nullptr) 46 , mStagingDisplayList(nullptr) 47 , mAnimatorManager(*this) 48 , mParentCount(0) { 49} 50 51RenderNode::~RenderNode() { 52 deleteDisplayList(nullptr); 53 delete mStagingDisplayList; 54 LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!"); 55} 56 57void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* observer) { 58 mNeedsDisplayListSync = true; 59 delete mStagingDisplayList; 60 mStagingDisplayList = displayList; 61 // If mParentCount == 0 we are the sole reference to this RenderNode, 62 // so immediately free the old display list 63 if (!mParentCount && !mStagingDisplayList) { 64 deleteDisplayList(observer); 65 } 66} 67 68/** 69 * This function is a simplified version of replay(), where we simply retrieve and log the 70 * display list. This function should remain in sync with the replay() function. 71 */ 72void RenderNode::output() { 73 LogcatStream strout; 74 strout << "Root"; 75 output(strout, 0); 76} 77 78void RenderNode::output(std::ostream& output, uint32_t level) { 79 output << " (" << getName() << " " << this 80 << (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : "") 81 << (properties().hasShadow() ? ", casting shadow" : "") 82 << (isRenderable() ? "" : ", empty") 83 << (properties().getProjectBackwards() ? ", projected" : "") 84 << (mLayer != nullptr ? ", on HW Layer" : "") 85 << ")" << std::endl; 86 87 properties().debugOutputProperties(output, level + 1); 88 89 if (mDisplayList) { 90 for (auto&& op : mDisplayList->getOps()) { 91 OpDumper::dump(*op, output, level + 1); 92 if (op->opId == RecordedOpId::RenderNodeOp) { 93 auto rnOp = reinterpret_cast<const RenderNodeOp*>(op); 94 rnOp->renderNode->output(output, level + 1); 95 } else { 96 output << std::endl; 97 } 98 } 99 } 100 output << std::string(level * 2, ' ') << "/RenderNode(" << getName() << " " << this << ")"; 101 output << std::endl; 102} 103 104void RenderNode::copyTo(proto::RenderNode *pnode) { 105 pnode->set_id(static_cast<uint64_t>( 106 reinterpret_cast<uintptr_t>(this))); 107 pnode->set_name(mName.string(), mName.length()); 108 109 proto::RenderProperties* pprops = pnode->mutable_properties(); 110 pprops->set_left(properties().getLeft()); 111 pprops->set_top(properties().getTop()); 112 pprops->set_right(properties().getRight()); 113 pprops->set_bottom(properties().getBottom()); 114 pprops->set_clip_flags(properties().getClippingFlags()); 115 pprops->set_alpha(properties().getAlpha()); 116 pprops->set_translation_x(properties().getTranslationX()); 117 pprops->set_translation_y(properties().getTranslationY()); 118 pprops->set_translation_z(properties().getTranslationZ()); 119 pprops->set_elevation(properties().getElevation()); 120 pprops->set_rotation(properties().getRotation()); 121 pprops->set_rotation_x(properties().getRotationX()); 122 pprops->set_rotation_y(properties().getRotationY()); 123 pprops->set_scale_x(properties().getScaleX()); 124 pprops->set_scale_y(properties().getScaleY()); 125 pprops->set_pivot_x(properties().getPivotX()); 126 pprops->set_pivot_y(properties().getPivotY()); 127 pprops->set_has_overlapping_rendering(properties().getHasOverlappingRendering()); 128 pprops->set_pivot_explicitly_set(properties().isPivotExplicitlySet()); 129 pprops->set_project_backwards(properties().getProjectBackwards()); 130 pprops->set_projection_receiver(properties().isProjectionReceiver()); 131 set(pprops->mutable_clip_bounds(), properties().getClipBounds()); 132 133 const Outline& outline = properties().getOutline(); 134 if (outline.getType() != Outline::Type::None) { 135 proto::Outline* poutline = pprops->mutable_outline(); 136 poutline->clear_path(); 137 if (outline.getType() == Outline::Type::Empty) { 138 poutline->set_type(proto::Outline_Type_Empty); 139 } else if (outline.getType() == Outline::Type::ConvexPath) { 140 poutline->set_type(proto::Outline_Type_ConvexPath); 141 if (const SkPath* path = outline.getPath()) { 142 set(poutline->mutable_path(), *path); 143 } 144 } else if (outline.getType() == Outline::Type::RoundRect) { 145 poutline->set_type(proto::Outline_Type_RoundRect); 146 } else { 147 ALOGW("Uknown outline type! %d", static_cast<int>(outline.getType())); 148 poutline->set_type(proto::Outline_Type_None); 149 } 150 poutline->set_should_clip(outline.getShouldClip()); 151 poutline->set_alpha(outline.getAlpha()); 152 poutline->set_radius(outline.getRadius()); 153 set(poutline->mutable_bounds(), outline.getBounds()); 154 } else { 155 pprops->clear_outline(); 156 } 157 158 const RevealClip& revealClip = properties().getRevealClip(); 159 if (revealClip.willClip()) { 160 proto::RevealClip* prevealClip = pprops->mutable_reveal_clip(); 161 prevealClip->set_x(revealClip.getX()); 162 prevealClip->set_y(revealClip.getY()); 163 prevealClip->set_radius(revealClip.getRadius()); 164 } else { 165 pprops->clear_reveal_clip(); 166 } 167 168 pnode->clear_children(); 169 if (mDisplayList) { 170 for (auto&& child : mDisplayList->getChildren()) { 171 child->renderNode->copyTo(pnode->add_children()); 172 } 173 } 174} 175 176int RenderNode::getDebugSize() { 177 int size = sizeof(RenderNode); 178 if (mStagingDisplayList) { 179 size += mStagingDisplayList->getUsedSize(); 180 } 181 if (mDisplayList && mDisplayList != mStagingDisplayList) { 182 size += mDisplayList->getUsedSize(); 183 } 184 return size; 185} 186 187void RenderNode::prepareTree(TreeInfo& info) { 188 ATRACE_CALL(); 189 LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing"); 190 191 // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer. 192 bool functorsNeedLayer = Properties::debugOverdraw; 193 194 prepareTreeImpl(info, functorsNeedLayer); 195} 196 197void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) { 198 mAnimatorManager.addAnimator(animator); 199} 200 201void RenderNode::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) { 202 mAnimatorManager.removeAnimator(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) 237 || CC_UNLIKELY(!isRenderable()) 238 || CC_UNLIKELY(properties().getWidth() == 0) 239 || CC_UNLIKELY(properties().getHeight() == 0)) { 240 if (CC_UNLIKELY(mLayer)) { 241 renderthread::CanvasContext::destroyLayer(this); 242 } 243 return; 244 } 245 246 if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) { 247 damageSelf(info); 248 } 249 250 if (!mLayer) { 251 Caches::getInstance().dumpMemoryUsage(); 252 if (info.errorHandler) { 253 std::ostringstream err; 254 err << "Unable to create layer for " << getName(); 255 const int maxTextureSize = Caches::getInstance().maxTextureSize; 256 if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) { 257 err << ", size " << getWidth() << "x" << getHeight() 258 << " exceeds max size " << maxTextureSize; 259 } else { 260 err << ", see logcat for more info"; 261 } 262 info.errorHandler->onError(err.str()); 263 } 264 return; 265 } 266 267 SkRect dirty; 268 info.damageAccumulator->peekAtDirty(&dirty); 269 info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty); 270 271 // There might be prefetched layers that need to be accounted for. 272 // That might be us, so tell CanvasContext that this layer is in the 273 // tree and should not be destroyed. 274 info.canvasContext.markLayerInUse(this); 275} 276 277/** 278 * Traverse down the the draw tree to prepare for a frame. 279 * 280 * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven 281 * 282 * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the 283 * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer. 284 */ 285void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) { 286 info.damageAccumulator->pushTransform(this); 287 288 if (info.mode == TreeInfo::MODE_FULL) { 289 pushStagingPropertiesChanges(info); 290 } 291 uint32_t animatorDirtyMask = 0; 292 if (CC_LIKELY(info.runAnimations)) { 293 animatorDirtyMask = mAnimatorManager.animate(info); 294 } 295 296 bool willHaveFunctor = false; 297 if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) { 298 willHaveFunctor = !mStagingDisplayList->getFunctors().empty(); 299 } else if (mDisplayList) { 300 willHaveFunctor = !mDisplayList->getFunctors().empty(); 301 } 302 bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence( 303 willHaveFunctor, functorsNeedLayer); 304 305 if (CC_UNLIKELY(mPositionListener.get())) { 306 mPositionListener->onPositionUpdated(*this, info); 307 } 308 309 prepareLayer(info, animatorDirtyMask); 310 if (info.mode == TreeInfo::MODE_FULL) { 311 pushStagingDisplayListChanges(info); 312 } 313 prepareSubTree(info, childFunctorsNeedLayer, mDisplayList); 314 315 if (mDisplayList) { 316 for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { 317 // If any vector drawable in the display list needs update, damage the node. 318 if (vectorDrawable->isDirty()) { 319 damageSelf(info); 320 } 321 vectorDrawable->setPropertyChangeWillBeConsumed(true); 322 } 323 } 324 pushLayerUpdate(info); 325 326 info.damageAccumulator->popTransform(); 327} 328 329void RenderNode::syncProperties() { 330 mProperties = mStagingProperties; 331} 332 333void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { 334 // Push the animators first so that setupStartValueIfNecessary() is called 335 // before properties() is trampled by stagingProperties(), as they are 336 // required by some animators. 337 if (CC_LIKELY(info.runAnimations)) { 338 mAnimatorManager.pushStaging(); 339 } 340 if (mDirtyPropertyFields) { 341 mDirtyPropertyFields = 0; 342 damageSelf(info); 343 info.damageAccumulator->popTransform(); 344 syncProperties(); 345 // We could try to be clever and only re-damage if the matrix changed. 346 // However, we don't need to worry about that. The cost of over-damaging 347 // here is only going to be a single additional map rect of this node 348 // plus a rect join(). The parent's transform (and up) will only be 349 // performed once. 350 info.damageAccumulator->pushTransform(this); 351 damageSelf(info); 352 } 353} 354 355void RenderNode::syncDisplayList(TreeInfo* info) { 356 // Make sure we inc first so that we don't fluctuate between 0 and 1, 357 // which would thrash the layer cache 358 if (mStagingDisplayList) { 359 for (auto&& child : mStagingDisplayList->getChildren()) { 360 child->renderNode->incParentRefCount(); 361 } 362 } 363 deleteDisplayList(info ? info->observer : nullptr, info); 364 mDisplayList = mStagingDisplayList; 365 mStagingDisplayList = nullptr; 366 if (mDisplayList) { 367 for (auto& iter : mDisplayList->getFunctors()) { 368 (*iter.functor)(DrawGlInfo::kModeSync, nullptr); 369 } 370 for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { 371 vectorDrawable->syncProperties(); 372 } 373 } 374} 375 376void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { 377 if (mNeedsDisplayListSync) { 378 mNeedsDisplayListSync = false; 379 // Damage with the old display list first then the new one to catch any 380 // changes in isRenderable or, in the future, bounds 381 damageSelf(info); 382 syncDisplayList(&info); 383 damageSelf(info); 384 } 385} 386 387void RenderNode::deleteDisplayList(TreeObserver* observer, TreeInfo* info) { 388 if (mDisplayList) { 389 for (auto&& child : mDisplayList->getChildren()) { 390 child->renderNode->decParentRefCount(observer, info); 391 } 392 } 393 delete mDisplayList; 394 mDisplayList = nullptr; 395} 396 397void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) { 398 if (subtree) { 399 TextureCache& cache = Caches::getInstance().textureCache; 400 info.out.hasFunctors |= subtree->getFunctors().size(); 401 for (auto&& bitmapResource : subtree->getBitmapResources()) { 402 void* ownerToken = &info.canvasContext; 403 info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource); 404 } 405 for (auto&& op : subtree->getChildren()) { 406 RenderNode* childNode = op->renderNode; 407 info.damageAccumulator->pushTransform(&op->localMatrix); 408 bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; 409 childNode->prepareTreeImpl(info, childFunctorsNeedLayer); 410 info.damageAccumulator->popTransform(); 411 } 412 } 413} 414 415void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) { 416 if (mLayer) { 417 renderthread::CanvasContext::destroyLayer(this); 418 } 419 if (mDisplayList) { 420 for (auto&& child : mDisplayList->getChildren()) { 421 child->renderNode->destroyHardwareResources(observer, info); 422 } 423 if (mNeedsDisplayListSync) { 424 // Next prepare tree we are going to push a new display list, so we can 425 // drop our current one now 426 deleteDisplayList(observer, info); 427 } 428 } 429} 430 431void RenderNode::decParentRefCount(TreeObserver* observer, TreeInfo* info) { 432 LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!"); 433 mParentCount--; 434 if (!mParentCount) { 435 if (observer) { 436 observer->onMaybeRemovedFromTree(this); 437 } 438 if (CC_UNLIKELY(mPositionListener.get())) { 439 mPositionListener->onPositionLost(*this, info); 440 } 441 // If a child of ours is being attached to our parent then this will incorrectly 442 // destroy its hardware resources. However, this situation is highly unlikely 443 // and the failure is "just" that the layer is re-created, so this should 444 // be safe enough 445 destroyHardwareResources(observer, info); 446 } 447} 448 449/** 450 * Apply property-based transformations to input matrix 451 * 452 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4 453 * matrix computation instead of the Skia 3x3 matrix + camera hackery. 454 */ 455void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const { 456 if (properties().getLeft() != 0 || properties().getTop() != 0) { 457 matrix.translate(properties().getLeft(), properties().getTop()); 458 } 459 if (properties().getStaticMatrix()) { 460 mat4 stat(*properties().getStaticMatrix()); 461 matrix.multiply(stat); 462 } else if (properties().getAnimationMatrix()) { 463 mat4 anim(*properties().getAnimationMatrix()); 464 matrix.multiply(anim); 465 } 466 467 bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ()); 468 if (properties().hasTransformMatrix() || applyTranslationZ) { 469 if (properties().isTransformTranslateOnly()) { 470 matrix.translate(properties().getTranslationX(), properties().getTranslationY(), 471 true3dTransform ? properties().getZ() : 0.0f); 472 } else { 473 if (!true3dTransform) { 474 matrix.multiply(*properties().getTransformMatrix()); 475 } else { 476 mat4 true3dMat; 477 true3dMat.loadTranslate( 478 properties().getPivotX() + properties().getTranslationX(), 479 properties().getPivotY() + properties().getTranslationY(), 480 properties().getZ()); 481 true3dMat.rotate(properties().getRotationX(), 1, 0, 0); 482 true3dMat.rotate(properties().getRotationY(), 0, 1, 0); 483 true3dMat.rotate(properties().getRotation(), 0, 0, 1); 484 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1); 485 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY()); 486 487 matrix.multiply(true3dMat); 488 } 489 } 490 } 491} 492 493/** 494 * Organizes the DisplayList hierarchy to prepare for background projection reordering. 495 * 496 * This should be called before a call to defer() or drawDisplayList() 497 * 498 * Each DisplayList that serves as a 3d root builds its list of composited children, 499 * which are flagged to not draw in the standard draw loop. 500 */ 501void RenderNode::computeOrdering() { 502 ATRACE_CALL(); 503 mProjectedNodes.clear(); 504 505 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that 506 // transform properties are applied correctly to top level children 507 if (mDisplayList == nullptr) return; 508 for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { 509 RenderNodeOp* childOp = mDisplayList->getChildren()[i]; 510 childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); 511 } 512} 513 514void RenderNode::computeOrderingImpl( 515 RenderNodeOp* opState, 516 std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, 517 const mat4* transformFromProjectionSurface) { 518 mProjectedNodes.clear(); 519 if (mDisplayList == nullptr || mDisplayList->isEmpty()) return; 520 521 // TODO: should avoid this calculation in most cases 522 // TODO: just calculate single matrix, down to all leaf composited elements 523 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); 524 localTransformFromProjectionSurface.multiply(opState->localMatrix); 525 526 if (properties().getProjectBackwards()) { 527 // composited projectee, flag for out of order draw, save matrix, and store in proj surface 528 opState->skipInOrderDraw = true; 529 opState->transformFromCompositingAncestor = localTransformFromProjectionSurface; 530 compositedChildrenOfProjectionSurface->push_back(opState); 531 } else { 532 // standard in order draw 533 opState->skipInOrderDraw = false; 534 } 535 536 if (mDisplayList->getChildren().size() > 0) { 537 const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0; 538 bool haveAppliedPropertiesToProjection = false; 539 for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { 540 RenderNodeOp* childOp = mDisplayList->getChildren()[i]; 541 RenderNode* child = childOp->renderNode; 542 543 std::vector<RenderNodeOp*>* projectionChildren = nullptr; 544 const mat4* projectionTransform = nullptr; 545 if (isProjectionReceiver && !child->properties().getProjectBackwards()) { 546 // if receiving projections, collect projecting descendant 547 548 // Note that if a direct descendant is projecting backwards, we pass its 549 // grandparent projection collection, since it shouldn't project onto its 550 // parent, where it will already be drawing. 551 projectionChildren = &mProjectedNodes; 552 projectionTransform = &mat4::identity(); 553 } else { 554 if (!haveAppliedPropertiesToProjection) { 555 applyViewPropertyTransforms(localTransformFromProjectionSurface); 556 haveAppliedPropertiesToProjection = true; 557 } 558 projectionChildren = compositedChildrenOfProjectionSurface; 559 projectionTransform = &localTransformFromProjectionSurface; 560 } 561 child->computeOrderingImpl(childOp, projectionChildren, projectionTransform); 562 } 563 } 564} 565 566} /* namespace uirenderer */ 567} /* namespace android */ 568