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 if (CC_UNLIKELY(hasLayer())) { 246 renderthread::CanvasContext::destroyLayer(this); 247 } 248 return; 249 } 250 251 if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) { 252 damageSelf(info); 253 } 254 255 if (!hasLayer()) { 256 Caches::getInstance().dumpMemoryUsage(); 257 if (info.errorHandler) { 258 std::ostringstream err; 259 err << "Unable to create layer for " << getName(); 260 const int maxTextureSize = Caches::getInstance().maxTextureSize; 261 if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) { 262 err << ", size " << getWidth() << "x" << getHeight() 263 << " exceeds max size " << maxTextureSize; 264 } else { 265 err << ", see logcat for more info"; 266 } 267 info.errorHandler->onError(err.str()); 268 } 269 return; 270 } 271 272 SkRect dirty; 273 info.damageAccumulator->peekAtDirty(&dirty); 274 info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty); 275 276 // There might be prefetched layers that need to be accounted for. 277 // That might be us, so tell CanvasContext that this layer is in the 278 // tree and should not be destroyed. 279 info.canvasContext.markLayerInUse(this); 280 } 281 282 /** 283 * Traverse down the the draw tree to prepare for a frame. 284 * 285 * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven 286 * 287 * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the 288 * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer. 289 */ 290 void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) { 291 info.damageAccumulator->pushTransform(this); 292 293 if (info.mode == TreeInfo::MODE_FULL) { 294 pushStagingPropertiesChanges(info); 295 } 296 uint32_t animatorDirtyMask = 0; 297 if (CC_LIKELY(info.runAnimations)) { 298 animatorDirtyMask = mAnimatorManager.animate(info); 299 } 300 301 bool willHaveFunctor = false; 302 if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) { 303 willHaveFunctor = mStagingDisplayList->hasFunctor(); 304 } else if (mDisplayList) { 305 willHaveFunctor = mDisplayList->hasFunctor(); 306 } 307 bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence( 308 willHaveFunctor, functorsNeedLayer); 309 310 if (CC_UNLIKELY(mPositionListener.get())) { 311 mPositionListener->onPositionUpdated(*this, info); 312 } 313 314 prepareLayer(info, animatorDirtyMask); 315 if (info.mode == TreeInfo::MODE_FULL) { 316 pushStagingDisplayListChanges(observer, info); 317 } 318 319 if (mDisplayList) { 320 info.out.hasFunctors |= mDisplayList->hasFunctor(); 321 bool isDirty = mDisplayList->prepareListAndChildren(observer, info, childFunctorsNeedLayer, 322 [](RenderNode* child, TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) { 323 child->prepareTreeImpl(observer, info, functorsNeedLayer); 324 }); 325 if (isDirty) { 326 damageSelf(info); 327 } 328 } 329 pushLayerUpdate(info); 330 331 info.damageAccumulator->popTransform(); 332 } 333 334 void RenderNode::syncProperties() { 335 mProperties = mStagingProperties; 336 } 337 338 void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { 339 // Push the animators first so that setupStartValueIfNecessary() is called 340 // before properties() is trampled by stagingProperties(), as they are 341 // required by some animators. 342 if (CC_LIKELY(info.runAnimations)) { 343 mAnimatorManager.pushStaging(); 344 } 345 if (mDirtyPropertyFields) { 346 mDirtyPropertyFields = 0; 347 damageSelf(info); 348 info.damageAccumulator->popTransform(); 349 syncProperties(); 350 // We could try to be clever and only re-damage if the matrix changed. 351 // However, we don't need to worry about that. The cost of over-damaging 352 // here is only going to be a single additional map rect of this node 353 // plus a rect join(). The parent's transform (and up) will only be 354 // performed once. 355 info.damageAccumulator->pushTransform(this); 356 damageSelf(info); 357 } 358 } 359 360 void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { 361 // Make sure we inc first so that we don't fluctuate between 0 and 1, 362 // which would thrash the layer cache 363 if (mStagingDisplayList) { 364 mStagingDisplayList->updateChildren([](RenderNode* child) { 365 child->incParentRefCount(); 366 }); 367 } 368 deleteDisplayList(observer, info); 369 mDisplayList = mStagingDisplayList; 370 mStagingDisplayList = nullptr; 371 if (mDisplayList) { 372 mDisplayList->syncContents(); 373 } 374 } 375 376 void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, 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(observer, &info); 383 damageSelf(info); 384 } 385 } 386 387 void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) { 388 if (mDisplayList) { 389 mDisplayList->updateChildren([&observer, info](RenderNode* child) { 390 child->decParentRefCount(observer, info); 391 }); 392 if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) { 393 delete mDisplayList; 394 } 395 } 396 mDisplayList = nullptr; 397 } 398 399 void RenderNode::destroyHardwareResources(TreeInfo* info) { 400 if (hasLayer()) { 401 renderthread::CanvasContext::destroyLayer(this); 402 } 403 setStagingDisplayList(nullptr); 404 405 ImmediateRemoved observer(info); 406 deleteDisplayList(observer, info); 407 } 408 409 void RenderNode::destroyLayers() { 410 if (hasLayer()) { 411 renderthread::CanvasContext::destroyLayer(this); 412 } 413 if (mDisplayList) { 414 mDisplayList->updateChildren([](RenderNode* child) { 415 child->destroyLayers(); 416 }); 417 } 418 } 419 420 void RenderNode::decParentRefCount(TreeObserver& observer, TreeInfo* info) { 421 LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!"); 422 mParentCount--; 423 if (!mParentCount) { 424 observer.onMaybeRemovedFromTree(this); 425 if (CC_UNLIKELY(mPositionListener.get())) { 426 mPositionListener->onPositionLost(*this, info); 427 } 428 } 429 } 430 431 void RenderNode::onRemovedFromTree(TreeInfo* info) { 432 destroyHardwareResources(info); 433 } 434 435 void RenderNode::clearRoot() { 436 ImmediateRemoved observer(nullptr); 437 decParentRefCount(observer); 438 } 439 440 /** 441 * Apply property-based transformations to input matrix 442 * 443 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4 444 * matrix computation instead of the Skia 3x3 matrix + camera hackery. 445 */ 446 void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const { 447 if (properties().getLeft() != 0 || properties().getTop() != 0) { 448 matrix.translate(properties().getLeft(), properties().getTop()); 449 } 450 if (properties().getStaticMatrix()) { 451 mat4 stat(*properties().getStaticMatrix()); 452 matrix.multiply(stat); 453 } else if (properties().getAnimationMatrix()) { 454 mat4 anim(*properties().getAnimationMatrix()); 455 matrix.multiply(anim); 456 } 457 458 bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ()); 459 if (properties().hasTransformMatrix() || applyTranslationZ) { 460 if (properties().isTransformTranslateOnly()) { 461 matrix.translate(properties().getTranslationX(), properties().getTranslationY(), 462 true3dTransform ? properties().getZ() : 0.0f); 463 } else { 464 if (!true3dTransform) { 465 matrix.multiply(*properties().getTransformMatrix()); 466 } else { 467 mat4 true3dMat; 468 true3dMat.loadTranslate( 469 properties().getPivotX() + properties().getTranslationX(), 470 properties().getPivotY() + properties().getTranslationY(), 471 properties().getZ()); 472 true3dMat.rotate(properties().getRotationX(), 1, 0, 0); 473 true3dMat.rotate(properties().getRotationY(), 0, 1, 0); 474 true3dMat.rotate(properties().getRotation(), 0, 0, 1); 475 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1); 476 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY()); 477 478 matrix.multiply(true3dMat); 479 } 480 } 481 } 482 } 483 484 /** 485 * Organizes the DisplayList hierarchy to prepare for background projection reordering. 486 * 487 * This should be called before a call to defer() or drawDisplayList() 488 * 489 * Each DisplayList that serves as a 3d root builds its list of composited children, 490 * which are flagged to not draw in the standard draw loop. 491 */ 492 void RenderNode::computeOrdering() { 493 ATRACE_CALL(); 494 mProjectedNodes.clear(); 495 496 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that 497 // transform properties are applied correctly to top level children 498 if (mDisplayList == nullptr) return; 499 for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { 500 RenderNodeOp* childOp = mDisplayList->getChildren()[i]; 501 childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); 502 } 503 } 504 505 void RenderNode::computeOrderingImpl( 506 RenderNodeOp* opState, 507 std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface, 508 const mat4* transformFromProjectionSurface) { 509 mProjectedNodes.clear(); 510 if (mDisplayList == nullptr || mDisplayList->isEmpty()) return; 511 512 // TODO: should avoid this calculation in most cases 513 // TODO: just calculate single matrix, down to all leaf composited elements 514 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); 515 localTransformFromProjectionSurface.multiply(opState->localMatrix); 516 517 if (properties().getProjectBackwards()) { 518 // composited projectee, flag for out of order draw, save matrix, and store in proj surface 519 opState->skipInOrderDraw = true; 520 opState->transformFromCompositingAncestor = localTransformFromProjectionSurface; 521 compositedChildrenOfProjectionSurface->push_back(opState); 522 } else { 523 // standard in order draw 524 opState->skipInOrderDraw = false; 525 } 526 527 if (mDisplayList->getChildren().size() > 0) { 528 const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0; 529 bool haveAppliedPropertiesToProjection = false; 530 for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { 531 RenderNodeOp* childOp = mDisplayList->getChildren()[i]; 532 RenderNode* child = childOp->renderNode; 533 534 std::vector<RenderNodeOp*>* projectionChildren = nullptr; 535 const mat4* projectionTransform = nullptr; 536 if (isProjectionReceiver && !child->properties().getProjectBackwards()) { 537 // if receiving projections, collect projecting descendant 538 539 // Note that if a direct descendant is projecting backwards, we pass its 540 // grandparent projection collection, since it shouldn't project onto its 541 // parent, where it will already be drawing. 542 projectionChildren = &mProjectedNodes; 543 projectionTransform = &mat4::identity(); 544 } else { 545 if (!haveAppliedPropertiesToProjection) { 546 applyViewPropertyTransforms(localTransformFromProjectionSurface); 547 haveAppliedPropertiesToProjection = true; 548 } 549 projectionChildren = compositedChildrenOfProjectionSurface; 550 projectionTransform = &localTransformFromProjectionSurface; 551 } 552 child->computeOrderingImpl(childOp, projectionChildren, projectionTransform); 553 } 554 } 555 } 556 557 } /* namespace uirenderer */ 558 } /* namespace android */ 559