RenderNode.cpp revision b36af87f8275f4b982906f88193ec27600f2746a
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 <algorithm> 20#include <string> 21 22#include <SkCanvas.h> 23#include <algorithm> 24 25 26#include "DamageAccumulator.h" 27#include "Debug.h" 28#if HWUI_NEW_OPS 29#include "RecordedOp.h" 30#endif 31#include "DisplayListOp.h" 32#include "LayerRenderer.h" 33#include "OpenGLRenderer.h" 34#include "TreeInfo.h" 35#include "utils/MathUtils.h" 36#include "utils/TraceUtils.h" 37#include "renderthread/CanvasContext.h" 38 39#include "protos/hwui.pb.h" 40#include "protos/ProtoHelpers.h" 41 42namespace android { 43namespace uirenderer { 44 45void RenderNode::debugDumpLayers(const char* prefix) { 46 if (mLayer) { 47 ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)", 48 prefix, this, getName(), mLayer, mLayer->getFbo(), 49 mLayer->wasBuildLayered ? "true" : "false"); 50 } 51 if (mDisplayList) { 52 for (auto&& child : mDisplayList->getChildren()) { 53 child->renderNode->debugDumpLayers(prefix); 54 } 55 } 56} 57 58RenderNode::RenderNode() 59 : mDirtyPropertyFields(0) 60 , mNeedsDisplayListSync(false) 61 , mDisplayList(nullptr) 62 , mStagingDisplayList(nullptr) 63 , mAnimatorManager(*this) 64 , mLayer(nullptr) 65 , mParentCount(0) { 66} 67 68RenderNode::~RenderNode() { 69 deleteDisplayList(); 70 delete mStagingDisplayList; 71 if (mLayer) { 72 ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer); 73 mLayer->postDecStrong(); 74 mLayer = nullptr; 75 } 76} 77 78void RenderNode::setStagingDisplayList(DisplayList* displayList) { 79 mNeedsDisplayListSync = true; 80 delete mStagingDisplayList; 81 mStagingDisplayList = displayList; 82} 83 84/** 85 * This function is a simplified version of replay(), where we simply retrieve and log the 86 * display list. This function should remain in sync with the replay() function. 87 */ 88void RenderNode::output(uint32_t level) { 89 ALOGD("%*sStart display list (%p, %s%s%s%s%s%s)", (level - 1) * 2, "", this, 90 getName(), 91 (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""), 92 (properties().hasShadow() ? ", casting shadow" : ""), 93 (isRenderable() ? "" : ", empty"), 94 (properties().getProjectBackwards() ? ", projected" : ""), 95 (mLayer != nullptr ? ", on HW Layer" : "")); 96 ALOGD("%*s%s %d", level * 2, "", "Save", 97 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 98 99 properties().debugOutputProperties(level); 100 101 if (mDisplayList) { 102#if HWUI_NEW_OPS 103 LOG_ALWAYS_FATAL("op dumping unsupported"); 104#else 105 // TODO: consider printing the chunk boundaries here 106 for (auto&& op : mDisplayList->getOps()) { 107 op->output(level, DisplayListOp::kOpLogFlag_Recurse); 108 } 109#endif 110 } 111 112 ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName()); 113} 114 115void RenderNode::copyTo(proto::RenderNode *pnode) { 116 pnode->set_id(static_cast<uint64_t>( 117 reinterpret_cast<uintptr_t>(this))); 118 pnode->set_name(mName.string(), mName.length()); 119 120 proto::RenderProperties* pprops = pnode->mutable_properties(); 121 pprops->set_left(properties().getLeft()); 122 pprops->set_top(properties().getTop()); 123 pprops->set_right(properties().getRight()); 124 pprops->set_bottom(properties().getBottom()); 125 pprops->set_clip_flags(properties().getClippingFlags()); 126 pprops->set_alpha(properties().getAlpha()); 127 pprops->set_translation_x(properties().getTranslationX()); 128 pprops->set_translation_y(properties().getTranslationY()); 129 pprops->set_translation_z(properties().getTranslationZ()); 130 pprops->set_elevation(properties().getElevation()); 131 pprops->set_rotation(properties().getRotation()); 132 pprops->set_rotation_x(properties().getRotationX()); 133 pprops->set_rotation_y(properties().getRotationY()); 134 pprops->set_scale_x(properties().getScaleX()); 135 pprops->set_scale_y(properties().getScaleY()); 136 pprops->set_pivot_x(properties().getPivotX()); 137 pprops->set_pivot_y(properties().getPivotY()); 138 pprops->set_has_overlapping_rendering(properties().getHasOverlappingRendering()); 139 pprops->set_pivot_explicitly_set(properties().isPivotExplicitlySet()); 140 pprops->set_project_backwards(properties().getProjectBackwards()); 141 pprops->set_projection_receiver(properties().isProjectionReceiver()); 142 set(pprops->mutable_clip_bounds(), properties().getClipBounds()); 143 144 const Outline& outline = properties().getOutline(); 145 if (outline.getType() != Outline::Type::None) { 146 proto::Outline* poutline = pprops->mutable_outline(); 147 poutline->clear_path(); 148 if (outline.getType() == Outline::Type::Empty) { 149 poutline->set_type(proto::Outline_Type_Empty); 150 } else if (outline.getType() == Outline::Type::ConvexPath) { 151 poutline->set_type(proto::Outline_Type_ConvexPath); 152 if (const SkPath* path = outline.getPath()) { 153 set(poutline->mutable_path(), *path); 154 } 155 } else if (outline.getType() == Outline::Type::RoundRect) { 156 poutline->set_type(proto::Outline_Type_RoundRect); 157 } else { 158 ALOGW("Uknown outline type! %d", static_cast<int>(outline.getType())); 159 poutline->set_type(proto::Outline_Type_None); 160 } 161 poutline->set_should_clip(outline.getShouldClip()); 162 poutline->set_alpha(outline.getAlpha()); 163 poutline->set_radius(outline.getRadius()); 164 set(poutline->mutable_bounds(), outline.getBounds()); 165 } else { 166 pprops->clear_outline(); 167 } 168 169 const RevealClip& revealClip = properties().getRevealClip(); 170 if (revealClip.willClip()) { 171 proto::RevealClip* prevealClip = pprops->mutable_reveal_clip(); 172 prevealClip->set_x(revealClip.getX()); 173 prevealClip->set_y(revealClip.getY()); 174 prevealClip->set_radius(revealClip.getRadius()); 175 } else { 176 pprops->clear_reveal_clip(); 177 } 178 179 pnode->clear_children(); 180 if (mDisplayList) { 181 for (auto&& child : mDisplayList->getChildren()) { 182 child->renderNode->copyTo(pnode->add_children()); 183 } 184 } 185} 186 187int RenderNode::getDebugSize() { 188 int size = sizeof(RenderNode); 189 if (mStagingDisplayList) { 190 size += mStagingDisplayList->getUsedSize(); 191 } 192 if (mDisplayList && mDisplayList != mStagingDisplayList) { 193 size += mDisplayList->getUsedSize(); 194 } 195 return size; 196} 197 198void RenderNode::prepareTree(TreeInfo& info) { 199 ATRACE_CALL(); 200 LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing"); 201 202 // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer. 203 bool functorsNeedLayer = Properties::debugOverdraw; 204 205 prepareTreeImpl(info, functorsNeedLayer); 206} 207 208void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) { 209 mAnimatorManager.addAnimator(animator); 210} 211 212void RenderNode::damageSelf(TreeInfo& info) { 213 if (isRenderable()) { 214 if (properties().getClipDamageToBounds()) { 215 info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); 216 } else { 217 // Hope this is big enough? 218 // TODO: Get this from the display list ops or something 219 info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); 220 } 221 } 222} 223 224void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) { 225 LayerType layerType = properties().effectiveLayerType(); 226 if (CC_UNLIKELY(layerType == LayerType::RenderLayer)) { 227 // Damage applied so far needs to affect our parent, but does not require 228 // the layer to be updated. So we pop/push here to clear out the current 229 // damage and get a clean state for display list or children updates to 230 // affect, which will require the layer to be updated 231 info.damageAccumulator->popTransform(); 232 info.damageAccumulator->pushTransform(this); 233 if (dirtyMask & DISPLAY_LIST) { 234 damageSelf(info); 235 } 236 } 237} 238 239void RenderNode::pushLayerUpdate(TreeInfo& info) { 240 LayerType layerType = properties().effectiveLayerType(); 241 // If we are not a layer OR we cannot be rendered (eg, view was detached) 242 // we need to destroy any Layers we may have had previously 243 if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable())) { 244 if (CC_UNLIKELY(mLayer)) { 245 LayerRenderer::destroyLayer(mLayer); 246 mLayer = nullptr; 247 } 248 return; 249 } 250 251 bool transformUpdateNeeded = false; 252 if (!mLayer) { 253 mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight()); 254 applyLayerPropertiesToLayer(info); 255 damageSelf(info); 256 transformUpdateNeeded = true; 257 } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) { 258 if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { 259 LayerRenderer::destroyLayer(mLayer); 260 mLayer = nullptr; 261 } 262 damageSelf(info); 263 transformUpdateNeeded = true; 264 } 265 266 SkRect dirty; 267 info.damageAccumulator->peekAtDirty(&dirty); 268 269 if (!mLayer) { 270 Caches::getInstance().dumpMemoryUsage(); 271 if (info.errorHandler) { 272 std::string msg = "Unable to create layer for "; 273 msg += getName(); 274 info.errorHandler->onError(msg); 275 } 276 return; 277 } 278 279 if (transformUpdateNeeded) { 280 // update the transform in window of the layer to reset its origin wrt light source position 281 Matrix4 windowTransform; 282 info.damageAccumulator->computeCurrentTransform(&windowTransform); 283 mLayer->setWindowTransform(windowTransform); 284 } 285 286 if (dirty.intersect(0, 0, getWidth(), getHeight())) { 287 dirty.roundOut(&dirty); 288 mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom); 289 } 290 // This is not inside the above if because we may have called 291 // updateDeferred on a previous prepare pass that didn't have a renderer 292 if (info.renderer && mLayer->deferredUpdateScheduled) { 293 info.renderer->pushLayerUpdate(mLayer); 294 } 295 296 if (info.canvasContext) { 297 // There might be prefetched layers that need to be accounted for. 298 // That might be us, so tell CanvasContext that this layer is in the 299 // tree and should not be destroyed. 300 info.canvasContext->markLayerInUse(this); 301 } 302} 303 304/** 305 * Traverse down the the draw tree to prepare for a frame. 306 * 307 * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven 308 * 309 * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the 310 * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer. 311 */ 312void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) { 313 info.damageAccumulator->pushTransform(this); 314 315 if (info.mode == TreeInfo::MODE_FULL) { 316 pushStagingPropertiesChanges(info); 317 } 318 uint32_t animatorDirtyMask = 0; 319 if (CC_LIKELY(info.runAnimations)) { 320 animatorDirtyMask = mAnimatorManager.animate(info); 321 } 322 323 bool willHaveFunctor = false; 324 if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) { 325 willHaveFunctor = !mStagingDisplayList->getFunctors().empty(); 326 } else if (mDisplayList) { 327 willHaveFunctor = !mDisplayList->getFunctors().empty(); 328 } 329 bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence( 330 willHaveFunctor, functorsNeedLayer); 331 332 prepareLayer(info, animatorDirtyMask); 333 if (info.mode == TreeInfo::MODE_FULL) { 334 pushStagingDisplayListChanges(info); 335 } 336 prepareSubTree(info, childFunctorsNeedLayer, mDisplayList); 337 pushLayerUpdate(info); 338 339 info.damageAccumulator->popTransform(); 340} 341 342void RenderNode::syncProperties() { 343 mProperties = mStagingProperties; 344} 345 346void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { 347 // Push the animators first so that setupStartValueIfNecessary() is called 348 // before properties() is trampled by stagingProperties(), as they are 349 // required by some animators. 350 if (CC_LIKELY(info.runAnimations)) { 351 mAnimatorManager.pushStaging(); 352 } 353 if (mDirtyPropertyFields) { 354 mDirtyPropertyFields = 0; 355 damageSelf(info); 356 info.damageAccumulator->popTransform(); 357 syncProperties(); 358 applyLayerPropertiesToLayer(info); 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::applyLayerPropertiesToLayer(TreeInfo& info) { 370 if (CC_LIKELY(!mLayer)) return; 371 372 const LayerProperties& props = properties().layerProperties(); 373 mLayer->setAlpha(props.alpha(), props.xferMode()); 374 mLayer->setColorFilter(props.colorFilter()); 375 mLayer->setBlend(props.needsBlending()); 376} 377 378void RenderNode::syncDisplayList() { 379 // Make sure we inc first so that we don't fluctuate between 0 and 1, 380 // which would thrash the layer cache 381 if (mStagingDisplayList) { 382 for (auto&& child : mStagingDisplayList->getChildren()) { 383 child->renderNode->incParentRefCount(); 384 } 385 } 386 deleteDisplayList(); 387 mDisplayList = mStagingDisplayList; 388 mStagingDisplayList = nullptr; 389 if (mDisplayList) { 390 for (size_t i = 0; i < mDisplayList->getFunctors().size(); i++) { 391 (*mDisplayList->getFunctors()[i])(DrawGlInfo::kModeSync, nullptr); 392 } 393 } 394} 395 396void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { 397 if (mNeedsDisplayListSync) { 398 mNeedsDisplayListSync = false; 399 // Damage with the old display list first then the new one to catch any 400 // changes in isRenderable or, in the future, bounds 401 damageSelf(info); 402 syncDisplayList(); 403 damageSelf(info); 404 } 405} 406 407void RenderNode::deleteDisplayList() { 408 if (mDisplayList) { 409 for (auto&& child : mDisplayList->getChildren()) { 410 child->renderNode->decParentRefCount(); 411 } 412 } 413 delete mDisplayList; 414 mDisplayList = nullptr; 415} 416 417void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) { 418 if (subtree) { 419 TextureCache& cache = Caches::getInstance().textureCache; 420 info.out.hasFunctors |= subtree->getFunctors().size(); 421 for (auto&& bitmapResource : subtree->getBitmapResources()) { 422 info.prepareTextures = cache.prefetchAndMarkInUse(info.canvasContext, bitmapResource); 423 } 424 for (auto&& op : subtree->getChildren()) { 425 RenderNode* childNode = op->renderNode; 426#if HWUI_NEW_OPS 427 info.damageAccumulator->pushTransform(&op->localMatrix); 428 bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; 429#else 430 info.damageAccumulator->pushTransform(&op->mTransformFromParent); 431 bool childFunctorsNeedLayer = functorsNeedLayer 432 // Recorded with non-rect clip, or canvas-rotated by parent 433 || op->mRecordedWithPotentialStencilClip; 434#endif 435 childNode->prepareTreeImpl(info, childFunctorsNeedLayer); 436 info.damageAccumulator->popTransform(); 437 } 438 } 439} 440 441void RenderNode::destroyHardwareResources() { 442 if (mLayer) { 443 LayerRenderer::destroyLayer(mLayer); 444 mLayer = nullptr; 445 } 446 if (mDisplayList) { 447 for (auto&& child : mDisplayList->getChildren()) { 448 child->renderNode->destroyHardwareResources(); 449 } 450 if (mNeedsDisplayListSync) { 451 // Next prepare tree we are going to push a new display list, so we can 452 // drop our current one now 453 deleteDisplayList(); 454 } 455 } 456} 457 458void RenderNode::decParentRefCount() { 459 LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!"); 460 mParentCount--; 461 if (!mParentCount) { 462 // If a child of ours is being attached to our parent then this will incorrectly 463 // destroy its hardware resources. However, this situation is highly unlikely 464 // and the failure is "just" that the layer is re-created, so this should 465 // be safe enough 466 destroyHardwareResources(); 467 } 468} 469 470bool RenderNode::applyViewProperties(CanvasState& canvasState) const { 471 const Outline& outline = properties().getOutline(); 472 if (properties().getAlpha() <= 0 473 || (outline.getShouldClip() && outline.isEmpty()) 474 || properties().getScaleX() == 0 475 || properties().getScaleY() == 0) { 476 return false; // rejected 477 } 478 479 if (properties().getLeft() != 0 || properties().getTop() != 0) { 480 canvasState.translate(properties().getLeft(), properties().getTop()); 481 } 482 if (properties().getStaticMatrix()) { 483 canvasState.concatMatrix(*properties().getStaticMatrix()); 484 } else if (properties().getAnimationMatrix()) { 485 canvasState.concatMatrix(*properties().getAnimationMatrix()); 486 } 487 if (properties().hasTransformMatrix()) { 488 if (properties().isTransformTranslateOnly()) { 489 canvasState.translate(properties().getTranslationX(), properties().getTranslationY()); 490 } else { 491 canvasState.concatMatrix(*properties().getTransformMatrix()); 492 } 493 } 494 return !canvasState.quickRejectConservative( 495 0, 0, properties().getWidth(), properties().getHeight()); 496} 497 498/* 499 * For property operations, we pass a savecount of 0, since the operations aren't part of the 500 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in 501 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount()) 502 */ 503#define PROPERTY_SAVECOUNT 0 504 505template <class T> 506void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { 507#if DEBUG_DISPLAY_LIST 508 properties().debugOutputProperties(handler.level() + 1); 509#endif 510 if (properties().getLeft() != 0 || properties().getTop() != 0) { 511 renderer.translate(properties().getLeft(), properties().getTop()); 512 } 513 if (properties().getStaticMatrix()) { 514 renderer.concatMatrix(*properties().getStaticMatrix()); 515 } else if (properties().getAnimationMatrix()) { 516 renderer.concatMatrix(*properties().getAnimationMatrix()); 517 } 518 if (properties().hasTransformMatrix()) { 519 if (properties().isTransformTranslateOnly()) { 520 renderer.translate(properties().getTranslationX(), properties().getTranslationY()); 521 } else { 522 renderer.concatMatrix(*properties().getTransformMatrix()); 523 } 524 } 525 const bool isLayer = properties().effectiveLayerType() != LayerType::None; 526 int clipFlags = properties().getClippingFlags(); 527 if (properties().getAlpha() < 1) { 528 if (isLayer) { 529 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer 530 } 531 if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) { 532 // simply scale rendering content's alpha 533 renderer.scaleAlpha(properties().getAlpha()); 534 } else { 535 // savelayer needed to create an offscreen buffer 536 Rect layerBounds(0, 0, getWidth(), getHeight()); 537 if (clipFlags) { 538 properties().getClippingRectForFlags(clipFlags, &layerBounds); 539 clipFlags = 0; // all clipping done by savelayer 540 } 541 SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( 542 layerBounds.left, layerBounds.top, 543 layerBounds.right, layerBounds.bottom, 544 (int) (properties().getAlpha() * 255), 545 SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag); 546 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 547 } 548 549 if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) { 550 // pretend alpha always causes savelayer to warn about 551 // performance problem affecting old versions 552 ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), 553 static_cast<int>(getWidth()), 554 static_cast<int>(getHeight())); 555 } 556 } 557 if (clipFlags) { 558 Rect clipRect; 559 properties().getClippingRectForFlags(clipFlags, &clipRect); 560 ClipRectOp* op = new (handler.allocator()) ClipRectOp( 561 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, 562 SkRegion::kIntersect_Op); 563 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 564 } 565 566 // TODO: support nesting round rect clips 567 if (mProperties.getRevealClip().willClip()) { 568 Rect bounds; 569 mProperties.getRevealClip().getBounds(&bounds); 570 renderer.setClippingRoundRect(handler.allocator(), bounds, mProperties.getRevealClip().getRadius()); 571 } else if (mProperties.getOutline().willClip()) { 572 renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline())); 573 } 574} 575 576/** 577 * Apply property-based transformations to input matrix 578 * 579 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4 580 * matrix computation instead of the Skia 3x3 matrix + camera hackery. 581 */ 582void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const { 583 if (properties().getLeft() != 0 || properties().getTop() != 0) { 584 matrix.translate(properties().getLeft(), properties().getTop()); 585 } 586 if (properties().getStaticMatrix()) { 587 mat4 stat(*properties().getStaticMatrix()); 588 matrix.multiply(stat); 589 } else if (properties().getAnimationMatrix()) { 590 mat4 anim(*properties().getAnimationMatrix()); 591 matrix.multiply(anim); 592 } 593 594 bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ()); 595 if (properties().hasTransformMatrix() || applyTranslationZ) { 596 if (properties().isTransformTranslateOnly()) { 597 matrix.translate(properties().getTranslationX(), properties().getTranslationY(), 598 true3dTransform ? properties().getZ() : 0.0f); 599 } else { 600 if (!true3dTransform) { 601 matrix.multiply(*properties().getTransformMatrix()); 602 } else { 603 mat4 true3dMat; 604 true3dMat.loadTranslate( 605 properties().getPivotX() + properties().getTranslationX(), 606 properties().getPivotY() + properties().getTranslationY(), 607 properties().getZ()); 608 true3dMat.rotate(properties().getRotationX(), 1, 0, 0); 609 true3dMat.rotate(properties().getRotationY(), 0, 1, 0); 610 true3dMat.rotate(properties().getRotation(), 0, 0, 1); 611 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1); 612 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY()); 613 614 matrix.multiply(true3dMat); 615 } 616 } 617 } 618} 619 620/** 621 * Organizes the DisplayList hierarchy to prepare for background projection reordering. 622 * 623 * This should be called before a call to defer() or drawDisplayList() 624 * 625 * Each DisplayList that serves as a 3d root builds its list of composited children, 626 * which are flagged to not draw in the standard draw loop. 627 */ 628void RenderNode::computeOrdering() { 629#if !HWUI_NEW_OPS 630 ATRACE_CALL(); 631 mProjectedNodes.clear(); 632 633 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that 634 // transform properties are applied correctly to top level children 635 if (mDisplayList == nullptr) return; 636 for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { 637 DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i]; 638 childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); 639 } 640#endif 641} 642 643void RenderNode::computeOrderingImpl( 644 DrawRenderNodeOp* opState, 645 std::vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, 646 const mat4* transformFromProjectionSurface) { 647#if !HWUI_NEW_OPS 648 mProjectedNodes.clear(); 649 if (mDisplayList == nullptr || mDisplayList->isEmpty()) return; 650 651 // TODO: should avoid this calculation in most cases 652 // TODO: just calculate single matrix, down to all leaf composited elements 653 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); 654 localTransformFromProjectionSurface.multiply(opState->mTransformFromParent); 655 656 if (properties().getProjectBackwards()) { 657 // composited projectee, flag for out of order draw, save matrix, and store in proj surface 658 opState->mSkipInOrderDraw = true; 659 opState->mTransformFromCompositingAncestor = localTransformFromProjectionSurface; 660 compositedChildrenOfProjectionSurface->push_back(opState); 661 } else { 662 // standard in order draw 663 opState->mSkipInOrderDraw = false; 664 } 665 666 if (mDisplayList->getChildren().size() > 0) { 667 const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0; 668 bool haveAppliedPropertiesToProjection = false; 669 for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { 670 DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i]; 671 RenderNode* child = childOp->renderNode; 672 673 std::vector<DrawRenderNodeOp*>* projectionChildren = nullptr; 674 const mat4* projectionTransform = nullptr; 675 if (isProjectionReceiver && !child->properties().getProjectBackwards()) { 676 // if receiving projections, collect projecting descendant 677 678 // Note that if a direct descendant is projecting backwards, we pass its 679 // grandparent projection collection, since it shouldn't project onto its 680 // parent, where it will already be drawing. 681 projectionChildren = &mProjectedNodes; 682 projectionTransform = &mat4::identity(); 683 } else { 684 if (!haveAppliedPropertiesToProjection) { 685 applyViewPropertyTransforms(localTransformFromProjectionSurface); 686 haveAppliedPropertiesToProjection = true; 687 } 688 projectionChildren = compositedChildrenOfProjectionSurface; 689 projectionTransform = &localTransformFromProjectionSurface; 690 } 691 child->computeOrderingImpl(childOp, projectionChildren, projectionTransform); 692 } 693 } 694#endif 695} 696 697class DeferOperationHandler { 698public: 699 DeferOperationHandler(DeferStateStruct& deferStruct, int level) 700 : mDeferStruct(deferStruct), mLevel(level) {} 701 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 702 operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); 703 } 704 inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } 705 inline void startMark(const char* name) {} // do nothing 706 inline void endMark() {} 707 inline int level() { return mLevel; } 708 inline int replayFlags() { return mDeferStruct.mReplayFlags; } 709 inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); } 710 711private: 712 DeferStateStruct& mDeferStruct; 713 const int mLevel; 714}; 715 716void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { 717 DeferOperationHandler handler(deferStruct, level); 718 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); 719} 720 721class ReplayOperationHandler { 722public: 723 ReplayOperationHandler(ReplayStateStruct& replayStruct, int level) 724 : mReplayStruct(replayStruct), mLevel(level) {} 725 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 726#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 727 mReplayStruct.mRenderer.eventMark(operation->name()); 728#endif 729 operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); 730 } 731 inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } 732 inline void startMark(const char* name) { 733 mReplayStruct.mRenderer.startMark(name); 734 } 735 inline void endMark() { 736 mReplayStruct.mRenderer.endMark(); 737 } 738 inline int level() { return mLevel; } 739 inline int replayFlags() { return mReplayStruct.mReplayFlags; } 740 inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); } 741 742private: 743 ReplayStateStruct& mReplayStruct; 744 const int mLevel; 745}; 746 747void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) { 748 ReplayOperationHandler handler(replayStruct, level); 749 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); 750} 751 752void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk, 753 std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) { 754#if !HWUI_NEW_OPS 755 if (chunk.beginChildIndex == chunk.endChildIndex) return; 756 757 for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) { 758 DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i]; 759 RenderNode* child = childOp->renderNode; 760 float childZ = child->properties().getZ(); 761 762 if (!MathUtils::isZero(childZ) && chunk.reorderChildren) { 763 zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp)); 764 childOp->mSkipInOrderDraw = true; 765 } else if (!child->properties().getProjectBackwards()) { 766 // regular, in order drawing DisplayList 767 childOp->mSkipInOrderDraw = false; 768 } 769 } 770 771 // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order) 772 std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end()); 773#endif 774} 775 776template <class T> 777void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) { 778 if (properties().getAlpha() <= 0.0f 779 || properties().getOutline().getAlpha() <= 0.0f 780 || !properties().getOutline().getPath() 781 || properties().getScaleX() == 0 782 || properties().getScaleY() == 0) { 783 // no shadow to draw 784 return; 785 } 786 787 mat4 shadowMatrixXY(transformFromParent); 788 applyViewPropertyTransforms(shadowMatrixXY); 789 790 // Z matrix needs actual 3d transformation, so mapped z values will be correct 791 mat4 shadowMatrixZ(transformFromParent); 792 applyViewPropertyTransforms(shadowMatrixZ, true); 793 794 const SkPath* casterOutlinePath = properties().getOutline().getPath(); 795 const SkPath* revealClipPath = properties().getRevealClip().getPath(); 796 if (revealClipPath && revealClipPath->isEmpty()) return; 797 798 float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha(); 799 800 801 // holds temporary SkPath to store the result of intersections 802 SkPath* frameAllocatedPath = nullptr; 803 const SkPath* outlinePath = casterOutlinePath; 804 805 // intersect the outline with the reveal clip, if present 806 if (revealClipPath) { 807 frameAllocatedPath = handler.allocPathForFrame(); 808 809 Op(*outlinePath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath); 810 outlinePath = frameAllocatedPath; 811 } 812 813 // intersect the outline with the clipBounds, if present 814 if (properties().getClippingFlags() & CLIP_TO_CLIP_BOUNDS) { 815 if (!frameAllocatedPath) { 816 frameAllocatedPath = handler.allocPathForFrame(); 817 } 818 819 Rect clipBounds; 820 properties().getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); 821 SkPath clipBoundsPath; 822 clipBoundsPath.addRect(clipBounds.left, clipBounds.top, 823 clipBounds.right, clipBounds.bottom); 824 825 Op(*outlinePath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath); 826 outlinePath = frameAllocatedPath; 827 } 828 829 DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp( 830 shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath); 831 handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 832} 833 834#define SHADOW_DELTA 0.1f 835 836template <class T> 837void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, 838 const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, 839 OpenGLRenderer& renderer, T& handler) { 840 const int size = zTranslatedNodes.size(); 841 if (size == 0 842 || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f) 843 || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { 844 // no 3d children to draw 845 return; 846 } 847 848 // Apply the base transform of the parent of the 3d children. This isolates 849 // 3d children of the current chunk from transformations made in previous chunks. 850 int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 851 renderer.setGlobalMatrix(initialTransform); 852 853 /** 854 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters 855 * with very similar Z heights to draw together. 856 * 857 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are 858 * underneath both, and neither's shadow is drawn on top of the other. 859 */ 860 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); 861 size_t drawIndex, shadowIndex, endIndex; 862 if (mode == ChildrenSelectMode::NegativeZChildren) { 863 drawIndex = 0; 864 endIndex = nonNegativeIndex; 865 shadowIndex = endIndex; // draw no shadows 866 } else { 867 drawIndex = nonNegativeIndex; 868 endIndex = size; 869 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child 870 } 871 872 DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "", 873 endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive"); 874 875 float lastCasterZ = 0.0f; 876 while (shadowIndex < endIndex || drawIndex < endIndex) { 877 if (shadowIndex < endIndex) { 878 DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value; 879 RenderNode* caster = casterOp->renderNode; 880 const float casterZ = zTranslatedNodes[shadowIndex].key; 881 // attempt to render the shadow if the caster about to be drawn is its caster, 882 // OR if its caster's Z value is similar to the previous potential caster 883 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { 884 caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler); 885 886 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow 887 shadowIndex++; 888 continue; 889 } 890 } 891 892 // only the actual child DL draw needs to be in save/restore, 893 // since it modifies the renderer's matrix 894 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 895 896 DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; 897 898 renderer.concatMatrix(childOp->mTransformFromParent); 899 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 900 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); 901 childOp->mSkipInOrderDraw = true; 902 903 renderer.restoreToCount(restoreTo); 904 drawIndex++; 905 } 906 renderer.restoreToCount(rootRestoreTo); 907} 908 909template <class T> 910void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) { 911 DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size()); 912 const SkPath* projectionReceiverOutline = properties().getOutline().getPath(); 913 int restoreTo = renderer.getSaveCount(); 914 915 LinearAllocator& alloc = handler.allocator(); 916 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 917 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 918 919 // Transform renderer to match background we're projecting onto 920 // (by offsetting canvas by translationX/Y of background rendernode, since only those are set) 921 const DisplayListOp* op = 922#if HWUI_NEW_OPS 923 nullptr; 924 LOG_ALWAYS_FATAL("unsupported"); 925#else 926 (mDisplayList->getOps()[mDisplayList->projectionReceiveIndex]); 927#endif 928 const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op); 929 const RenderProperties& backgroundProps = backgroundOp->renderNode->properties(); 930 renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY()); 931 932 // If the projection reciever has an outline, we mask projected content to it 933 // (which we know, apriori, are all tessellated paths) 934 renderer.setProjectionPathMask(alloc, projectionReceiverOutline); 935 936 // draw projected nodes 937 for (size_t i = 0; i < mProjectedNodes.size(); i++) { 938 DrawRenderNodeOp* childOp = mProjectedNodes[i]; 939 940 // matrix save, concat, and restore can be done safely without allocating operations 941 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 942 renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); 943 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 944 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); 945 childOp->mSkipInOrderDraw = true; 946 renderer.restoreToCount(restoreTo); 947 } 948 949 handler(new (alloc) RestoreToCountOp(restoreTo), 950 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 951} 952 953/** 954 * This function serves both defer and replay modes, and will organize the displayList's component 955 * operations for a single frame: 956 * 957 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of 958 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom 959 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the 960 * defer vs replay logic, per operation 961 */ 962template <class T> 963void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { 964 if (mDisplayList->isEmpty()) { 965 DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "", 966 this, getName()); 967 return; 968 } 969 970 const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get())); 971 // If we are updating the contents of mLayer, we don't want to apply any of 972 // the RenderNode's properties to this issueOperations pass. Those will all 973 // be applied when the layer is drawn, aka when this is true. 974 const bool useViewProperties = (!mLayer || drawLayer); 975 if (useViewProperties) { 976 const Outline& outline = properties().getOutline(); 977 if (properties().getAlpha() <= 0 978 || (outline.getShouldClip() && outline.isEmpty()) 979 || properties().getScaleX() == 0 980 || properties().getScaleY() == 0) { 981 DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "", 982 this, getName()); 983 return; 984 } 985 } 986 987 handler.startMark(getName()); 988 989#if DEBUG_DISPLAY_LIST 990 const Rect& clipRect = renderer.getLocalClipBounds(); 991 DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f", 992 handler.level() * 2, "", this, getName(), 993 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); 994#endif 995 996 LinearAllocator& alloc = handler.allocator(); 997 int restoreTo = renderer.getSaveCount(); 998 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 999 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 1000 1001 DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "", 1002 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); 1003 1004 if (useViewProperties) { 1005 setViewProperties<T>(renderer, handler); 1006 } 1007 1008#if HWUI_NEW_OPS 1009 LOG_ALWAYS_FATAL("legacy op traversal not supported"); 1010#else 1011 bool quickRejected = properties().getClipToBounds() 1012 && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); 1013 if (!quickRejected) { 1014 Matrix4 initialTransform(*(renderer.currentTransform())); 1015 renderer.setBaseTransform(initialTransform); 1016 1017 if (drawLayer) { 1018 handler(new (alloc) DrawLayerOp(mLayer), 1019 renderer.getSaveCount() - 1, properties().getClipToBounds()); 1020 } else { 1021 const int saveCountOffset = renderer.getSaveCount() - 1; 1022 const int projectionReceiveIndex = mDisplayList->projectionReceiveIndex; 1023 for (size_t chunkIndex = 0; chunkIndex < mDisplayList->getChunks().size(); chunkIndex++) { 1024 const DisplayList::Chunk& chunk = mDisplayList->getChunks()[chunkIndex]; 1025 1026 std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes; 1027 buildZSortedChildList(chunk, zTranslatedNodes); 1028 1029 issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren, 1030 initialTransform, zTranslatedNodes, renderer, handler); 1031 1032 for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { 1033 DisplayListOp *op = mDisplayList->getOps()[opIndex]; 1034#if DEBUG_DISPLAY_LIST 1035 op->output(handler.level() + 1); 1036#endif 1037 handler(op, saveCountOffset, properties().getClipToBounds()); 1038 1039 if (CC_UNLIKELY(!mProjectedNodes.empty() && projectionReceiveIndex >= 0 && 1040 opIndex == static_cast<size_t>(projectionReceiveIndex))) { 1041 issueOperationsOfProjectedChildren(renderer, handler); 1042 } 1043 } 1044 1045 issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren, 1046 initialTransform, zTranslatedNodes, renderer, handler); 1047 } 1048 } 1049 } 1050#endif 1051 1052 DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo); 1053 handler(new (alloc) RestoreToCountOp(restoreTo), 1054 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 1055 1056 DISPLAY_LIST_LOGD("%*sDone (%p, %s)", handler.level() * 2, "", this, getName()); 1057 handler.endMark(); 1058} 1059 1060} /* namespace uirenderer */ 1061} /* namespace android */ 1062