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