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