RenderNode.cpp revision 10ed692118552a01ff97b095295852b631e51bee
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 (mDisplayListData) { 52 for (auto&& child : mDisplayListData->children()) { 53 child->renderNode->debugDumpLayers(prefix); 54 } 55 } 56} 57 58RenderNode::RenderNode() 59 : mDirtyPropertyFields(0) 60 , mNeedsDisplayListDataSync(false) 61 , mDisplayListData(nullptr) 62 , mStagingDisplayListData(nullptr) 63 , mAnimatorManager(*this) 64 , mLayer(nullptr) 65 , mParentCount(0) { 66} 67 68RenderNode::~RenderNode() { 69 deleteDisplayListData(); 70 delete mStagingDisplayListData; 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(DisplayListData* data) { 79 mNeedsDisplayListDataSync = true; 80 delete mStagingDisplayListData; 81 mStagingDisplayListData = data; 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 (mDisplayListData) { 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 : mDisplayListData->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 (mDisplayListData) { 181 for (auto&& child : mDisplayListData->children()) { 182 child->renderNode->copyTo(pnode->add_children()); 183 } 184 } 185} 186 187int RenderNode::getDebugSize() { 188 int size = sizeof(RenderNode); 189 if (mStagingDisplayListData) { 190 size += mStagingDisplayListData->getUsedSize(); 191 } 192 if (mDisplayListData && mDisplayListData != mStagingDisplayListData) { 193 size += mDisplayListData->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 && mStagingDisplayListData) { 325 willHaveFunctor = !mStagingDisplayListData->functors.isEmpty(); 326 } else if (mDisplayListData) { 327 willHaveFunctor = !mDisplayListData->functors.isEmpty(); 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, mDisplayListData); 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 (mStagingDisplayListData) { 382 for (auto&& child : mStagingDisplayListData->children()) { 383 child->renderNode->incParentRefCount(); 384 } 385 } 386 deleteDisplayListData(); 387 mDisplayListData = mStagingDisplayListData; 388 mStagingDisplayListData = nullptr; 389 if (mDisplayListData) { 390 for (size_t i = 0; i < mDisplayListData->functors.size(); i++) { 391 (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, nullptr); 392 } 393 } 394} 395 396void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { 397 if (mNeedsDisplayListDataSync) { 398 mNeedsDisplayListDataSync = 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::deleteDisplayListData() { 408 if (mDisplayListData) { 409 for (auto&& child : mDisplayListData->children()) { 410 child->renderNode->decParentRefCount(); 411 } 412 } 413 delete mDisplayListData; 414 mDisplayListData = nullptr; 415} 416 417void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree) { 418 if (subtree) { 419 TextureCache& cache = Caches::getInstance().textureCache; 420 info.out.hasFunctors |= subtree->functors.size(); 421 for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) { 422 info.prepareTextures = cache.prefetchAndMarkInUse( 423 info.canvasContext, subtree->bitmapResources[i]); 424 } 425 for (auto&& op : subtree->children()) { 426 RenderNode* childNode = op->renderNode; 427#if HWUI_NEW_OPS 428 info.damageAccumulator->pushTransform(&op->localMatrix); 429 bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; 430#else 431 info.damageAccumulator->pushTransform(&op->mTransformFromParent); 432 bool childFunctorsNeedLayer = functorsNeedLayer 433 // Recorded with non-rect clip, or canvas-rotated by parent 434 || op->mRecordedWithPotentialStencilClip; 435#endif 436 childNode->prepareTreeImpl(info, childFunctorsNeedLayer); 437 info.damageAccumulator->popTransform(); 438 } 439 } 440} 441 442void RenderNode::destroyHardwareResources() { 443 if (mLayer) { 444 LayerRenderer::destroyLayer(mLayer); 445 mLayer = nullptr; 446 } 447 if (mDisplayListData) { 448 for (auto&& child : mDisplayListData->children()) { 449 child->renderNode->destroyHardwareResources(); 450 } 451 if (mNeedsDisplayListDataSync) { 452 // Next prepare tree we are going to push a new display list, so we can 453 // drop our current one now 454 deleteDisplayListData(); 455 } 456 } 457} 458 459void RenderNode::decParentRefCount() { 460 LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!"); 461 mParentCount--; 462 if (!mParentCount) { 463 // If a child of ours is being attached to our parent then this will incorrectly 464 // destroy its hardware resources. However, this situation is highly unlikely 465 // and the failure is "just" that the layer is re-created, so this should 466 // be safe enough 467 destroyHardwareResources(); 468 } 469} 470 471bool RenderNode::applyViewProperties(CanvasState& canvasState) const { 472 const Outline& outline = properties().getOutline(); 473 if (properties().getAlpha() <= 0 474 || (outline.getShouldClip() && outline.isEmpty()) 475 || properties().getScaleX() == 0 476 || properties().getScaleY() == 0) { 477 return false; // rejected 478 } 479 480 if (properties().getLeft() != 0 || properties().getTop() != 0) { 481 canvasState.translate(properties().getLeft(), properties().getTop()); 482 } 483 if (properties().getStaticMatrix()) { 484 canvasState.concatMatrix(*properties().getStaticMatrix()); 485 } else if (properties().getAnimationMatrix()) { 486 canvasState.concatMatrix(*properties().getAnimationMatrix()); 487 } 488 if (properties().hasTransformMatrix()) { 489 if (properties().isTransformTranslateOnly()) { 490 canvasState.translate(properties().getTranslationX(), properties().getTranslationY()); 491 } else { 492 canvasState.concatMatrix(*properties().getTransformMatrix()); 493 } 494 } 495 return !canvasState.quickRejectConservative( 496 0, 0, properties().getWidth(), properties().getHeight()); 497} 498 499/* 500 * For property operations, we pass a savecount of 0, since the operations aren't part of the 501 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in 502 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount()) 503 */ 504#define PROPERTY_SAVECOUNT 0 505 506template <class T> 507void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { 508#if DEBUG_DISPLAY_LIST 509 properties().debugOutputProperties(handler.level() + 1); 510#endif 511 if (properties().getLeft() != 0 || properties().getTop() != 0) { 512 renderer.translate(properties().getLeft(), properties().getTop()); 513 } 514 if (properties().getStaticMatrix()) { 515 renderer.concatMatrix(*properties().getStaticMatrix()); 516 } else if (properties().getAnimationMatrix()) { 517 renderer.concatMatrix(*properties().getAnimationMatrix()); 518 } 519 if (properties().hasTransformMatrix()) { 520 if (properties().isTransformTranslateOnly()) { 521 renderer.translate(properties().getTranslationX(), properties().getTranslationY()); 522 } else { 523 renderer.concatMatrix(*properties().getTransformMatrix()); 524 } 525 } 526 const bool isLayer = properties().effectiveLayerType() != LayerType::None; 527 int clipFlags = properties().getClippingFlags(); 528 if (properties().getAlpha() < 1) { 529 if (isLayer) { 530 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer 531 } 532 if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) { 533 // simply scale rendering content's alpha 534 renderer.scaleAlpha(properties().getAlpha()); 535 } else { 536 // savelayer needed to create an offscreen buffer 537 Rect layerBounds(0, 0, getWidth(), getHeight()); 538 if (clipFlags) { 539 properties().getClippingRectForFlags(clipFlags, &layerBounds); 540 clipFlags = 0; // all clipping done by savelayer 541 } 542 SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( 543 layerBounds.left, layerBounds.top, 544 layerBounds.right, layerBounds.bottom, 545 (int) (properties().getAlpha() * 255), 546 SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag); 547 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 548 } 549 550 if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) { 551 // pretend alpha always causes savelayer to warn about 552 // performance problem affecting old versions 553 ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), 554 static_cast<int>(getWidth()), 555 static_cast<int>(getHeight())); 556 } 557 } 558 if (clipFlags) { 559 Rect clipRect; 560 properties().getClippingRectForFlags(clipFlags, &clipRect); 561 ClipRectOp* op = new (handler.allocator()) ClipRectOp( 562 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, 563 SkRegion::kIntersect_Op); 564 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 565 } 566 567 // TODO: support nesting round rect clips 568 if (mProperties.getRevealClip().willClip()) { 569 Rect bounds; 570 mProperties.getRevealClip().getBounds(&bounds); 571 renderer.setClippingRoundRect(handler.allocator(), bounds, mProperties.getRevealClip().getRadius()); 572 } else if (mProperties.getOutline().willClip()) { 573 renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline())); 574 } 575} 576 577/** 578 * Apply property-based transformations to input matrix 579 * 580 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4 581 * matrix computation instead of the Skia 3x3 matrix + camera hackery. 582 */ 583void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const { 584 if (properties().getLeft() != 0 || properties().getTop() != 0) { 585 matrix.translate(properties().getLeft(), properties().getTop()); 586 } 587 if (properties().getStaticMatrix()) { 588 mat4 stat(*properties().getStaticMatrix()); 589 matrix.multiply(stat); 590 } else if (properties().getAnimationMatrix()) { 591 mat4 anim(*properties().getAnimationMatrix()); 592 matrix.multiply(anim); 593 } 594 595 bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ()); 596 if (properties().hasTransformMatrix() || applyTranslationZ) { 597 if (properties().isTransformTranslateOnly()) { 598 matrix.translate(properties().getTranslationX(), properties().getTranslationY(), 599 true3dTransform ? properties().getZ() : 0.0f); 600 } else { 601 if (!true3dTransform) { 602 matrix.multiply(*properties().getTransformMatrix()); 603 } else { 604 mat4 true3dMat; 605 true3dMat.loadTranslate( 606 properties().getPivotX() + properties().getTranslationX(), 607 properties().getPivotY() + properties().getTranslationY(), 608 properties().getZ()); 609 true3dMat.rotate(properties().getRotationX(), 1, 0, 0); 610 true3dMat.rotate(properties().getRotationY(), 0, 1, 0); 611 true3dMat.rotate(properties().getRotation(), 0, 0, 1); 612 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1); 613 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY()); 614 615 matrix.multiply(true3dMat); 616 } 617 } 618 } 619} 620 621/** 622 * Organizes the DisplayList hierarchy to prepare for background projection reordering. 623 * 624 * This should be called before a call to defer() or drawDisplayList() 625 * 626 * Each DisplayList that serves as a 3d root builds its list of composited children, 627 * which are flagged to not draw in the standard draw loop. 628 */ 629void RenderNode::computeOrdering() { 630#if !HWUI_NEW_OPS 631 ATRACE_CALL(); 632 mProjectedNodes.clear(); 633 634 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that 635 // transform properties are applied correctly to top level children 636 if (mDisplayListData == nullptr) return; 637 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { 638 DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; 639 childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); 640 } 641#endif 642} 643 644void RenderNode::computeOrderingImpl( 645 DrawRenderNodeOp* opState, 646 std::vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, 647 const mat4* transformFromProjectionSurface) { 648#if !HWUI_NEW_OPS 649 mProjectedNodes.clear(); 650 if (mDisplayListData == nullptr || mDisplayListData->isEmpty()) return; 651 652 // TODO: should avoid this calculation in most cases 653 // TODO: just calculate single matrix, down to all leaf composited elements 654 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); 655 localTransformFromProjectionSurface.multiply(opState->mTransformFromParent); 656 657 if (properties().getProjectBackwards()) { 658 // composited projectee, flag for out of order draw, save matrix, and store in proj surface 659 opState->mSkipInOrderDraw = true; 660 opState->mTransformFromCompositingAncestor = localTransformFromProjectionSurface; 661 compositedChildrenOfProjectionSurface->push_back(opState); 662 } else { 663 // standard in order draw 664 opState->mSkipInOrderDraw = false; 665 } 666 667 if (mDisplayListData->children().size() > 0) { 668 const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0; 669 bool haveAppliedPropertiesToProjection = false; 670 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { 671 DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; 672 RenderNode* child = childOp->renderNode; 673 674 std::vector<DrawRenderNodeOp*>* projectionChildren = nullptr; 675 const mat4* projectionTransform = nullptr; 676 if (isProjectionReceiver && !child->properties().getProjectBackwards()) { 677 // if receiving projections, collect projecting descendant 678 679 // Note that if a direct descendant is projecting backwards, we pass its 680 // grandparent projection collection, since it shouldn't project onto its 681 // parent, where it will already be drawing. 682 projectionChildren = &mProjectedNodes; 683 projectionTransform = &mat4::identity(); 684 } else { 685 if (!haveAppliedPropertiesToProjection) { 686 applyViewPropertyTransforms(localTransformFromProjectionSurface); 687 haveAppliedPropertiesToProjection = true; 688 } 689 projectionChildren = compositedChildrenOfProjectionSurface; 690 projectionTransform = &localTransformFromProjectionSurface; 691 } 692 child->computeOrderingImpl(childOp, projectionChildren, projectionTransform); 693 } 694 } 695#endif 696} 697 698class DeferOperationHandler { 699public: 700 DeferOperationHandler(DeferStateStruct& deferStruct, int level) 701 : mDeferStruct(deferStruct), mLevel(level) {} 702 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 703 operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); 704 } 705 inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } 706 inline void startMark(const char* name) {} // do nothing 707 inline void endMark() {} 708 inline int level() { return mLevel; } 709 inline int replayFlags() { return mDeferStruct.mReplayFlags; } 710 inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); } 711 712private: 713 DeferStateStruct& mDeferStruct; 714 const int mLevel; 715}; 716 717void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { 718 DeferOperationHandler handler(deferStruct, level); 719 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); 720} 721 722class ReplayOperationHandler { 723public: 724 ReplayOperationHandler(ReplayStateStruct& replayStruct, int level) 725 : mReplayStruct(replayStruct), mLevel(level) {} 726 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 727#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 728 mReplayStruct.mRenderer.eventMark(operation->name()); 729#endif 730 operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); 731 } 732 inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } 733 inline void startMark(const char* name) { 734 mReplayStruct.mRenderer.startMark(name); 735 } 736 inline void endMark() { 737 mReplayStruct.mRenderer.endMark(); 738 } 739 inline int level() { return mLevel; } 740 inline int replayFlags() { return mReplayStruct.mReplayFlags; } 741 inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); } 742 743private: 744 ReplayStateStruct& mReplayStruct; 745 const int mLevel; 746}; 747 748void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) { 749 ReplayOperationHandler handler(replayStruct, level); 750 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); 751} 752 753void RenderNode::buildZSortedChildList(const DisplayListData::Chunk& chunk, 754 std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) { 755#if !HWUI_NEW_OPS 756 if (chunk.beginChildIndex == chunk.endChildIndex) return; 757 758 for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) { 759 DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; 760 RenderNode* child = childOp->renderNode; 761 float childZ = child->properties().getZ(); 762 763 if (!MathUtils::isZero(childZ) && chunk.reorderChildren) { 764 zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp)); 765 childOp->mSkipInOrderDraw = true; 766 } else if (!child->properties().getProjectBackwards()) { 767 // regular, in order drawing DisplayList 768 childOp->mSkipInOrderDraw = false; 769 } 770 } 771 772 // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order) 773 std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end()); 774#endif 775} 776 777template <class T> 778void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) { 779 if (properties().getAlpha() <= 0.0f 780 || properties().getOutline().getAlpha() <= 0.0f 781 || !properties().getOutline().getPath() 782 || properties().getScaleX() == 0 783 || properties().getScaleY() == 0) { 784 // no shadow to draw 785 return; 786 } 787 788 mat4 shadowMatrixXY(transformFromParent); 789 applyViewPropertyTransforms(shadowMatrixXY); 790 791 // Z matrix needs actual 3d transformation, so mapped z values will be correct 792 mat4 shadowMatrixZ(transformFromParent); 793 applyViewPropertyTransforms(shadowMatrixZ, true); 794 795 const SkPath* casterOutlinePath = properties().getOutline().getPath(); 796 const SkPath* revealClipPath = properties().getRevealClip().getPath(); 797 if (revealClipPath && revealClipPath->isEmpty()) return; 798 799 float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha(); 800 801 802 // holds temporary SkPath to store the result of intersections 803 SkPath* frameAllocatedPath = nullptr; 804 const SkPath* outlinePath = casterOutlinePath; 805 806 // intersect the outline with the reveal clip, if present 807 if (revealClipPath) { 808 frameAllocatedPath = handler.allocPathForFrame(); 809 810 Op(*outlinePath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath); 811 outlinePath = frameAllocatedPath; 812 } 813 814 // intersect the outline with the clipBounds, if present 815 if (properties().getClippingFlags() & CLIP_TO_CLIP_BOUNDS) { 816 if (!frameAllocatedPath) { 817 frameAllocatedPath = handler.allocPathForFrame(); 818 } 819 820 Rect clipBounds; 821 properties().getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); 822 SkPath clipBoundsPath; 823 clipBoundsPath.addRect(clipBounds.left, clipBounds.top, 824 clipBounds.right, clipBounds.bottom); 825 826 Op(*outlinePath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath); 827 outlinePath = frameAllocatedPath; 828 } 829 830 DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp( 831 shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath); 832 handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 833} 834 835#define SHADOW_DELTA 0.1f 836 837template <class T> 838void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, 839 const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, 840 OpenGLRenderer& renderer, T& handler) { 841 const int size = zTranslatedNodes.size(); 842 if (size == 0 843 || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f) 844 || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { 845 // no 3d children to draw 846 return; 847 } 848 849 // Apply the base transform of the parent of the 3d children. This isolates 850 // 3d children of the current chunk from transformations made in previous chunks. 851 int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 852 renderer.setGlobalMatrix(initialTransform); 853 854 /** 855 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters 856 * with very similar Z heights to draw together. 857 * 858 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are 859 * underneath both, and neither's shadow is drawn on top of the other. 860 */ 861 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); 862 size_t drawIndex, shadowIndex, endIndex; 863 if (mode == ChildrenSelectMode::NegativeZChildren) { 864 drawIndex = 0; 865 endIndex = nonNegativeIndex; 866 shadowIndex = endIndex; // draw no shadows 867 } else { 868 drawIndex = nonNegativeIndex; 869 endIndex = size; 870 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child 871 } 872 873 DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "", 874 endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive"); 875 876 float lastCasterZ = 0.0f; 877 while (shadowIndex < endIndex || drawIndex < endIndex) { 878 if (shadowIndex < endIndex) { 879 DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value; 880 RenderNode* caster = casterOp->renderNode; 881 const float casterZ = zTranslatedNodes[shadowIndex].key; 882 // attempt to render the shadow if the caster about to be drawn is its caster, 883 // OR if its caster's Z value is similar to the previous potential caster 884 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { 885 caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler); 886 887 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow 888 shadowIndex++; 889 continue; 890 } 891 } 892 893 // only the actual child DL draw needs to be in save/restore, 894 // since it modifies the renderer's matrix 895 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 896 897 DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; 898 899 renderer.concatMatrix(childOp->mTransformFromParent); 900 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 901 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); 902 childOp->mSkipInOrderDraw = true; 903 904 renderer.restoreToCount(restoreTo); 905 drawIndex++; 906 } 907 renderer.restoreToCount(rootRestoreTo); 908} 909 910template <class T> 911void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) { 912 DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size()); 913 const SkPath* projectionReceiverOutline = properties().getOutline().getPath(); 914 int restoreTo = renderer.getSaveCount(); 915 916 LinearAllocator& alloc = handler.allocator(); 917 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 918 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 919 920 // Transform renderer to match background we're projecting onto 921 // (by offsetting canvas by translationX/Y of background rendernode, since only those are set) 922 const DisplayListOp* op = 923#if HWUI_NEW_OPS 924 nullptr; 925 LOG_ALWAYS_FATAL("unsupported"); 926#else 927 (mDisplayListData->getOps()[mDisplayListData->projectionReceiveIndex]); 928#endif 929 const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op); 930 const RenderProperties& backgroundProps = backgroundOp->renderNode->properties(); 931 renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY()); 932 933 // If the projection reciever has an outline, we mask projected content to it 934 // (which we know, apriori, are all tessellated paths) 935 renderer.setProjectionPathMask(alloc, projectionReceiverOutline); 936 937 // draw projected nodes 938 for (size_t i = 0; i < mProjectedNodes.size(); i++) { 939 DrawRenderNodeOp* childOp = mProjectedNodes[i]; 940 941 // matrix save, concat, and restore can be done safely without allocating operations 942 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 943 renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); 944 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 945 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); 946 childOp->mSkipInOrderDraw = true; 947 renderer.restoreToCount(restoreTo); 948 } 949 950 handler(new (alloc) RestoreToCountOp(restoreTo), 951 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 952} 953 954/** 955 * This function serves both defer and replay modes, and will organize the displayList's component 956 * operations for a single frame: 957 * 958 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of 959 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom 960 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the 961 * defer vs replay logic, per operation 962 */ 963template <class T> 964void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { 965 if (mDisplayListData->isEmpty()) { 966 DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "", 967 this, getName()); 968 return; 969 } 970 971 const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get())); 972 // If we are updating the contents of mLayer, we don't want to apply any of 973 // the RenderNode's properties to this issueOperations pass. Those will all 974 // be applied when the layer is drawn, aka when this is true. 975 const bool useViewProperties = (!mLayer || drawLayer); 976 if (useViewProperties) { 977 const Outline& outline = properties().getOutline(); 978 if (properties().getAlpha() <= 0 979 || (outline.getShouldClip() && outline.isEmpty()) 980 || properties().getScaleX() == 0 981 || properties().getScaleY() == 0) { 982 DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "", 983 this, getName()); 984 return; 985 } 986 } 987 988 handler.startMark(getName()); 989 990#if DEBUG_DISPLAY_LIST 991 const Rect& clipRect = renderer.getLocalClipBounds(); 992 DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f", 993 handler.level() * 2, "", this, getName(), 994 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); 995#endif 996 997 LinearAllocator& alloc = handler.allocator(); 998 int restoreTo = renderer.getSaveCount(); 999 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 1000 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 1001 1002 DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "", 1003 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); 1004 1005 if (useViewProperties) { 1006 setViewProperties<T>(renderer, handler); 1007 } 1008 1009#if HWUI_NEW_OPS 1010 LOG_ALWAYS_FATAL("legacy op traversal not supported"); 1011#else 1012 bool quickRejected = properties().getClipToBounds() 1013 && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); 1014 if (!quickRejected) { 1015 Matrix4 initialTransform(*(renderer.currentTransform())); 1016 renderer.setBaseTransform(initialTransform); 1017 1018 if (drawLayer) { 1019 handler(new (alloc) DrawLayerOp(mLayer), 1020 renderer.getSaveCount() - 1, properties().getClipToBounds()); 1021 } else { 1022 const int saveCountOffset = renderer.getSaveCount() - 1; 1023 const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; 1024 for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) { 1025 const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex]; 1026 1027 std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes; 1028 buildZSortedChildList(chunk, zTranslatedNodes); 1029 1030 issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren, 1031 initialTransform, zTranslatedNodes, renderer, handler); 1032 1033 for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { 1034 DisplayListOp *op = mDisplayListData->getOps()[opIndex]; 1035#if DEBUG_DISPLAY_LIST 1036 op->output(handler.level() + 1); 1037#endif 1038 handler(op, saveCountOffset, properties().getClipToBounds()); 1039 1040 if (CC_UNLIKELY(!mProjectedNodes.empty() && projectionReceiveIndex >= 0 && 1041 opIndex == static_cast<size_t>(projectionReceiveIndex))) { 1042 issueOperationsOfProjectedChildren(renderer, handler); 1043 } 1044 } 1045 1046 issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren, 1047 initialTransform, zTranslatedNodes, renderer, handler); 1048 } 1049 } 1050 } 1051#endif 1052 1053 DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo); 1054 handler(new (alloc) RestoreToCountOp(restoreTo), 1055 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 1056 1057 DISPLAY_LIST_LOGD("%*sDone (%p, %s)", handler.level() * 2, "", this, getName()); 1058 handler.endMark(); 1059} 1060 1061} /* namespace uirenderer */ 1062} /* namespace android */ 1063