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