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