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