RenderNodeDrawableTests.cpp revision 6e49c9f007c879f05b035c40c0ba543c00f9d0d0
1/* 2 * Copyright (C) 2016 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 <gtest/gtest.h> 18#include <VectorDrawable.h> 19 20#include "AnimationContext.h" 21#include "DamageAccumulator.h" 22#include "IContextFactory.h" 23#include "pipeline/skia/SkiaDisplayList.h" 24#include "pipeline/skia/SkiaPipeline.h" 25#include "pipeline/skia/SkiaRecordingCanvas.h" 26#include "renderthread/CanvasContext.h" 27#include "tests/common/TestUtils.h" 28#include "SkiaCanvas.h" 29#include <SkSurface_Base.h> 30#include <SkLiteRecorder.h> 31#include <SkClipStack.h> 32#include <string.h> 33 34 35using namespace android; 36using namespace android::uirenderer; 37using namespace android::uirenderer::renderthread; 38using namespace android::uirenderer::skiapipeline; 39 40TEST(RenderNodeDrawable, create) { 41 auto rootNode = TestUtils::createNode(0, 0, 200, 400, 42 [](RenderProperties& props, Canvas& canvas) { 43 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); 44 }); 45 46 auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1)); 47 SkLiteRecorder canvas; 48 canvas.reset(skLiteDL.get()); 49 canvas.translate(100, 100); 50 RenderNodeDrawable drawable(rootNode.get(), &canvas); 51 52 ASSERT_EQ(drawable.getRenderNode(), rootNode.get()); 53 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties()); 54 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix()); 55} 56 57namespace { 58 59static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { 60 SkPaint paint; 61 // order put in blue channel, transparent so overlapped content doesn't get rejected 62 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); 63 canvas->drawRect(0, 0, 100, 100, paint); 64} 65 66static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) { 67 auto node = TestUtils::createSkiaNode(0, 0, 100, 100, 68 [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) { 69 drawOrderedRect(&canvas, expectedDrawOrder); 70 props.setTranslationZ(z); 71 }); 72 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 73} 74 75static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, 76 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) { 77 auto node = TestUtils::createSkiaNode(0, 0, 100, 100, 78 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) { 79 drawOrderedRect(&canvas, expectedDrawOrder); 80 if (setup) { 81 setup(props, canvas); 82 } 83 }); 84 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 85} 86 87class ZReorderCanvas : public SkCanvas { 88public: 89 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {} 90 void onDrawRect(const SkRect& rect, const SkPaint& paint) override { 91 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel 92 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 93 } 94 int getIndex() { return mIndex; } 95protected: 96 int mIndex = 0; 97}; 98 99} // end anonymous namespace 100 101TEST(RenderNodeDrawable, zReorder) { 102 103 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, 104 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 105 canvas.insertReorderBarrier(true); 106 canvas.insertReorderBarrier(false); 107 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 108 drawOrderedRect(&canvas, 1); 109 canvas.insertReorderBarrier(true); 110 drawOrderedNode(&canvas, 6, 2.0f); 111 drawOrderedRect(&canvas, 3); 112 drawOrderedNode(&canvas, 4, 0.0f); 113 drawOrderedRect(&canvas, 5); 114 drawOrderedNode(&canvas, 2, -2.0f); 115 drawOrderedNode(&canvas, 7, 2.0f); 116 canvas.insertReorderBarrier(false); 117 drawOrderedRect(&canvas, 8); 118 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 119 canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op 120 drawOrderedRect(&canvas, 11); 121 drawOrderedNode(&canvas, 10, -1.0f); 122 canvas.insertReorderBarrier(false); 123 canvas.insertReorderBarrier(true); //test with two empty reorder sections 124 canvas.insertReorderBarrier(true); 125 canvas.insertReorderBarrier(false); 126 drawOrderedRect(&canvas, 12); 127 }); 128 129 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection 130 ZReorderCanvas canvas(100, 100); 131 RenderNodeDrawable drawable(parent.get(), &canvas, false); 132 canvas.drawDrawable(&drawable); 133 EXPECT_EQ(13, canvas.getIndex()); 134} 135 136TEST(RenderNodeDrawable, composeOnLayer) 137{ 138 auto surface = SkSurface::MakeRasterN32Premul(1, 1); 139 SkCanvas& canvas = *surface->getCanvas(); 140 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); 141 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); 142 143 auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1, 144 [](RenderProperties& props, SkiaRecordingCanvas& recorder) { 145 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); 146 }); 147 148 //attach a layer to the render node 149 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1); 150 auto canvas2 = surfaceLayer->getCanvas(); 151 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 152 rootNode->setLayerSurface(surfaceLayer); 153 154 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false); 155 canvas.drawDrawable(&drawable1); 156 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0)); 157 158 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true); 159 canvas.drawDrawable(&drawable2); 160 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0)); 161 162 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false); 163 canvas.drawDrawable(&drawable3); 164 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0)); 165 166 rootNode->setLayerSurface(sk_sp<SkSurface>()); 167} 168 169namespace { 170class ContextFactory : public IContextFactory { 171public: 172 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { 173 return new AnimationContext(clock); 174 } 175}; 176 177inline SkRect getBounds(const SkCanvas* canvas) { 178 SkClipStack::BoundsType boundType; 179 SkRect clipBounds; 180 canvas->getClipStack()->getBounds(&clipBounds, &boundType); 181 return clipBounds; 182} 183inline SkRect getLocalBounds(const SkCanvas* canvas) { 184 SkMatrix invertedTotalMatrix; 185 EXPECT_TRUE(canvas->getTotalMatrix().invert(&invertedTotalMatrix)); 186 SkRect outlineInDeviceCoord = getBounds(canvas); 187 SkRect outlineInLocalCoord; 188 invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord); 189 return outlineInLocalCoord; 190} 191} // end anonymous namespace 192 193RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { 194 static const int SCROLL_X = 5; 195 static const int SCROLL_Y = 10; 196 class ProjectionTestCanvas : public SkCanvas { 197 public: 198 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} 199 void onDrawRect(const SkRect& rect, const SkPaint& paint) override { 200 const int index = mIndex++; 201 SkMatrix expectedMatrix;; 202 switch (index) { 203 case 0: //this is node "B" 204 EXPECT_EQ(SkRect::MakeWH(100, 100), rect); 205 EXPECT_EQ(SK_ColorWHITE, paint.getColor()); 206 expectedMatrix.reset(); 207 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), getBounds(this)); 208 break; 209 case 1: //this is node "P" 210 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect); 211 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor()); 212 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y); 213 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), getLocalBounds(this)); 214 break; 215 case 2: //this is node "C" 216 EXPECT_EQ(SkRect::MakeWH(100, 50), rect); 217 EXPECT_EQ(SK_ColorBLUE, paint.getColor()); 218 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y); 219 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), getBounds(this)); 220 break; 221 default: 222 ADD_FAILURE(); 223 } 224 EXPECT_EQ(expectedMatrix, getTotalMatrix()); 225 } 226 227 int getIndex() { return mIndex; } 228 protected: 229 int mIndex = 0; 230 }; 231 232 /** 233 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 234 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 235 * draw, but because it is projected backwards, it's drawn in between B and C. 236 * 237 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background 238 * (which isn't affected by scroll). 239 */ 240 auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100, 241 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 242 properties.setProjectionReceiver(true); 243 // scroll doesn't apply to background, so undone via translationX/Y 244 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 245 properties.setTranslationX(SCROLL_X); 246 properties.setTranslationY(SCROLL_Y); 247 248 SkPaint paint; 249 paint.setColor(SK_ColorWHITE); 250 canvas.drawRect(0, 0, 100, 100, paint); 251 }, "B"); 252 253 auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50, 254 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 255 properties.setProjectBackwards(true); 256 properties.setClipToBounds(false); 257 SkPaint paint; 258 paint.setColor(SK_ColorDKGRAY); 259 canvas.drawRect(-10, -10, 60, 60, paint); 260 }, "P"); 261 auto child = TestUtils::createSkiaNode(0, 50, 100, 100, 262 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 263 SkPaint paint; 264 paint.setColor(SK_ColorBLUE); 265 canvas.drawRect(0, 0, 100, 50, paint); 266 canvas.drawRenderNode(projectingRipple.get()); 267 }, "C"); 268 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, 269 [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 270 // Set a rect outline for the projecting ripple to be masked against. 271 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 272 273 canvas.save(SaveFlags::MatrixClip); 274 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally) 275 canvas.drawRenderNode(receiverBackground.get()); 276 canvas.drawRenderNode(child.get()); 277 canvas.restore(); 278 }, "A"); 279 ContextFactory contextFactory; 280 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( 281 renderThread, false, parent.get(), &contextFactory)); 282 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); 283 DamageAccumulator damageAccumulator; 284 info.damageAccumulator = &damageAccumulator; 285 info.observer = nullptr; 286 parent->prepareTree(info); 287 288 //parent(A) -> (receiverBackground, child) 289 //child(C) -> (rect[0, 0, 100, 50], projectingRipple) 290 //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards 291 //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver 292 293 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection 294 ProjectionTestCanvas canvas(100, 100); 295 RenderNodeDrawable drawable(parent.get(), &canvas, true); 296 canvas.drawDrawable(&drawable); 297 EXPECT_EQ(3, canvas.getIndex()); 298} 299 300RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) { 301 /* R is backward projected on B and C is a layer. 302 A 303 / \ 304 B C 305 | 306 R 307 */ 308 static const int SCROLL_X = 5; 309 static const int SCROLL_Y = 10; 310 static const int CANVAS_WIDTH = 400; 311 static const int CANVAS_HEIGHT = 400; 312 static const int LAYER_WIDTH = 200; 313 static const int LAYER_HEIGHT = 200; 314 class ProjectionTestCanvas : public SkCanvas { 315 public: 316 ProjectionTestCanvas(int* drawCounter) 317 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) 318 , mDrawCounter(drawCounter) 319 {} 320 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, 321 const SkPaint&) override { 322 EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer 323 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), getBounds(this)); 324 } 325 void onDrawRect(const SkRect& rect, const SkPaint& paint) override { 326 EXPECT_EQ(1, (*mDrawCounter)++); 327 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this)); 328 } 329 void onDrawOval(const SkRect&, const SkPaint&) override { 330 EXPECT_EQ(2, (*mDrawCounter)++); 331 SkMatrix expectedMatrix; 332 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y); 333 EXPECT_EQ(expectedMatrix, getTotalMatrix()); 334 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), getLocalBounds(this)); 335 } 336 int* mDrawCounter; 337 }; 338 339 class ProjectionLayer : public SkSurface_Base { 340 public: 341 ProjectionLayer(int* drawCounter) 342 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr) 343 , mDrawCounter(drawCounter) { 344 } 345 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override { 346 EXPECT_EQ(3, (*mDrawCounter)++); 347 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X, 348 300 - SCROLL_Y), getBounds(this->getCanvas())); 349 } 350 SkCanvas* onNewCanvas() override { 351 return new ProjectionTestCanvas(mDrawCounter); 352 } 353 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { 354 return sk_sp<SkSurface>(); 355 } 356 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override { 357 return sk_sp<SkImage>(); 358 } 359 void onCopyOnWrite(ContentChangeMode) override {} 360 int* mDrawCounter; 361 }; 362 363 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, 364 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 365 properties.setProjectionReceiver(true); 366 // scroll doesn't apply to background, so undone via translationX/Y 367 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 368 properties.setTranslationX(SCROLL_X); 369 properties.setTranslationY(SCROLL_Y); 370 371 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); 372 }, "B"); //B 373 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 374 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 375 properties.setProjectBackwards(true); 376 properties.setClipToBounds(false); 377 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 378 }, "R"); //R 379 auto child = TestUtils::createSkiaNode(100, 100, 300, 300, 380 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 381 canvas.drawRenderNode(projectingRipple.get()); 382 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint()); 383 }, "C"); //C 384 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, 385 [&receiverBackground, &child](RenderProperties& properties, 386 SkiaRecordingCanvas& canvas) { 387 // Set a rect outline for the projecting ripple to be masked against. 388 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 389 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally) 390 canvas.drawRenderNode(receiverBackground.get()); 391 canvas.drawRenderNode(child.get()); 392 }, "A"); //A 393 394 //prepareTree is required to find, which receivers have backward projected nodes 395 ContextFactory contextFactory; 396 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( 397 renderThread, false, parent.get(), &contextFactory)); 398 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); 399 DamageAccumulator damageAccumulator; 400 info.damageAccumulator = &damageAccumulator; 401 info.observer = nullptr; 402 parent->prepareTree(info); 403 404 int drawCounter = 0; 405 //set a layer after prepareTree to avoid layer logic there 406 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); 407 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter)); 408 child->setLayerSurface(surfaceLayer1); 409 Matrix4 windowTransform; 410 windowTransform.loadTranslate(100, 100, 0); 411 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform); 412 413 LayerUpdateQueue layerUpdateQueue; 414 layerUpdateQueue.enqueueLayerWithDamage(child.get(), 415 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT)); 416 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true); 417 EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer 418 419 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true); 420 surfaceLayer1->getCanvas()->drawDrawable(&drawable); 421 EXPECT_EQ(4, drawCounter); 422 423 // clean up layer pointer, so we can safely destruct RenderNode 424 child->setLayerSurface(nullptr); 425} 426 427RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { 428 /* R is backward projected on B. 429 A 430 / \ 431 B C 432 | 433 R 434 */ 435 static const int SCROLL_X = 500000; 436 static const int SCROLL_Y = 0; 437 static const int CANVAS_WIDTH = 400; 438 static const int CANVAS_HEIGHT = 400; 439 class ProjectionChildScrollTestCanvas : public SkCanvas { 440 public: 441 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {} 442 void onDrawRect(const SkRect& rect, const SkPaint& paint) override { 443 EXPECT_EQ(0, mIndex++); 444 EXPECT_TRUE(getTotalMatrix().isIdentity()); 445 } 446 void onDrawOval(const SkRect&, const SkPaint&) override { 447 EXPECT_EQ(1, mIndex++); 448 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this)); 449 EXPECT_TRUE(getTotalMatrix().isIdentity()); 450 } 451 int mIndex = 0; 452 }; 453 454 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, 455 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 456 properties.setProjectionReceiver(true); 457 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); 458 }, "B"); //B 459 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200, 460 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 461 // scroll doesn't apply to background, so undone via translationX/Y 462 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 463 properties.setTranslationX(SCROLL_X); 464 properties.setTranslationY(SCROLL_Y); 465 properties.setProjectBackwards(true); 466 properties.setClipToBounds(false); 467 canvas.drawOval(0, 0, 200, 200, SkPaint()); 468 }, "R"); //R 469 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, 470 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { 471 // Record time clip will be ignored by projectee 472 canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp); 473 474 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally) 475 canvas.drawRenderNode(projectingRipple.get()); 476 }, "C"); //C 477 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, 478 [&receiverBackground, &child](RenderProperties& properties, 479 SkiaRecordingCanvas& canvas) { 480 canvas.drawRenderNode(receiverBackground.get()); 481 canvas.drawRenderNode(child.get()); 482 }, "A"); //A 483 484 //prepareTree is required to find, which receivers have backward projected nodes 485 ContextFactory contextFactory; 486 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( 487 renderThread, false, parent.get(), &contextFactory)); 488 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); 489 DamageAccumulator damageAccumulator; 490 info.damageAccumulator = &damageAccumulator; 491 info.observer = nullptr; 492 parent->prepareTree(info); 493 494 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas()); 495 RenderNodeDrawable drawable(parent.get(), canvas.get(), true); 496 canvas->drawDrawable(&drawable); 497 EXPECT_EQ(2, canvas->mIndex); 498} 499 500namespace { 501static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) 502{ 503 ContextFactory contextFactory; 504 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( 505 renderThread, false, renderNode.get(), &contextFactory)); 506 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); 507 DamageAccumulator damageAccumulator; 508 info.damageAccumulator = &damageAccumulator; 509 info.observer = nullptr; 510 renderNode->prepareTree(info); 511 512 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection 513 ZReorderCanvas canvas(100, 100); 514 RenderNodeDrawable drawable(renderNode.get(), &canvas, false); 515 canvas.drawDrawable(&drawable); 516 return canvas.getIndex(); 517} 518} 519 520RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) { 521 /* R is backward projected on B 522 A 523 / \ 524 B C 525 | 526 R 527 */ 528 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 529 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 530 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 531 props.setProjectionReceiver(true); 532 } ); //nodeB 533 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 534 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 535 props.setProjectBackwards(true); 536 props.setClipToBounds(false); 537 } ); //nodeR 538 } ); //nodeC 539 }); //nodeA 540 EXPECT_EQ(3, drawNode(renderThread, nodeA)); 541} 542 543RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) { 544 /* R is backward projected on E 545 A 546 / | \ 547 / | \ 548 B C E 549 | 550 R 551 */ 552 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 553 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 554 drawOrderedNode(&canvas, 0, nullptr); //nodeB 555 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 556 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2 557 props.setProjectBackwards(true); 558 props.setClipToBounds(false); 559 } ); //nodeR 560 } ); //nodeC 561 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3 562 props.setProjectionReceiver(true); 563 } ); //nodeE 564 }); //nodeA 565 EXPECT_EQ(4, drawNode(renderThread, nodeA)); 566} 567 568RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) { 569 /* R is backward projected without receiver 570 A 571 / \ 572 B C 573 | 574 R 575 */ 576 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 577 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 578 drawOrderedNode(&canvas, 0, nullptr); //nodeB 579 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 580 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 581 //not having a projection receiver is an undefined behavior 582 props.setProjectBackwards(true); 583 props.setClipToBounds(false); 584 } ); //nodeR 585 } ); //nodeC 586 }); //nodeA 587 EXPECT_EQ(2, drawNode(renderThread, nodeA)); 588} 589 590RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) { 591 /* R is backward projected on C 592 A 593 / \ 594 B C 595 | 596 R 597 */ 598 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 599 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 600 drawOrderedNode(&canvas, 0, nullptr); //nodeB 601 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 602 props.setProjectionReceiver(true); 603 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 604 props.setProjectBackwards(true); 605 props.setClipToBounds(false); 606 } ); //nodeR 607 } ); //nodeC 608 }); //nodeA 609 EXPECT_EQ(3, drawNode(renderThread, nodeA)); 610} 611 612RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) { 613 /* R is backward projected on R 614 A 615 / \ 616 B C 617 | 618 R 619 */ 620 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 621 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 622 drawOrderedNode(&canvas, 0, nullptr); //nodeB 623 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 624 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 625 //having a node that is projected on itself is an undefined/unexpected behavior 626 props.setProjectionReceiver(true); 627 props.setProjectBackwards(true); 628 props.setClipToBounds(false); 629 } ); //nodeR 630 } ); //nodeC 631 }); //nodeA 632 EXPECT_EQ(2, drawNode(renderThread, nodeA)); 633} 634 635//Note: the outcome for this test is different in HWUI 636RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) { 637 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. 638 A 639 /|\ 640 / | \ 641 B C R 642 */ 643 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 644 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 645 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 646 props.setProjectionReceiver(true); 647 } ); //nodeB 648 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 649 } ); //nodeC 650 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 651 props.setProjectBackwards(true); 652 props.setClipToBounds(false); 653 } ); //nodeR 654 }); //nodeA 655 EXPECT_EQ(2, drawNode(renderThread, nodeA)); 656} 657 658RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) { 659 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. 660 A 661 | 662 G 663 /|\ 664 / | \ 665 B C R 666 */ 667 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 668 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 669 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 670 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 671 props.setProjectionReceiver(true); 672 } ); //nodeB 673 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 674 } ); //nodeC 675 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 676 props.setProjectBackwards(true); 677 props.setClipToBounds(false); 678 } ); //nodeR 679 } ); //nodeG 680 }); //nodeA 681 EXPECT_EQ(3, drawNode(renderThread, nodeA)); 682} 683 684RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) { 685 /* R is backward projected on B 686 A 687 | 688 B 689 | 690 C 691 | 692 R 693 */ 694 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 695 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 696 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 697 props.setProjectionReceiver(true); 698 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 699 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 700 props.setProjectBackwards(true); 701 props.setClipToBounds(false); 702 } ); //nodeR 703 } ); //nodeC 704 } ); //nodeB 705 }); //nodeA 706 EXPECT_EQ(3, drawNode(renderThread, nodeA)); 707} 708 709RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) { 710 /* B and G are receivables, R is backward projected 711 A 712 / \ 713 B C 714 / \ 715 G R 716 */ 717 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 718 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 719 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B 720 props.setProjectionReceiver(true); 721 } ); //nodeB 722 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C 723 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G 724 props.setProjectionReceiver(true); 725 } ); //nodeG 726 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R 727 props.setProjectBackwards(true); 728 props.setClipToBounds(false); 729 } ); //nodeR 730 } ); //nodeC 731 }); //nodeA 732 EXPECT_EQ(4, drawNode(renderThread, nodeA)); 733} 734 735RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) { 736 /* B and G are receivables, G is backward projected 737 A 738 / \ 739 B C 740 / \ 741 G R 742 */ 743 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 744 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 745 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B 746 props.setProjectionReceiver(true); 747 } ); //nodeB 748 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C 749 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G 750 props.setProjectionReceiver(true); 751 props.setProjectBackwards(true); 752 props.setClipToBounds(false); 753 } ); //nodeG 754 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R 755 } ); //nodeR 756 } ); //nodeC 757 }); //nodeA 758 EXPECT_EQ(4, drawNode(renderThread, nodeA)); 759} 760 761RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) { 762 /* B and G are receivables, R is backward projected 763 A 764 / \ 765 B C 766 / \ 767 G D 768 | 769 R 770 */ 771 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, 772 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { 773 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B 774 props.setProjectionReceiver(true); 775 } ); //nodeB 776 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C 777 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G 778 props.setProjectionReceiver(true); 779 } ); //nodeG 780 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D 781 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R 782 props.setProjectBackwards(true); 783 props.setClipToBounds(false); 784 } ); //nodeR 785 } ); //nodeD 786 } ); //nodeC 787 }); //nodeA 788 EXPECT_EQ(5, drawNode(renderThread, nodeA)); 789} 790