RenderNode.cpp revision 113e0824d6bddf4376240681f9cf6a2deded9498
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 19#include "RenderNode.h" 20 21#include <SkCanvas.h> 22#include <algorithm> 23 24#include <utils/Trace.h> 25 26#include "Debug.h" 27#include "DisplayListOp.h" 28#include "DisplayListLogBuffer.h" 29 30namespace android { 31namespace uirenderer { 32 33void RenderNode::outputLogBuffer(int fd) { 34 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); 35 if (logBuffer.isEmpty()) { 36 return; 37 } 38 39 FILE *file = fdopen(fd, "a"); 40 41 fprintf(file, "\nRecent DisplayList operations\n"); 42 logBuffer.outputCommands(file); 43 44 String8 cachesLog; 45 Caches::getInstance().dumpMemoryUsage(cachesLog); 46 fprintf(file, "\nCaches:\n%s", cachesLog.string()); 47 fprintf(file, "\n"); 48 49 fflush(file); 50} 51 52RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) { 53} 54 55RenderNode::~RenderNode() { 56 LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this); 57 58 mDestroyed = true; 59 delete mDisplayListData; 60} 61 62void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) { 63 if (displayList) { 64 DISPLAY_LIST_LOGD("Deferring display list destruction"); 65 Caches::getInstance().deleteDisplayListDeferred(displayList); 66 } 67} 68 69void RenderNode::setData(DisplayListData* data) { 70 delete mDisplayListData; 71 mDisplayListData = data; 72 if (mDisplayListData) { 73 Caches::getInstance().registerFunctors(mDisplayListData->functorCount); 74 } 75} 76 77/** 78 * This function is a simplified version of replay(), where we simply retrieve and log the 79 * display list. This function should remain in sync with the replay() function. 80 */ 81void RenderNode::output(uint32_t level) { 82 ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this, 83 mName.string(), isRenderable()); 84 ALOGD("%*s%s %d", level * 2, "", "Save", 85 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 86 87 outputViewProperties(level); 88 int flags = DisplayListOp::kOpLogFlag_Recurse; 89 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { 90 mDisplayListData->displayListOps[i]->output(level, flags); 91 } 92 93 ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string()); 94} 95 96void RenderNode::outputViewProperties(const int level) { 97 properties().updateMatrix(); 98 if (properties().mLeft != 0 || properties().mTop != 0) { 99 ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop); 100 } 101 if (properties().mStaticMatrix) { 102 ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING, 103 level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix)); 104 } 105 if (properties().mAnimationMatrix) { 106 ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING, 107 level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix)); 108 } 109 if (properties().mMatrixFlags != 0) { 110 if (properties().mMatrixFlags == TRANSLATION) { 111 ALOGD("%*sTranslate %.2f, %.2f, %.2f", 112 level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ); 113 } else { 114 ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING, 115 level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix)); 116 } 117 } 118 119 bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds; 120 if (properties().mAlpha < 1) { 121 if (properties().mCaching) { 122 ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha); 123 } else if (!properties().mHasOverlappingRendering) { 124 ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha); 125 } else { 126 int flags = SkCanvas::kHasAlphaLayer_SaveFlag; 127 if (clipToBoundsNeeded) { 128 flags |= SkCanvas::kClipToLayer_SaveFlag; 129 clipToBoundsNeeded = false; // clipping done by save layer 130 } 131 ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "", 132 (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop, 133 (int)(properties().mAlpha * 255), flags); 134 } 135 } 136 if (clipToBoundsNeeded) { 137 ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f, 138 (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop); 139 } 140} 141 142/* 143 * For property operations, we pass a savecount of 0, since the operations aren't part of the 144 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in 145 * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount) 146 */ 147#define PROPERTY_SAVECOUNT 0 148 149template <class T> 150void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler, 151 const int level) { 152#if DEBUG_DISPLAY_LIST 153 outputViewProperties(level); 154#endif 155 properties().updateMatrix(); 156 if (properties().mLeft != 0 || properties().mTop != 0) { 157 renderer.translate(properties().mLeft, properties().mTop); 158 } 159 if (properties().mStaticMatrix) { 160 renderer.concatMatrix(properties().mStaticMatrix); 161 } else if (properties().mAnimationMatrix) { 162 renderer.concatMatrix(properties().mAnimationMatrix); 163 } 164 if (properties().mMatrixFlags != 0) { 165 if (properties().mMatrixFlags == TRANSLATION) { 166 renderer.translate(properties().mTranslationX, properties().mTranslationY); 167 } else { 168 renderer.concatMatrix(*properties().mTransformMatrix); 169 } 170 } 171 bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds; 172 if (properties().mAlpha < 1) { 173 if (properties().mCaching) { 174 renderer.setOverrideLayerAlpha(properties().mAlpha); 175 } else if (!properties().mHasOverlappingRendering) { 176 renderer.scaleAlpha(properties().mAlpha); 177 } else { 178 // TODO: should be able to store the size of a DL at record time and not 179 // have to pass it into this call. In fact, this information might be in the 180 // location/size info that we store with the new native transform data. 181 int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag; 182 if (clipToBoundsNeeded) { 183 saveFlags |= SkCanvas::kClipToLayer_SaveFlag; 184 clipToBoundsNeeded = false; // clipping done by saveLayer 185 } 186 187 SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( 188 0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags); 189 handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds); 190 } 191 } 192 if (clipToBoundsNeeded) { 193 ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0, 194 properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op); 195 handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds); 196 } 197 if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) { 198 ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op); 199 handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds); 200 } 201} 202 203/** 204 * Apply property-based transformations to input matrix 205 * 206 * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4 207 * matrix computation instead of the Skia 3x3 matrix + camera hackery. 208 */ 209void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) { 210 if (properties().mLeft != 0 || properties().mTop != 0) { 211 matrix.translate(properties().mLeft, properties().mTop); 212 } 213 if (properties().mStaticMatrix) { 214 mat4 stat(*properties().mStaticMatrix); 215 matrix.multiply(stat); 216 } else if (properties().mAnimationMatrix) { 217 mat4 anim(*properties().mAnimationMatrix); 218 matrix.multiply(anim); 219 } 220 if (properties().mMatrixFlags != 0) { 221 properties().updateMatrix(); 222 if (properties().mMatrixFlags == TRANSLATION) { 223 matrix.translate(properties().mTranslationX, properties().mTranslationY, 224 true3dTransform ? properties().mTranslationZ : 0.0f); 225 } else { 226 if (!true3dTransform) { 227 matrix.multiply(*properties().mTransformMatrix); 228 } else { 229 mat4 true3dMat; 230 true3dMat.loadTranslate( 231 properties().mPivotX + properties().mTranslationX, 232 properties().mPivotY + properties().mTranslationY, 233 properties().mTranslationZ); 234 true3dMat.rotate(properties().mRotationX, 1, 0, 0); 235 true3dMat.rotate(properties().mRotationY, 0, 1, 0); 236 true3dMat.rotate(properties().mRotation, 0, 0, 1); 237 true3dMat.scale(properties().mScaleX, properties().mScaleY, 1); 238 true3dMat.translate(-properties().mPivotX, -properties().mPivotY); 239 240 matrix.multiply(true3dMat); 241 } 242 } 243 } 244} 245 246/** 247 * Organizes the DisplayList hierarchy to prepare for background projection reordering. 248 * 249 * This should be called before a call to defer() or drawDisplayList() 250 * 251 * Each DisplayList that serves as a 3d root builds its list of composited children, 252 * which are flagged to not draw in the standard draw loop. 253 */ 254void RenderNode::computeOrdering() { 255 ATRACE_CALL(); 256 mProjectedNodes.clear(); 257 258 // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that 259 // transform properties are applied correctly to top level children 260 if (mDisplayListData == NULL) return; 261 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { 262 DrawDisplayListOp* childOp = mDisplayListData->children[i]; 263 childOp->mDisplayList->computeOrderingImpl(childOp, 264 &mProjectedNodes, &mat4::identity()); 265 } 266} 267 268void RenderNode::computeOrderingImpl( 269 DrawDisplayListOp* opState, 270 Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, 271 const mat4* transformFromProjectionSurface) { 272 mProjectedNodes.clear(); 273 if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return; 274 275 // TODO: should avoid this calculation in most cases 276 // TODO: just calculate single matrix, down to all leaf composited elements 277 Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); 278 localTransformFromProjectionSurface.multiply(opState->mTransformFromParent); 279 280 if (properties().mProjectBackwards) { 281 // composited projectee, flag for out of order draw, save matrix, and store in proj surface 282 opState->mSkipInOrderDraw = true; 283 opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface); 284 compositedChildrenOfProjectionSurface->add(opState); 285 } else { 286 // standard in order draw 287 opState->mSkipInOrderDraw = false; 288 } 289 290 if (mDisplayListData->children.size() > 0) { 291 const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0; 292 bool haveAppliedPropertiesToProjection = false; 293 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { 294 DrawDisplayListOp* childOp = mDisplayListData->children[i]; 295 RenderNode* child = childOp->mDisplayList; 296 297 Vector<DrawDisplayListOp*>* projectionChildren = NULL; 298 const mat4* projectionTransform = NULL; 299 if (isProjectionReceiver && !child->properties().mProjectBackwards) { 300 // if receiving projections, collect projecting descendent 301 302 // Note that if a direct descendent is projecting backwards, we pass it's 303 // grandparent projection collection, since it shouldn't project onto it's 304 // parent, where it will already be drawing. 305 projectionChildren = &mProjectedNodes; 306 projectionTransform = &mat4::identity(); 307 } else { 308 if (!haveAppliedPropertiesToProjection) { 309 applyViewPropertyTransforms(localTransformFromProjectionSurface); 310 haveAppliedPropertiesToProjection = true; 311 } 312 projectionChildren = compositedChildrenOfProjectionSurface; 313 projectionTransform = &localTransformFromProjectionSurface; 314 } 315 child->computeOrderingImpl(childOp, projectionChildren, projectionTransform); 316 } 317 } 318 319} 320 321class DeferOperationHandler { 322public: 323 DeferOperationHandler(DeferStateStruct& deferStruct, int level) 324 : mDeferStruct(deferStruct), mLevel(level) {} 325 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 326 operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); 327 } 328 inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } 329 330private: 331 DeferStateStruct& mDeferStruct; 332 const int mLevel; 333}; 334 335void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { 336 DeferOperationHandler handler(deferStruct, level); 337 iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level); 338} 339 340class ReplayOperationHandler { 341public: 342 ReplayOperationHandler(ReplayStateStruct& replayStruct, int level) 343 : mReplayStruct(replayStruct), mLevel(level) {} 344 inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { 345#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 346 properties().mReplayStruct.mRenderer.eventMark(operation->name()); 347#endif 348 operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); 349 } 350 inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } 351 352private: 353 ReplayStateStruct& mReplayStruct; 354 const int mLevel; 355}; 356 357void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) { 358 ReplayOperationHandler handler(replayStruct, level); 359 360 replayStruct.mRenderer.startMark(mName.string()); 361 iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level); 362 replayStruct.mRenderer.endMark(); 363 364 DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(), 365 replayStruct.mDrawGlStatus); 366} 367 368void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) { 369 if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return; 370 371 for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { 372 DrawDisplayListOp* childOp = mDisplayListData->children[i]; 373 RenderNode* child = childOp->mDisplayList; 374 float childZ = child->properties().mTranslationZ; 375 376 if (childZ != 0.0f) { 377 zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp)); 378 childOp->mSkipInOrderDraw = true; 379 } else if (!child->properties().mProjectBackwards) { 380 // regular, in order drawing DisplayList 381 childOp->mSkipInOrderDraw = false; 382 } 383 } 384 385 // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order) 386 std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end()); 387} 388 389#define SHADOW_DELTA 0.1f 390 391template <class T> 392void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes, 393 ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) { 394 const int size = zTranslatedNodes.size(); 395 if (size == 0 396 || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f) 397 || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) { 398 // no 3d children to draw 399 return; 400 } 401 402 int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 403 LinearAllocator& alloc = handler.allocator(); 404 ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight, 405 SkRegion::kIntersect_Op); // clip to 3d root bounds 406 handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds); 407 408 /** 409 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters 410 * with very similar Z heights to draw together. 411 * 412 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are 413 * underneath both, and neither's shadow is drawn on top of the other. 414 */ 415 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); 416 size_t drawIndex, shadowIndex, endIndex; 417 if (mode == kNegativeZChildren) { 418 drawIndex = 0; 419 endIndex = nonNegativeIndex; 420 shadowIndex = endIndex; // draw no shadows 421 } else { 422 drawIndex = nonNegativeIndex; 423 endIndex = size; 424 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child 425 } 426 float lastCasterZ = 0.0f; 427 while (shadowIndex < endIndex || drawIndex < endIndex) { 428 if (shadowIndex < endIndex) { 429 DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value; 430 RenderNode* caster = casterOp->mDisplayList; 431 const float casterZ = zTranslatedNodes[shadowIndex].key; 432 // attempt to render the shadow if the caster about to be drawn is its caster, 433 // OR if its caster's Z value is similar to the previous potential caster 434 if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { 435 436 if (caster->properties().mAlpha > 0.0f) { 437 mat4 shadowMatrixXY(casterOp->mTransformFromParent); 438 caster->applyViewPropertyTransforms(shadowMatrixXY); 439 440 // Z matrix needs actual 3d transformation, so mapped z values will be correct 441 mat4 shadowMatrixZ(casterOp->mTransformFromParent); 442 caster->applyViewPropertyTransforms(shadowMatrixZ, true); 443 444 DisplayListOp* shadowOp = new (alloc) DrawShadowOp( 445 shadowMatrixXY, shadowMatrixZ, 446 caster->properties().mAlpha, &(caster->properties().mOutline), 447 caster->properties().mWidth, caster->properties().mHeight); 448 handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds); 449 } 450 451 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow 452 shadowIndex++; 453 continue; 454 } 455 } 456 457 // only the actual child DL draw needs to be in save/restore, 458 // since it modifies the renderer's matrix 459 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 460 461 DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value; 462 RenderNode* child = childOp->mDisplayList; 463 464 renderer.concatMatrix(childOp->mTransformFromParent); 465 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 466 handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds); 467 childOp->mSkipInOrderDraw = true; 468 469 renderer.restoreToCount(restoreTo); 470 drawIndex++; 471 } 472 handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds); 473} 474 475template <class T> 476void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) { 477 int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 478 LinearAllocator& alloc = handler.allocator(); 479 ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight, 480 SkRegion::kReplace_Op); // clip to projection surface root bounds 481 handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds); 482 483 for (size_t i = 0; i < mProjectedNodes.size(); i++) { 484 DrawDisplayListOp* childOp = mProjectedNodes[i]; 485 486 // matrix save, concat, and restore can be done safely without allocating operations 487 int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); 488 renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); 489 childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone 490 handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds); 491 childOp->mSkipInOrderDraw = true; 492 renderer.restoreToCount(restoreTo); 493 } 494 handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds); 495} 496 497/** 498 * This function serves both defer and replay modes, and will organize the displayList's component 499 * operations for a single frame: 500 * 501 * Every 'simple' state operation that affects just the matrix and alpha (or other factors of 502 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom 503 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the 504 * defer vs replay logic, per operation 505 */ 506template <class T> 507void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) { 508 if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging 509 ALOGW("Error: %s is drawing after destruction", mName.string()); 510 CRASH(); 511 } 512 if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) { 513 DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string()); 514 return; 515 } 516 517#if DEBUG_DISPLAY_LIST 518 Rect* clipRect = renderer.getClipRect(); 519 DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f", 520 level * 2, "", this, mName.string(), clipRect->left, clipRect->top, 521 clipRect->right, clipRect->bottom); 522#endif 523 524 LinearAllocator& alloc = handler.allocator(); 525 int restoreTo = renderer.getSaveCount(); 526 handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), 527 PROPERTY_SAVECOUNT, properties().mClipToBounds); 528 529 DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", 530 SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); 531 532 setViewProperties<T>(renderer, handler, level + 1); 533 534 bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight); 535 if (!quickRejected) { 536 Vector<ZDrawDisplayListOpPair> zTranslatedNodes; 537 buildZSortedChildList(zTranslatedNodes); 538 539 // for 3d root, draw children with negative z values 540 iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler); 541 542 DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); 543 const int saveCountOffset = renderer.getSaveCount() - 1; 544 const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; 545 for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { 546 DisplayListOp *op = mDisplayListData->displayListOps[i]; 547 548#if DEBUG_DISPLAY_LIST 549 op->output(level + 1); 550#endif 551 552 logBuffer.writeCommand(level, op->name()); 553 handler(op, saveCountOffset, properties().mClipToBounds); 554 555 if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) { 556 iterateProjectedChildren(renderer, handler, level); 557 } 558 } 559 560 // for 3d root, draw children with positive z values 561 iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler); 562 } 563 564 DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); 565 handler(new (alloc) RestoreToCountOp(restoreTo), 566 PROPERTY_SAVECOUNT, properties().mClipToBounds); 567 renderer.setOverrideLayerAlpha(1.0f); 568} 569 570} /* namespace uirenderer */ 571} /* namespace android */ 572