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