RenderNode.cpp revision c79eabcd3c6306bb2ec75f9584b79e661f265127
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#define ATRACE_TAG ATRACE_TAG_VIEW 18#define LOG_TAG "OpenGLRenderer" 19 20#include "RenderNode.h" 21 22#include <algorithm> 23#include <string> 24 25#include <SkCanvas.h> 26#include <algorithm> 27 28#include <utils/Trace.h> 29 30#include "DamageAccumulator.h" 31#include "Debug.h" 32#include "DisplayListOp.h" 33#include "DisplayListLogBuffer.h" 34#include "LayerRenderer.h" 35#include "OpenGLRenderer.h" 36#include "utils/MathUtils.h" 37 38namespace android { 39namespace uirenderer { 40 41void RenderNode::outputLogBuffer(int fd) { 42 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); 43 if (logBuffer.isEmpty()) { 44 return; 45 } 46 47 FILE *file = fdopen(fd, "a"); 48 49 fprintf(file, "\nRecent DisplayList operations\n"); 50 logBuffer.outputCommands(file); 51 52 String8 cachesLog; 53 Caches::getInstance().dumpMemoryUsage(cachesLog); 54 fprintf(file, "\nCaches:\n%s", cachesLog.string()); 55 fprintf(file, "\n"); 56 57 fflush(file); 58} 59 60RenderNode::RenderNode() 61 : mDirtyPropertyFields(0) 62 , mNeedsDisplayListDataSync(false) 63 , mDisplayListData(0) 64 , mStagingDisplayListData(0) 65 , mAnimatorManager(*this) 66 , mLayer(0) 67 , mParentCount(0) { 68} 69 70RenderNode::~RenderNode() { 71 deleteDisplayListData(); 72 delete mStagingDisplayListData; 73 LayerRenderer::destroyLayerDeferred(mLayer); 74} 75 76void RenderNode::setStagingDisplayList(DisplayListData* data) { 77 mNeedsDisplayListDataSync = true; 78 delete mStagingDisplayListData; 79 mStagingDisplayListData = data; 80 if (mStagingDisplayListData) { 81 Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size()); 82 } 83} 84 85/** 86 * This function is a simplified version of replay(), where we simply retrieve and log the 87 * display list. This function should remain in sync with the replay() function. 88 */ 89void RenderNode::output(uint32_t level) { 90 ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this, 91 getName(), isRenderable()); 92 ALOGD("%*s%s %d", level * 2, "", "Save", 93 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 94 95 properties().debugOutputProperties(level); 96 int flags = DisplayListOp::kOpLogFlag_Recurse; 97 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { 98 mDisplayListData->displayListOps[i]->output(level, flags); 99 } 100 101 ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName()); 102} 103 104int RenderNode::getDebugSize() { 105 int size = sizeof(RenderNode); 106 if (mStagingDisplayListData) { 107 size += mStagingDisplayListData->allocator.usedSize(); 108 } 109 if (mDisplayListData && mDisplayListData != mStagingDisplayListData) { 110 size += mDisplayListData->allocator.usedSize(); 111 } 112 return size; 113} 114 115void RenderNode::prepareTree(TreeInfo& info) { 116 ATRACE_CALL(); 117 118 prepareTreeImpl(info); 119} 120 121void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) { 122 mAnimatorManager.addAnimator(animator); 123} 124 125void RenderNode::damageSelf(TreeInfo& info) { 126 if (isRenderable()) { 127 if (properties().getClipDamageToBounds()) { 128 info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); 129 } else { 130 // Hope this is big enough? 131 // TODO: Get this from the display list ops or something 132 info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX); 133 } 134 } 135} 136 137void RenderNode::prepareLayer(TreeInfo& info) { 138 LayerType layerType = properties().layerProperties().type(); 139 if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) { 140 // We push a null transform here as we don't care what the existing dirty 141 // area is, only what our display list dirty is as well as our children's 142 // dirty area 143 info.damageAccumulator->pushNullTransform(); 144 } 145} 146 147void RenderNode::pushLayerUpdate(TreeInfo& info) { 148 LayerType layerType = properties().layerProperties().type(); 149 // If we are not a layer OR we cannot be rendered (eg, view was detached) 150 // we need to destroy any Layers we may have had previously 151 if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) { 152 if (layerType == kLayerTypeRenderLayer) { 153 info.damageAccumulator->popTransform(); 154 } 155 if (CC_UNLIKELY(mLayer)) { 156 LayerRenderer::destroyLayer(mLayer); 157 mLayer = NULL; 158 } 159 return; 160 } 161 162 if (!mLayer) { 163 mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight()); 164 applyLayerPropertiesToLayer(info); 165 damageSelf(info); 166 } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) { 167 if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { 168 LayerRenderer::destroyLayer(mLayer); 169 mLayer = 0; 170 } 171 damageSelf(info); 172 } 173 174 SkRect dirty; 175 info.damageAccumulator->peekAtDirty(&dirty); 176 info.damageAccumulator->popTransform(); 177 178 if (!mLayer) { 179 if (info.errorHandler) { 180 std::string msg = "Unable to create layer for "; 181 msg += getName(); 182 info.errorHandler->onError(msg); 183 } 184 return; 185 } 186 187 188 if (dirty.intersect(0, 0, getWidth(), getHeight())) { 189 dirty.roundOut(); 190 mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom); 191 } 192 // This is not inside the above if because we may have called 193 // updateDeferred on a previous prepare pass that didn't have a renderer 194 if (info.renderer && mLayer->deferredUpdateScheduled) { 195 info.renderer->pushLayerUpdate(mLayer); 196 } 197} 198 199void RenderNode::prepareTreeImpl(TreeInfo& info) { 200 info.damageAccumulator->pushTransform(this); 201 202 if (info.mode == TreeInfo::MODE_FULL) { 203 pushStagingPropertiesChanges(info); 204 } 205 mAnimatorManager.animate(info); 206 prepareLayer(info); 207 if (info.mode == TreeInfo::MODE_FULL) { 208 pushStagingDisplayListChanges(info); 209 } 210 prepareSubTree(info, mDisplayListData); 211 pushLayerUpdate(info); 212 213 info.damageAccumulator->popTransform(); 214} 215 216void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { 217 // Push the animators first so that setupStartValueIfNecessary() is called 218 // before properties() is trampled by stagingProperties(), as they are 219 // required by some animators. 220 mAnimatorManager.pushStaging(info); 221 if (mDirtyPropertyFields) { 222 mDirtyPropertyFields = 0; 223 damageSelf(info); 224 info.damageAccumulator->popTransform(); 225 mProperties = mStagingProperties; 226 applyLayerPropertiesToLayer(info); 227 // We could try to be clever and only re-damage if the matrix changed. 228 // However, we don't need to worry about that. The cost of over-damaging 229 // here is only going to be a single additional map rect of this node 230 // plus a rect join(). The parent's transform (and up) will only be 231 // performed once. 232 info.damageAccumulator->pushTransform(this); 233 damageSelf(info); 234 } 235} 236 237void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) { 238 if (CC_LIKELY(!mLayer)) return; 239 240 const LayerProperties& props = properties().layerProperties(); 241 mLayer->setAlpha(props.alpha(), props.xferMode()); 242 mLayer->setColorFilter(props.colorFilter()); 243 mLayer->setBlend(props.needsBlending()); 244} 245 246void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { 247 if (mNeedsDisplayListDataSync) { 248 mNeedsDisplayListDataSync = false; 249 // Make sure we inc first so that we don't fluctuate between 0 and 1, 250 // which would thrash the layer cache 251 if (mStagingDisplayListData) { 252 for (size_t i = 0; i < mStagingDisplayListData->children().size(); i++) { 253 mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount(); 254 } 255 } 256 deleteDisplayListData(); 257 mDisplayListData = mStagingDisplayListData; 258 mStagingDisplayListData = NULL; 259 if (mDisplayListData) { 260 for (size_t i = 0; i < mDisplayListData->functors.size(); i++) { 261 (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, NULL); 262 } 263 } 264 damageSelf(info); 265 } 266} 267 268void RenderNode::deleteDisplayListData() { 269 if (mDisplayListData) { 270 for (size_t i = 0; i < mDisplayListData->children().size(); i++) { 271 mDisplayListData->children()[i]->mRenderNode->decParentRefCount(); 272 } 273 } 274 delete mDisplayListData; 275 mDisplayListData = NULL; 276} 277 278void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { 279 if (subtree) { 280 TextureCache& cache = Caches::getInstance().textureCache; 281 info.out.hasFunctors |= subtree->functors.size(); 282 // TODO: Fix ownedBitmapResources to not require disabling prepareTextures 283 // and thus falling out of async drawing path. 284 if (subtree->ownedBitmapResources.size()) { 285 info.prepareTextures = false; 286 } 287 for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) { 288 info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]); 289 } 290 for (size_t i = 0; i < subtree->children().size(); i++) { 291 DrawRenderNodeOp* op = subtree->children()[i]; 292 RenderNode* childNode = op->mRenderNode; 293 info.damageAccumulator->pushTransform(&op->mTransformFromParent); 294 childNode->prepareTreeImpl(info); 295 info.damageAccumulator->popTransform(); 296 } 297 } 298} 299 300void RenderNode::destroyHardwareResources() { 301 if (mLayer) { 302 LayerRenderer::destroyLayer(mLayer); 303 mLayer = NULL; 304 } 305 if (mDisplayListData) { 306 for (size_t i = 0; i < mDisplayListData->children().size(); i++) { 307 mDisplayListData->children()[i]->mRenderNode->destroyHardwareResources(); 308 } 309 if (mNeedsDisplayListDataSync) { 310 // Next prepare tree we are going to push a new display list, so we can 311 // drop our current one now 312 deleteDisplayListData(); 313 } 314 } 315} 316 317void RenderNode::decParentRefCount() { 318 LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!"); 319 mParentCount--; 320 if (!mParentCount) { 321 // If a child of ours is being attached to our parent then this will incorrectly 322 // destroy its hardware resources. However, this situation is highly unlikely 323 // and the failure is "just" that the layer is re-created, so this should 324 // be safe enough 325 destroyHardwareResources(); 326 } 327} 328 329/* 330 * For property operations, we pass a savecount of 0, since the operations aren't part of the 331 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in 332 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount()) 333 */ 334#define PROPERTY_SAVECOUNT 0 335 336template <class T> 337void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { 338#if DEBUG_DISPLAY_LIST 339 properties().debugOutputProperties(handler.level() + 1); 340#endif 341 if (properties().getLeft() != 0 || properties().getTop() != 0) { 342 renderer.translate(properties().getLeft(), properties().getTop()); 343 } 344 if (properties().getStaticMatrix()) { 345 renderer.concatMatrix(*properties().getStaticMatrix()); 346 } else if (properties().getAnimationMatrix()) { 347 renderer.concatMatrix(*properties().getAnimationMatrix()); 348 } 349 if (properties().hasTransformMatrix()) { 350 if (properties().isTransformTranslateOnly()) { 351 renderer.translate(properties().getTranslationX(), properties().getTranslationY()); 352 } else { 353 renderer.concatMatrix(*properties().getTransformMatrix()); 354 } 355 } 356 const bool isLayer = properties().layerProperties().type() != kLayerTypeNone; 357 int clipFlags = properties().getClippingFlags(); 358 if (properties().getAlpha() < 1) { 359 if (isLayer) { 360 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer 361 362 renderer.setOverrideLayerAlpha(properties().getAlpha()); 363 } else if (!properties().getHasOverlappingRendering()) { 364 renderer.scaleAlpha(properties().getAlpha()); 365 } else { 366 Rect layerBounds(0, 0, getWidth(), getHeight()); 367 int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag; 368 if (clipFlags) { 369 saveFlags |= SkCanvas::kClipToLayer_SaveFlag; 370 properties().getClippingRectForFlags(clipFlags, &layerBounds); 371 clipFlags = 0; // all clipping done by saveLayer 372 } 373 374 SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( 375 layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom, 376 properties().getAlpha() * 255, saveFlags); 377 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 378 } 379 } 380 if (clipFlags) { 381 Rect clipRect; 382 properties().getClippingRectForFlags(clipFlags, &clipRect); 383 ClipRectOp* op = new (handler.allocator()) ClipRectOp( 384 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, 385 SkRegion::kIntersect_Op); 386 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 387 } 388 389 // TODO: support both reveal clip and outline clip simultaneously 390 if (mProperties.getRevealClip().willClip()) { 391 Rect bounds; 392 mProperties.getRevealClip().getBounds(&bounds); 393 renderer.setClippingRoundRect(handler.allocator(), bounds, mProperties.getRevealClip().getRadius()); 394 } else if (mProperties.getOutline().willClip()) { 395 renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline())); 396 } 397 398} 399 400/** 401 * Apply property-based transformations to input matrix 402 * 403 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4 404 * matrix computation instead of the Skia 3x3 matrix + camera hackery. 405 */ 406void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) { 407 if (properties().getLeft() != 0 || properties().getTop() != 0) { 408 matrix.translate(properties().getLeft(), properties().getTop()); 409 } 410 if (properties().getStaticMatrix()) { 411 mat4 stat(*properties().getStaticMatrix()); 412 matrix.multiply(stat); 413 } else if (properties().getAnimationMatrix()) { 414 mat4 anim(*properties().getAnimationMatrix()); 415 matrix.multiply(anim); 416 } 417 418 bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ()); 419 if (properties().hasTransformMatrix() || applyTranslationZ) { 420 if (properties().isTransformTranslateOnly()) { 421 matrix.translate(properties().getTranslationX(), properties().getTranslationY(), 422 true3dTransform ? properties().getZ() : 0.0f); 423 } else { 424 if (!true3dTransform) { 425 matrix.multiply(*properties().getTransformMatrix()); 426 } else { 427 mat4 true3dMat; 428 true3dMat.loadTranslate( 429 properties().getPivotX() + properties().getTranslationX(), 430 properties().getPivotY() + properties().getTranslationY(), 431 properties().getZ()); 432 true3dMat.rotate(properties().getRotationX(), 1, 0, 0); 433 true3dMat.rotate(properties().getRotationY(), 0, 1, 0); 434 true3dMat.rotate(properties().getRotation(), 0, 0, 1); 435 true3dMat.scale(properties().getScaleX(), properties().getScaleY(), 1); 436 true3dMat.translate(-properties().getPivotX(), -properties().getPivotY()); 437 438 matrix.multiply(true3dMat); 439 } 440 } 441 } 442} 443 444/** 445 * Organizes the DisplayList hierarchy to prepare for background projection reordering. 446 * 447 * This should be called before a call to defer() or drawDisplayList() 448 * 449 * Each DisplayList that serves as a 3d root builds its list of composited children, 450 * which are flagged to not draw in the standard draw loop. 451 */ 452void RenderNode::computeOrdering() { 453 ATRACE_CALL(); 454 mProjectedNodes.clear(); 455 456 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that 457 // transform properties are applied correctly to top level children 458 if (mDisplayListData == NULL) return; 459 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { 460 DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; 461 childOp->mRenderNode->computeOrderingImpl(childOp, 462 properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity()); 463 } 464} 465 466void RenderNode::computeOrderingImpl( 467 DrawRenderNodeOp* opState, 468 const SkPath* outlineOfProjectionSurface, 469 Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, 470 const mat4* transformFromProjectionSurface) { 471 mProjectedNodes.clear(); 472 if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return; 473 474 // TODO: should avoid this calculation in most cases 475 // TODO: just calculate single matrix, down to all leaf composited elements 476 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); 477 localTransformFromProjectionSurface.multiply(opState->mTransformFromParent); 478 479 if (properties().getProjectBackwards()) { 480 // composited projectee, flag for out of order draw, save matrix, and store in proj surface 481 opState->mSkipInOrderDraw = true; 482 opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface); 483 compositedChildrenOfProjectionSurface->add(opState); 484 } else { 485 // standard in order draw 486 opState->mSkipInOrderDraw = false; 487 } 488 489 if (mDisplayListData->children().size() > 0) { 490 const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0; 491 bool haveAppliedPropertiesToProjection = false; 492 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { 493 DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; 494 RenderNode* child = childOp->mRenderNode; 495 496 const SkPath* projectionOutline = NULL; 497 Vector<DrawRenderNodeOp*>* projectionChildren = NULL; 498 const mat4* projectionTransform = NULL; 499 if (isProjectionReceiver && !child->properties().getProjectBackwards()) { 500 // if receiving projections, collect projecting descendent 501 502 // Note that if a direct descendent is projecting backwards, we pass it's 503 // grandparent projection collection, since it shouldn't project onto it's 504 // parent, where it will already be drawing. 505 projectionOutline = properties().getOutline().getPath(); 506 projectionChildren = &mProjectedNodes; 507 projectionTransform = &mat4::identity(); 508 } else { 509 if (!haveAppliedPropertiesToProjection) { 510 applyViewPropertyTransforms(localTransformFromProjectionSurface); 511 haveAppliedPropertiesToProjection = true; 512 } 513 projectionOutline = outlineOfProjectionSurface; 514 projectionChildren = compositedChildrenOfProjectionSurface; 515 projectionTransform = &localTransformFromProjectionSurface; 516 } 517 child->computeOrderingImpl(childOp, 518 projectionOutline, projectionChildren, projectionTransform); 519 } 520 } 521} 522 523class DeferOperationHandler { 524public: 525 DeferOperationHandler(DeferStateStruct& deferStruct, int level) 526 : mDeferStruct(deferStruct), mLevel(level) {} 527 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 528 operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); 529 } 530 inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } 531 inline void startMark(const char* name) {} // do nothing 532 inline void endMark() {} 533 inline int level() { return mLevel; } 534 inline int replayFlags() { return mDeferStruct.mReplayFlags; } 535 536private: 537 DeferStateStruct& mDeferStruct; 538 const int mLevel; 539}; 540 541void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { 542 DeferOperationHandler handler(deferStruct, level); 543 issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); 544} 545 546class ReplayOperationHandler { 547public: 548 ReplayOperationHandler(ReplayStateStruct& replayStruct, int level) 549 : mReplayStruct(replayStruct), mLevel(level) {} 550 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 551#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 552 mReplayStruct.mRenderer.eventMark(operation->name()); 553#endif 554 operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); 555 } 556 inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } 557 inline void startMark(const char* name) { 558 mReplayStruct.mRenderer.startMark(name); 559 } 560 inline void endMark() { 561 mReplayStruct.mRenderer.endMark(); 562 } 563 inline int level() { return mLevel; } 564 inline int replayFlags() { return mReplayStruct.mReplayFlags; } 565 566private: 567 ReplayStateStruct& mReplayStruct; 568 const int mLevel; 569}; 570 571void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) { 572 ReplayOperationHandler handler(replayStruct, level); 573 issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); 574} 575 576void RenderNode::buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) { 577 if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return; 578 579 for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { 580 DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; 581 RenderNode* child = childOp->mRenderNode; 582 float childZ = child->properties().getZ(); 583 584 if (!MathUtils::isZero(childZ)) { 585 zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp)); 586 childOp->mSkipInOrderDraw = true; 587 } else if (!child->properties().getProjectBackwards()) { 588 // regular, in order drawing DisplayList 589 childOp->mSkipInOrderDraw = false; 590 } 591 } 592 593 // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order) 594 std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end()); 595} 596 597template <class T> 598void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) { 599 if (properties().getAlpha() <= 0.0f 600 || properties().getOutline().getAlpha() <= 0.0f 601 || !properties().getOutline().getPath()) { 602 // no shadow to draw 603 return; 604 } 605 606 mat4 shadowMatrixXY(transformFromParent); 607 applyViewPropertyTransforms(shadowMatrixXY); 608 609 // Z matrix needs actual 3d transformation, so mapped z values will be correct 610 mat4 shadowMatrixZ(transformFromParent); 611 applyViewPropertyTransforms(shadowMatrixZ, true); 612 613 const SkPath* outlinePath = properties().getOutline().getPath(); 614 const SkPath* revealClipPath = properties().getRevealClip().getPath(); 615 if (revealClipPath && revealClipPath->isEmpty()) return; 616 617 float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha(); 618 DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp( 619 shadowMatrixXY, shadowMatrixZ, casterAlpha, 620 outlinePath, revealClipPath); 621 handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 622} 623 624template <class T> 625int RenderNode::issueOperationsOfNegZChildren( 626 const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, 627 OpenGLRenderer& renderer, T& handler) { 628 if (zTranslatedNodes.isEmpty()) return -1; 629 630 // create a save around the body of the ViewGroup's draw method, so that 631 // matrix/clip methods don't affect composited children 632 int shadowSaveCount = renderer.getSaveCount(); 633 handler(new (handler.allocator()) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 634 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 635 636 issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler); 637 return shadowSaveCount; 638} 639 640template <class T> 641void RenderNode::issueOperationsOfPosZChildren(int shadowRestoreTo, 642 const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, 643 OpenGLRenderer& renderer, T& handler) { 644 if (zTranslatedNodes.isEmpty()) return; 645 646 LOG_ALWAYS_FATAL_IF(shadowRestoreTo < 0, "invalid save to restore to"); 647 handler(new (handler.allocator()) RestoreToCountOp(shadowRestoreTo), 648 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 649 renderer.setOverrideLayerAlpha(1.0f); 650 651 issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler); 652} 653 654#define SHADOW_DELTA 0.1f 655 656template <class T> 657void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, 658 ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) { 659 const int size = zTranslatedNodes.size(); 660 if (size == 0 661 || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f) 662 || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { 663 // no 3d children to draw 664 return; 665 } 666 667 /** 668 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters 669 * with very similar Z heights to draw together. 670 * 671 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are 672 * underneath both, and neither's shadow is drawn on top of the other. 673 */ 674 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); 675 size_t drawIndex, shadowIndex, endIndex; 676 if (mode == kNegativeZChildren) { 677 drawIndex = 0; 678 endIndex = nonNegativeIndex; 679 shadowIndex = endIndex; // draw no shadows 680 } else { 681 drawIndex = nonNegativeIndex; 682 endIndex = size; 683 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child 684 } 685 686 DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "", 687 endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive"); 688 689 float lastCasterZ = 0.0f; 690 while (shadowIndex < endIndex || drawIndex < endIndex) { 691 if (shadowIndex < endIndex) { 692 DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value; 693 RenderNode* caster = casterOp->mRenderNode; 694 const float casterZ = zTranslatedNodes[shadowIndex].key; 695 // attempt to render the shadow if the caster about to be drawn is its caster, 696 // OR if its caster's Z value is similar to the previous potential caster 697 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { 698 caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler); 699 700 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow 701 shadowIndex++; 702 continue; 703 } 704 } 705 706 // only the actual child DL draw needs to be in save/restore, 707 // since it modifies the renderer's matrix 708 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 709 710 DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; 711 RenderNode* child = childOp->mRenderNode; 712 713 renderer.concatMatrix(childOp->mTransformFromParent); 714 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 715 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); 716 childOp->mSkipInOrderDraw = true; 717 718 renderer.restoreToCount(restoreTo); 719 drawIndex++; 720 } 721} 722 723template <class T> 724void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) { 725 DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size()); 726 const SkPath* projectionReceiverOutline = properties().getOutline().getPath(); 727 int restoreTo = renderer.getSaveCount(); 728 729 // If the projection reciever has an outline, we mask each of the projected rendernodes to it 730 // Either with clipRect, or special saveLayer masking 731 LinearAllocator& alloc = handler.allocator(); 732 if (projectionReceiverOutline != NULL) { 733 const SkRect& outlineBounds = projectionReceiverOutline->getBounds(); 734 if (projectionReceiverOutline->isRect(NULL)) { 735 // mask to the rect outline simply with clipRect 736 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 737 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 738 ClipRectOp* clipOp = new (alloc) ClipRectOp( 739 outlineBounds.left(), outlineBounds.top(), 740 outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op); 741 handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 742 } else { 743 // wrap the projected RenderNodes with a SaveLayer that will mask to the outline 744 SaveLayerOp* op = new (alloc) SaveLayerOp( 745 outlineBounds.left(), outlineBounds.top(), 746 outlineBounds.right(), outlineBounds.bottom(), 747 255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag); 748 op->setMask(projectionReceiverOutline); 749 handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); 750 751 /* TODO: add optimizations here to take advantage of placement/size of projected 752 * children (which may shrink saveLayer area significantly). This is dependent on 753 * passing actual drawing/dirtying bounds of projected content down to native. 754 */ 755 } 756 } 757 758 // draw projected nodes 759 for (size_t i = 0; i < mProjectedNodes.size(); i++) { 760 DrawRenderNodeOp* childOp = mProjectedNodes[i]; 761 762 // matrix save, concat, and restore can be done safely without allocating operations 763 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 764 renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); 765 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 766 handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); 767 childOp->mSkipInOrderDraw = true; 768 renderer.restoreToCount(restoreTo); 769 } 770 771 if (projectionReceiverOutline != NULL) { 772 handler(new (alloc) RestoreToCountOp(restoreTo), 773 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 774 } 775} 776 777/** 778 * This function serves both defer and replay modes, and will organize the displayList's component 779 * operations for a single frame: 780 * 781 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of 782 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom 783 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the 784 * defer vs replay logic, per operation 785 */ 786template <class T> 787void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { 788 const int level = handler.level(); 789 if (mDisplayListData->isEmpty()) { 790 DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName()); 791 return; 792 } 793 794 const bool drawLayer = (mLayer && (&renderer != mLayer->renderer)); 795 // If we are updating the contents of mLayer, we don't want to apply any of 796 // the RenderNode's properties to this issueOperations pass. Those will all 797 // be applied when the layer is drawn, aka when this is true. 798 const bool useViewProperties = (!mLayer || drawLayer); 799 if (useViewProperties) { 800 const Outline& outline = properties().getOutline(); 801 if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) { 802 DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", level * 2, "", this, getName()); 803 return; 804 } 805 } 806 807 handler.startMark(getName()); 808 809#if DEBUG_DISPLAY_LIST 810 const Rect& clipRect = renderer.getLocalClipBounds(); 811 DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f", 812 level * 2, "", this, getName(), 813 clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); 814#endif 815 816 LinearAllocator& alloc = handler.allocator(); 817 int restoreTo = renderer.getSaveCount(); 818 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 819 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 820 821 DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", 822 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); 823 824 if (useViewProperties) { 825 setViewProperties<T>(renderer, handler); 826 } 827 828 bool quickRejected = properties().getClipToBounds() 829 && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); 830 if (!quickRejected) { 831 if (drawLayer) { 832 handler(new (alloc) DrawLayerOp(mLayer, 0, 0), 833 renderer.getSaveCount() - 1, properties().getClipToBounds()); 834 } else { 835 Vector<ZDrawRenderNodeOpPair> zTranslatedNodes; 836 buildZSortedChildList(zTranslatedNodes); 837 838 // for 3d root, draw children with negative z values 839 int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes, renderer, handler); 840 841 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); 842 const int saveCountOffset = renderer.getSaveCount() - 1; 843 const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; 844 const int size = static_cast<int>(mDisplayListData->displayListOps.size()); 845 for (int i = 0; i < size; i++) { 846 DisplayListOp *op = mDisplayListData->displayListOps[i]; 847 848#if DEBUG_DISPLAY_LIST 849 op->output(level + 1); 850#endif 851 logBuffer.writeCommand(level, op->name()); 852 handler(op, saveCountOffset, properties().getClipToBounds()); 853 854 if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) { 855 issueOperationsOfProjectedChildren(renderer, handler); 856 } 857 } 858 859 // for 3d root, draw children with positive z values 860 issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler); 861 } 862 } 863 864 DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); 865 handler(new (alloc) RestoreToCountOp(restoreTo), 866 PROPERTY_SAVECOUNT, properties().getClipToBounds()); 867 renderer.setOverrideLayerAlpha(1.0f); 868 869 DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName()); 870 handler.endMark(); 871} 872 873} /* namespace uirenderer */ 874} /* namespace android */ 875