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