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