FrameBuilderTests.cpp revision 834653bcf80a3879c4d80004469053f205d45b69
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 19#include <BakedOpState.h> 20#include <DeferredLayerUpdater.h> 21#include <FrameBuilder.h> 22#include <LayerUpdateQueue.h> 23#include <RecordedOp.h> 24#include <RecordingCanvas.h> 25#include <tests/common/TestUtils.h> 26 27#include <unordered_map> 28 29namespace android { 30namespace uirenderer { 31 32const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; 33 34/** 35 * Virtual class implemented by each test to redirect static operation / state transitions to 36 * virtual methods. 37 * 38 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests), 39 * and allows Renderer vs Dispatching behavior to be merged. 40 * 41 * onXXXOp methods fail by default - tests should override ops they expect 42 * startRepaintLayer fails by default - tests should override if expected 43 * startFrame/endFrame do nothing by default - tests should override to intercept 44 */ 45class TestRendererBase { 46public: 47 virtual ~TestRendererBase() {} 48 virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) { 49 ADD_FAILURE() << "Temporary layers not expected in this test"; 50 return nullptr; 51 } 52 virtual void recycleTemporaryLayer(OffscreenBuffer*) { 53 ADD_FAILURE() << "Temporary layers not expected in this test"; 54 } 55 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) { 56 ADD_FAILURE() << "Layer repaint not expected in this test"; 57 } 58 virtual void endLayer() { 59 ADD_FAILURE() << "Layer updates not expected in this test"; 60 } 61 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {} 62 virtual void endFrame(const Rect& repaintRect) {} 63 64 // define virtual defaults for single draw methods 65#define X(Type) \ 66 virtual void on##Type(const Type&, const BakedOpState&) { \ 67 ADD_FAILURE() << #Type " not expected in this test"; \ 68 } 69 MAP_RENDERABLE_OPS(X) 70#undef X 71 72 // define virtual defaults for merged draw methods 73#define X(Type) \ 74 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \ 75 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \ 76 } 77 MAP_MERGEABLE_OPS(X) 78#undef X 79 80 int getIndex() { return mIndex; } 81 82protected: 83 int mIndex = 0; 84}; 85 86/** 87 * Dispatches all static methods to similar formed methods on renderer, which fail by default but 88 * are overridden by subclasses per test. 89 */ 90class TestDispatcher { 91public: 92 // define single op methods, which redirect to TestRendererBase 93#define X(Type) \ 94 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \ 95 renderer.on##Type(op, state); \ 96 } 97 MAP_RENDERABLE_OPS(X); 98#undef X 99 100 // define merged op methods, which redirect to TestRendererBase 101#define X(Type) \ 102 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \ 103 renderer.onMerged##Type##s(opList); \ 104 } 105 MAP_MERGEABLE_OPS(X); 106#undef X 107}; 108 109class FailRenderer : public TestRendererBase {}; 110 111RENDERTHREAD_TEST(FrameBuilder, simple) { 112 class SimpleTestRenderer : public TestRendererBase { 113 public: 114 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 115 EXPECT_EQ(0, mIndex++); 116 EXPECT_EQ(100u, width); 117 EXPECT_EQ(200u, height); 118 } 119 void onRectOp(const RectOp& op, const BakedOpState& state) override { 120 EXPECT_EQ(1, mIndex++); 121 } 122 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 123 EXPECT_EQ(2, mIndex++); 124 } 125 void endFrame(const Rect& repaintRect) override { 126 EXPECT_EQ(3, mIndex++); 127 } 128 }; 129 130 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200, 131 [](RenderProperties& props, RecordingCanvas& canvas) { 132 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); 133 canvas.drawRect(0, 0, 100, 200, SkPaint()); 134 canvas.drawBitmap(*bitmap, 10, 10, nullptr); 135 }); 136 FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, 137 sLightGeometry, Caches::getInstance()); 138 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 139 140 SimpleTestRenderer renderer; 141 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 142 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end 143} 144 145RENDERTHREAD_TEST(FrameBuilder, simpleStroke) { 146 class SimpleStrokeTestRenderer : public TestRendererBase { 147 public: 148 void onPointsOp(const PointsOp& op, const BakedOpState& state) override { 149 EXPECT_EQ(0, mIndex++); 150 // even though initial bounds are empty... 151 EXPECT_TRUE(op.unmappedBounds.isEmpty()) 152 << "initial bounds should be empty, since they're unstroked"; 153 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds) 154 << "final bounds should account for stroke"; 155 } 156 }; 157 158 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200, 159 [](RenderProperties& props, RecordingCanvas& canvas) { 160 SkPaint strokedPaint; 161 strokedPaint.setStrokeWidth(10); 162 canvas.drawPoint(50, 50, strokedPaint); 163 }); 164 FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, 165 sLightGeometry, Caches::getInstance()); 166 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 167 168 SimpleStrokeTestRenderer renderer; 169 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 170 EXPECT_EQ(1, renderer.getIndex()); 171} 172 173RENDERTHREAD_TEST(FrameBuilder, simpleRejection) { 174 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 175 [](RenderProperties& props, RecordingCanvas& canvas) { 176 canvas.save(SaveFlags::MatrixClip); 177 canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty 178 canvas.drawRect(0, 0, 400, 400, SkPaint()); 179 canvas.restore(); 180 }); 181 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 182 sLightGeometry, Caches::getInstance()); 183 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 184 185 FailRenderer renderer; 186 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 187} 188 189RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { 190 const int LOOPS = 5; 191 class SimpleBatchingTestRenderer : public TestRendererBase { 192 public: 193 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 194 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects"; 195 } 196 void onRectOp(const RectOp& op, const BakedOpState& state) override { 197 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps"; 198 } 199 }; 200 201 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 202 [](RenderProperties& props, RecordingCanvas& canvas) { 203 204 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10, 205 kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap 206 207 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. 208 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. 209 canvas.save(SaveFlags::MatrixClip); 210 for (int i = 0; i < LOOPS; i++) { 211 canvas.translate(0, 10); 212 canvas.drawRect(0, 0, 10, 10, SkPaint()); 213 canvas.drawBitmap(*bitmap, 5, 0, nullptr); 214 } 215 canvas.restore(); 216 }); 217 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 218 sLightGeometry, Caches::getInstance()); 219 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 220 221 SimpleBatchingTestRenderer renderer; 222 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 223 EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 224 << "Expect number of ops = 2 * loop count"; 225} 226 227RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) { 228 class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase { 229 public: 230 void onRectOp(const RectOp& op, const BakedOpState& state) override { 231 EXPECT_EQ(0, mIndex++); 232 EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds); 233 EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, 234 state.computedState.clipSideFlags); 235 } 236 }; 237 238 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 239 [](RenderProperties& props, RecordingCanvas& canvas) { 240 canvas.drawRect(0, 0, 100, 100, SkPaint()); 241 }); 242 243 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 244 sLightGeometry, Caches::getInstance()); 245 frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node 246 *TestUtils::getSyncedNode(node)); 247 248 DeferRenderNodeTranslateClipTestRenderer renderer; 249 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 250 EXPECT_EQ(1, renderer.getIndex()); 251} 252 253RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) { 254 class DeferRenderNodeSceneTestRenderer : public TestRendererBase { 255 public: 256 void onRectOp(const RectOp& op, const BakedOpState& state) override { 257 const Rect& clippedBounds = state.computedState.clippedBounds; 258 Matrix4 expected; 259 switch (mIndex++) { 260 case 0: 261 // background - left side 262 EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds); 263 expected.loadTranslate(100, 100, 0); 264 break; 265 case 1: 266 // background - top side 267 EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds); 268 expected.loadTranslate(100, 100, 0); 269 break; 270 case 2: 271 // content 272 EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds); 273 expected.loadTranslate(-50, -50, 0); 274 break; 275 case 3: 276 // overlay 277 EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds); 278 break; 279 default: 280 ADD_FAILURE() << "Too many rects observed"; 281 } 282 EXPECT_EQ(expected, state.computedState.transform); 283 } 284 }; 285 286 std::vector<sp<RenderNode>> nodes; 287 SkPaint transparentPaint; 288 transparentPaint.setAlpha(128); 289 290 // backdrop 291 nodes.push_back(TestUtils::createNode<RecordingCanvas>(100, 100, 700, 500, // 600x400 292 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 293 canvas.drawRect(0, 0, 600, 400, transparentPaint); 294 })); 295 296 // content 297 Rect contentDrawBounds(150, 150, 650, 450); // 500x300 298 nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600, 299 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 300 canvas.drawRect(0, 0, 800, 600, transparentPaint); 301 })); 302 303 // overlay 304 nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600, 305 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 306 canvas.drawRect(0, 0, 800, 200, transparentPaint); 307 })); 308 309 for (auto& node : nodes) { 310 TestUtils::syncHierarchyPropertiesAndDisplayList(node); 311 } 312 313 FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, 314 sLightGeometry, Caches::getInstance()); 315 frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds); 316 317 DeferRenderNodeSceneTestRenderer renderer; 318 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 319 EXPECT_EQ(4, renderer.getIndex()); 320} 321 322RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { 323 class EmptyNoFbo0TestRenderer : public TestRendererBase { 324 public: 325 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 326 ADD_FAILURE() << "Primary frame draw not expected in this test"; 327 } 328 void endFrame(const Rect& repaintRect) override { 329 ADD_FAILURE() << "Primary frame draw not expected in this test"; 330 } 331 }; 332 333 // Use layer update constructor, so no work is enqueued for Fbo0 334 LayerUpdateQueue emptyLayerUpdateQueue; 335 FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance()); 336 EmptyNoFbo0TestRenderer renderer; 337 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 338} 339 340RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { 341 class EmptyWithFbo0TestRenderer : public TestRendererBase { 342 public: 343 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 344 EXPECT_EQ(0, mIndex++); 345 } 346 void endFrame(const Rect& repaintRect) override { 347 EXPECT_EQ(1, mIndex++); 348 } 349 }; 350 auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, 351 [](RenderProperties& props, RecordingCanvas& canvas) { 352 // no drawn content 353 }); 354 355 // Draw, but pass node without draw content, so no work is done for primary frame 356 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 357 sLightGeometry, Caches::getInstance()); 358 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 359 360 EmptyWithFbo0TestRenderer renderer; 361 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 362 EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," 363 " but fbo0 update lifecycle should still be observed"; 364} 365 366RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { 367 class AvoidOverdrawRectsTestRenderer : public TestRendererBase { 368 public: 369 void onRectOp(const RectOp& op, const BakedOpState& state) override { 370 EXPECT_EQ(mIndex++, 0) << "Should be one rect"; 371 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds) 372 << "Last rect should occlude others."; 373 } 374 }; 375 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 376 [](RenderProperties& props, RecordingCanvas& canvas) { 377 canvas.drawRect(0, 0, 200, 200, SkPaint()); 378 canvas.drawRect(0, 0, 200, 200, SkPaint()); 379 canvas.drawRect(10, 10, 190, 190, SkPaint()); 380 }); 381 382 // Damage (and therefore clip) is same as last draw, subset of renderable area. 383 // This means last op occludes other contents, and they'll be rejected to avoid overdraw. 384 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, 385 sLightGeometry, Caches::getInstance()); 386 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 387 388 EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) 389 << "Recording must not have rejected ops, in order for this test to be valid"; 390 391 AvoidOverdrawRectsTestRenderer renderer; 392 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 393 EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op"; 394} 395 396RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { 397 static sk_sp<Bitmap> opaqueBitmap(TestUtils::createBitmap(50, 50, 398 SkColorType::kRGB_565_SkColorType)); 399 static sk_sp<Bitmap> transpBitmap(TestUtils::createBitmap(50, 50, 400 SkColorType::kAlpha_8_SkColorType)); 401 class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { 402 public: 403 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 404 switch(mIndex++) { 405 case 0: 406 EXPECT_EQ(opaqueBitmap.get(), op.bitmap); 407 break; 408 case 1: 409 EXPECT_EQ(transpBitmap.get(), op.bitmap); 410 break; 411 default: 412 ADD_FAILURE() << "Only two ops expected."; 413 } 414 } 415 }; 416 417 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 50, 50, 418 [](RenderProperties& props, RecordingCanvas& canvas) { 419 canvas.drawRect(0, 0, 50, 50, SkPaint()); 420 canvas.drawRect(0, 0, 50, 50, SkPaint()); 421 canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); 422 423 // only the below draws should remain, since they're 424 canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr); 425 canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); 426 }); 427 FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, 428 sLightGeometry, Caches::getInstance()); 429 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 430 431 EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) 432 << "Recording must not have rejected ops, in order for this test to be valid"; 433 434 AvoidOverdrawBitmapsTestRenderer renderer; 435 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 436 EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops"; 437} 438 439RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { 440 class ClippedMergingTestRenderer : public TestRendererBase { 441 public: 442 void onMergedBitmapOps(const MergedBakedOpList& opList) override { 443 EXPECT_EQ(0, mIndex); 444 mIndex += opList.count; 445 EXPECT_EQ(4u, opList.count); 446 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip); 447 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right, 448 opList.clipSideFlags); 449 } 450 }; 451 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 452 [](RenderProperties& props, RecordingCanvas& canvas) { 453 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20)); 454 455 // left side clipped (to inset left half) 456 canvas.save(SaveFlags::MatrixClip); 457 canvas.clipRect(10, 0, 50, 100, SkClipOp::kIntersect); 458 canvas.drawBitmap(*bitmap, 0, 40, nullptr); 459 canvas.restore(); 460 461 // top side clipped (to inset top half) 462 canvas.save(SaveFlags::MatrixClip); 463 canvas.clipRect(0, 10, 100, 50, SkClipOp::kIntersect); 464 canvas.drawBitmap(*bitmap, 40, 0, nullptr); 465 canvas.restore(); 466 467 // right side clipped (to inset right half) 468 canvas.save(SaveFlags::MatrixClip); 469 canvas.clipRect(50, 0, 90, 100, SkClipOp::kIntersect); 470 canvas.drawBitmap(*bitmap, 80, 40, nullptr); 471 canvas.restore(); 472 473 // bottom not clipped, just abutting (inset bottom half) 474 canvas.save(SaveFlags::MatrixClip); 475 canvas.clipRect(0, 50, 100, 90, SkClipOp::kIntersect); 476 canvas.drawBitmap(*bitmap, 40, 70, nullptr); 477 canvas.restore(); 478 }); 479 480 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 481 sLightGeometry, Caches::getInstance()); 482 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 483 484 ClippedMergingTestRenderer renderer; 485 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 486 EXPECT_EQ(4, renderer.getIndex()); 487} 488 489RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) { 490 class RegionClipStopsMergeTestRenderer : public TestRendererBase { 491 public: 492 void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; } 493 }; 494 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 495 [](RenderProperties& props, RecordingCanvas& canvas) { 496 SkPath path; 497 path.addCircle(200, 200, 200, SkPath::kCW_Direction); 498 canvas.save(SaveFlags::MatrixClip); 499 canvas.clipPath(&path, SkClipOp::kIntersect); 500 SkPaint paint; 501 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 502 paint.setAntiAlias(true); 503 paint.setTextSize(50); 504 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); 505 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200); 506 canvas.restore(); 507 }); 508 509 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 510 sLightGeometry, Caches::getInstance()); 511 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 512 513 RegionClipStopsMergeTestRenderer renderer; 514 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 515 EXPECT_EQ(2, renderer.getIndex()); 516} 517 518RENDERTHREAD_TEST(FrameBuilder, textMerging) { 519 class TextMergingTestRenderer : public TestRendererBase { 520 public: 521 void onMergedTextOps(const MergedBakedOpList& opList) override { 522 EXPECT_EQ(0, mIndex); 523 mIndex += opList.count; 524 EXPECT_EQ(2u, opList.count); 525 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags); 526 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags); 527 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags); 528 } 529 }; 530 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 531 [](RenderProperties& props, RecordingCanvas& canvas) { 532 SkPaint paint; 533 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 534 paint.setAntiAlias(true); 535 paint.setTextSize(50); 536 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped 537 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped 538 }); 539 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 540 sLightGeometry, Caches::getInstance()); 541 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 542 543 TextMergingTestRenderer renderer; 544 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 545 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; 546} 547 548RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { 549 const int LOOPS = 5; 550 class TextStrikethroughTestRenderer : public TestRendererBase { 551 public: 552 void onRectOp(const RectOp& op, const BakedOpState& state) override { 553 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text"; 554 } 555 void onMergedTextOps(const MergedBakedOpList& opList) override { 556 EXPECT_EQ(0, mIndex); 557 mIndex += opList.count; 558 EXPECT_EQ(5u, opList.count); 559 } 560 }; 561 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 2000, 562 [](RenderProperties& props, RecordingCanvas& canvas) { 563 SkPaint textPaint; 564 textPaint.setAntiAlias(true); 565 textPaint.setTextSize(20); 566 textPaint.setStrikeThruText(true); 567 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 568 for (int i = 0; i < LOOPS; i++) { 569 TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); 570 } 571 }); 572 573 FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, 574 sLightGeometry, Caches::getInstance()); 575 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 576 577 TextStrikethroughTestRenderer renderer; 578 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 579 EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 580 << "Expect number of ops = 2 * loop count"; 581} 582 583static auto styles = { 584 SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style }; 585 586RENDERTHREAD_TEST(FrameBuilder, textStyle) { 587 class TextStyleTestRenderer : public TestRendererBase { 588 public: 589 void onMergedTextOps(const MergedBakedOpList& opList) override { 590 ASSERT_EQ(0, mIndex); 591 ASSERT_EQ(3u, opList.count); 592 mIndex += opList.count; 593 594 int index = 0; 595 for (auto style : styles) { 596 auto state = opList.states[index++]; 597 ASSERT_EQ(style, state->op->paint->getStyle()) 598 << "Remainder of validation relies upon stable merged order"; 599 ASSERT_EQ(0, state->computedState.clipSideFlags) 600 << "Clipped bounds validation requires unclipped ops"; 601 } 602 603 Rect fill = opList.states[0]->computedState.clippedBounds; 604 Rect stroke = opList.states[1]->computedState.clippedBounds; 605 EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds) 606 << "Stroke+Fill should be same as stroke"; 607 608 EXPECT_TRUE(stroke.contains(fill)); 609 EXPECT_FALSE(fill.contains(stroke)); 610 611 // outset by half the stroke width 612 Rect outsetFill(fill); 613 outsetFill.outset(5); 614 EXPECT_EQ(stroke, outsetFill); 615 } 616 }; 617 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 618 [](RenderProperties& props, RecordingCanvas& canvas) { 619 SkPaint paint; 620 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 621 paint.setAntiAlias(true); 622 paint.setTextSize(50); 623 paint.setStrokeWidth(10); 624 625 // draw 3 copies of the same text overlapping, each with a different style. 626 // They'll get merged, but with 627 for (auto style : styles) { 628 paint.setStyle(style); 629 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); 630 } 631 }); 632 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 633 sLightGeometry, Caches::getInstance()); 634 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 635 TextStyleTestRenderer renderer; 636 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 637 EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops"; 638} 639 640RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { 641 class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase { 642 public: 643 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 644 EXPECT_EQ(0, mIndex++); 645 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect()); 646 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds); 647 648 Matrix4 expected; 649 expected.loadTranslate(5, 5, 0); 650 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 651 } 652 }; 653 654 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 655 SkMatrix::MakeTrans(5, 5)); 656 657 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 658 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 659 canvas.save(SaveFlags::MatrixClip); 660 canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect); 661 canvas.drawLayer(layerUpdater.get()); 662 canvas.restore(); 663 }); 664 665 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 666 sLightGeometry, Caches::getInstance()); 667 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 668 669 TextureLayerClipLocalMatrixTestRenderer renderer; 670 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 671 EXPECT_EQ(1, renderer.getIndex()); 672} 673 674RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) { 675 class TextureLayerCombineMatricesTestRenderer : public TestRendererBase { 676 public: 677 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 678 EXPECT_EQ(0, mIndex++); 679 680 Matrix4 expected; 681 expected.loadTranslate(35, 45, 0); 682 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 683 } 684 }; 685 686 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 687 SkMatrix::MakeTrans(5, 5)); 688 689 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 690 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 691 canvas.save(SaveFlags::MatrixClip); 692 canvas.translate(30, 40); 693 canvas.drawLayer(layerUpdater.get()); 694 canvas.restore(); 695 }); 696 697 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 698 sLightGeometry, Caches::getInstance()); 699 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 700 701 TextureLayerCombineMatricesTestRenderer renderer; 702 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 703 EXPECT_EQ(1, renderer.getIndex()); 704} 705 706RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) { 707 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 708 SkMatrix::MakeTrans(5, 5)); 709 layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected 710 711 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 712 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 713 canvas.drawLayer(layerUpdater.get()); 714 }); 715 716 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 717 sLightGeometry, Caches::getInstance()); 718 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 719 720 FailRenderer renderer; 721 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 722} 723 724RENDERTHREAD_TEST(FrameBuilder, functor_reject) { 725 class FunctorTestRenderer : public TestRendererBase { 726 public: 727 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override { 728 EXPECT_EQ(0, mIndex++); 729 } 730 }; 731 Functor noopFunctor; 732 733 // 1 million pixel tall view, scrolled down 80% 734 auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 1000000, 735 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { 736 canvas.translate(0, -800000); 737 canvas.callDrawGLFunction(&noopFunctor, nullptr); 738 }); 739 740 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 741 sLightGeometry, Caches::getInstance()); 742 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView)); 743 744 FunctorTestRenderer renderer; 745 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 746 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected"; 747} 748 749RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) { 750 class ColorTestRenderer : public TestRendererBase { 751 public: 752 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 753 EXPECT_EQ(0, mIndex++); 754 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds) 755 << "Color op should be expanded to bounds of surrounding"; 756 } 757 }; 758 759 auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(0, 0, 10, 10, 760 [](RenderProperties& props, RecordingCanvas& canvas) { 761 props.setClipToBounds(false); 762 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 763 }); 764 765 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 766 sLightGeometry, Caches::getInstance()); 767 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView)); 768 769 ColorTestRenderer renderer; 770 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 771 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected"; 772} 773 774TEST(FrameBuilder, renderNode) { 775 class RenderNodeTestRenderer : public TestRendererBase { 776 public: 777 void onRectOp(const RectOp& op, const BakedOpState& state) override { 778 switch(mIndex++) { 779 case 0: 780 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 781 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 782 break; 783 case 1: 784 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds); 785 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 786 break; 787 default: 788 ADD_FAILURE(); 789 } 790 } 791 }; 792 793 auto child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, 794 [](RenderProperties& props, RecordingCanvas& canvas) { 795 SkPaint paint; 796 paint.setColor(SK_ColorWHITE); 797 canvas.drawRect(0, 0, 100, 100, paint); 798 }); 799 800 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 801 [&child](RenderProperties& props, RecordingCanvas& canvas) { 802 SkPaint paint; 803 paint.setColor(SK_ColorDKGRAY); 804 canvas.drawRect(0, 0, 200, 200, paint); 805 806 canvas.save(SaveFlags::MatrixClip); 807 canvas.translate(40, 40); 808 canvas.drawRenderNode(child.get()); 809 canvas.restore(); 810 }); 811 812 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 813 sLightGeometry, Caches::getInstance()); 814 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 815 816 RenderNodeTestRenderer renderer; 817 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 818 EXPECT_EQ(2, renderer.getIndex()); 819} 820 821RENDERTHREAD_TEST(FrameBuilder, clipped) { 822 class ClippedTestRenderer : public TestRendererBase { 823 public: 824 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 825 EXPECT_EQ(0, mIndex++); 826 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds); 827 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect()); 828 EXPECT_TRUE(state.computedState.transform.isIdentity()); 829 } 830 }; 831 832 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 833 [](RenderProperties& props, RecordingCanvas& canvas) { 834 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200)); 835 canvas.drawBitmap(*bitmap, 0, 0, nullptr); 836 }); 837 838 // clip to small area, should see in receiver 839 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200, 840 sLightGeometry, Caches::getInstance()); 841 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 842 843 ClippedTestRenderer renderer; 844 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 845} 846 847RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { 848 class SaveLayerSimpleTestRenderer : public TestRendererBase { 849 public: 850 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 851 EXPECT_EQ(0, mIndex++); 852 EXPECT_EQ(180u, width); 853 EXPECT_EQ(180u, height); 854 return nullptr; 855 } 856 void endLayer() override { 857 EXPECT_EQ(2, mIndex++); 858 } 859 void onRectOp(const RectOp& op, const BakedOpState& state) override { 860 EXPECT_EQ(1, mIndex++); 861 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds); 862 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds); 863 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect()); 864 865 Matrix4 expectedTransform; 866 expectedTransform.loadTranslate(-10, -10, 0); 867 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform); 868 } 869 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 870 EXPECT_EQ(3, mIndex++); 871 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 872 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 873 EXPECT_TRUE(state.computedState.transform.isIdentity()); 874 } 875 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 876 EXPECT_EQ(4, mIndex++); 877 EXPECT_EQ(nullptr, offscreenBuffer); 878 } 879 }; 880 881 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 882 [](RenderProperties& props, RecordingCanvas& canvas) { 883 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); 884 canvas.drawRect(10, 10, 190, 190, SkPaint()); 885 canvas.restore(); 886 }); 887 888 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 889 sLightGeometry, Caches::getInstance()); 890 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 891 892 SaveLayerSimpleTestRenderer renderer; 893 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 894 EXPECT_EQ(5, renderer.getIndex()); 895} 896 897RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { 898 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as: 899 * - startTemporaryLayer2, rect2 endLayer2 900 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1 901 * - startFrame, layerOp1, endFrame 902 */ 903 class SaveLayerNestedTestRenderer : public TestRendererBase { 904 public: 905 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 906 const int index = mIndex++; 907 if (index == 0) { 908 EXPECT_EQ(400u, width); 909 EXPECT_EQ(400u, height); 910 return (OffscreenBuffer*) 0x400; 911 } else if (index == 3) { 912 EXPECT_EQ(800u, width); 913 EXPECT_EQ(800u, height); 914 return (OffscreenBuffer*) 0x800; 915 } else { ADD_FAILURE(); } 916 return (OffscreenBuffer*) nullptr; 917 } 918 void endLayer() override { 919 int index = mIndex++; 920 EXPECT_TRUE(index == 2 || index == 6); 921 } 922 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 923 EXPECT_EQ(7, mIndex++); 924 } 925 void endFrame(const Rect& repaintRect) override { 926 EXPECT_EQ(9, mIndex++); 927 } 928 void onRectOp(const RectOp& op, const BakedOpState& state) override { 929 const int index = mIndex++; 930 if (index == 1) { 931 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect 932 } else if (index == 4) { 933 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect 934 } else { ADD_FAILURE(); } 935 } 936 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 937 const int index = mIndex++; 938 if (index == 5) { 939 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle); 940 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer 941 } else if (index == 8) { 942 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle); 943 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer 944 } else { ADD_FAILURE(); } 945 } 946 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 947 const int index = mIndex++; 948 // order isn't important, but we need to see both 949 if (index == 10) { 950 EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer); 951 } else if (index == 11) { 952 EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer); 953 } else { ADD_FAILURE(); } 954 } 955 }; 956 957 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 800, 800, 958 [](RenderProperties& props, RecordingCanvas& canvas) { 959 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); 960 { 961 canvas.drawRect(0, 0, 800, 800, SkPaint()); 962 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); 963 { 964 canvas.drawRect(0, 0, 400, 400, SkPaint()); 965 } 966 canvas.restore(); 967 } 968 canvas.restore(); 969 }); 970 971 FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800, 972 sLightGeometry, Caches::getInstance()); 973 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 974 975 SaveLayerNestedTestRenderer renderer; 976 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 977 EXPECT_EQ(12, renderer.getIndex()); 978} 979 980RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { 981 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 982 [](RenderProperties& props, RecordingCanvas& canvas) { 983 canvas.save(SaveFlags::MatrixClip); 984 canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); 985 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); 986 987 // draw within save layer may still be recorded, but shouldn't be drawn 988 canvas.drawRect(200, 200, 400, 400, SkPaint()); 989 990 canvas.restore(); 991 canvas.restore(); 992 }); 993 994 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 995 sLightGeometry, Caches::getInstance()); 996 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 997 998 FailRenderer renderer; 999 // should see no ops, even within the layer, since the layer should be rejected 1000 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1001} 1002 1003RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { 1004 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase { 1005 public: 1006 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1007 EXPECT_EQ(0, mIndex++); 1008 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 1009 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 1010 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1011 } 1012 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1013 EXPECT_EQ(1, mIndex++); 1014 ASSERT_NE(nullptr, op.paint); 1015 ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint)); 1016 } 1017 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1018 EXPECT_EQ(2, mIndex++); 1019 EXPECT_EQ(Rect(200, 200), op.unmappedBounds); 1020 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 1021 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 1022 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1023 } 1024 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1025 EXPECT_EQ(3, mIndex++); 1026 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 1027 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 1028 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1029 } 1030 }; 1031 1032 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1033 [](RenderProperties& props, RecordingCanvas& canvas) { 1034 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 1035 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1036 canvas.restore(); 1037 }); 1038 1039 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1040 sLightGeometry, Caches::getInstance()); 1041 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1042 1043 SaveLayerUnclippedSimpleTestRenderer renderer; 1044 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1045 EXPECT_EQ(4, renderer.getIndex()); 1046} 1047 1048RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) { 1049 class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase { 1050 public: 1051 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1052 EXPECT_EQ(0, mIndex++); 1053 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds) 1054 << "Bounds rect should round out"; 1055 } 1056 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {} 1057 void onRectOp(const RectOp& op, const BakedOpState& state) override {} 1058 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1059 EXPECT_EQ(1, mIndex++); 1060 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds) 1061 << "Bounds rect should round out"; 1062 } 1063 }; 1064 1065 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1066 [](RenderProperties& props, RecordingCanvas& canvas) { 1067 canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out 1068 128, (SaveFlags::Flags)(0)); 1069 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1070 canvas.restore(); 1071 }); 1072 1073 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1074 sLightGeometry, Caches::getInstance()); 1075 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1076 1077 SaveLayerUnclippedRoundTestRenderer renderer; 1078 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1079 EXPECT_EQ(2, renderer.getIndex()); 1080} 1081 1082RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { 1083 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase { 1084 public: 1085 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1086 int index = mIndex++; 1087 EXPECT_GT(4, index); 1088 EXPECT_EQ(5, op.unmappedBounds.getWidth()); 1089 EXPECT_EQ(5, op.unmappedBounds.getHeight()); 1090 if (index == 0) { 1091 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds); 1092 } else if (index == 1) { 1093 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds); 1094 } else if (index == 2) { 1095 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds); 1096 } else if (index == 3) { 1097 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds); 1098 } 1099 } 1100 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1101 EXPECT_EQ(4, mIndex++); 1102 ASSERT_EQ(op.vertexCount, 16u); 1103 for (size_t i = 0; i < op.vertexCount; i++) { 1104 auto v = op.vertices[i]; 1105 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200); 1106 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200); 1107 } 1108 } 1109 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1110 EXPECT_EQ(5, mIndex++); 1111 } 1112 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1113 EXPECT_LT(5, mIndex++); 1114 } 1115 }; 1116 1117 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1118 [](RenderProperties& props, RecordingCanvas& canvas) { 1119 1120 int restoreTo = canvas.save(SaveFlags::MatrixClip); 1121 canvas.scale(2, 2); 1122 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); 1123 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); 1124 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); 1125 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); 1126 canvas.drawRect(0, 0, 100, 100, SkPaint()); 1127 canvas.restoreToCount(restoreTo); 1128 }); 1129 1130 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1131 sLightGeometry, Caches::getInstance()); 1132 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1133 1134 SaveLayerUnclippedMergedClearsTestRenderer renderer; 1135 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1136 EXPECT_EQ(10, renderer.getIndex()) 1137 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect."; 1138} 1139 1140RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { 1141 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase { 1142 public: 1143 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1144 EXPECT_EQ(0, mIndex++); 1145 } 1146 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1147 EXPECT_EQ(1, mIndex++); 1148 ASSERT_NE(nullptr, op.paint); 1149 EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint)); 1150 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds) 1151 << "Expect dirty rect as clip"; 1152 ASSERT_NE(nullptr, state.computedState.clipState); 1153 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect); 1154 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1155 } 1156 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1157 EXPECT_EQ(2, mIndex++); 1158 } 1159 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1160 EXPECT_EQ(3, mIndex++); 1161 } 1162 }; 1163 1164 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1165 [](RenderProperties& props, RecordingCanvas& canvas) { 1166 // save smaller than clip, so we get unclipped behavior 1167 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 1168 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1169 canvas.restore(); 1170 }); 1171 1172 // draw with partial screen dirty, and assert we see that rect later 1173 FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, 1174 sLightGeometry, Caches::getInstance()); 1175 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1176 1177 SaveLayerUnclippedClearClipTestRenderer renderer; 1178 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1179 EXPECT_EQ(4, renderer.getIndex()); 1180} 1181 1182RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { 1183 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1184 [](RenderProperties& props, RecordingCanvas& canvas) { 1185 // unclipped savelayer + rect both in area that won't intersect with dirty 1186 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); 1187 canvas.drawRect(100, 100, 200, 200, SkPaint()); 1188 canvas.restore(); 1189 }); 1190 1191 // draw with partial screen dirty that doesn't intersect with savelayer 1192 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, 1193 sLightGeometry, Caches::getInstance()); 1194 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1195 1196 FailRenderer renderer; 1197 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1198} 1199 1200/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as: 1201 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer 1202 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe 1203 */ 1204RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { 1205 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { 1206 public: 1207 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1208 EXPECT_EQ(0, mIndex++); // savelayer first 1209 return (OffscreenBuffer*)0xabcd; 1210 } 1211 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1212 int index = mIndex++; 1213 EXPECT_TRUE(index == 1 || index == 7); 1214 } 1215 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1216 int index = mIndex++; 1217 EXPECT_TRUE(index == 2 || index == 8); 1218 } 1219 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1220 EXPECT_EQ(3, mIndex++); 1221 Matrix4 expected; 1222 expected.loadTranslate(-100, -100, 0); 1223 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds); 1224 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 1225 } 1226 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1227 int index = mIndex++; 1228 EXPECT_TRUE(index == 4 || index == 10); 1229 } 1230 void endLayer() override { 1231 EXPECT_EQ(5, mIndex++); 1232 } 1233 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1234 EXPECT_EQ(6, mIndex++); 1235 } 1236 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1237 EXPECT_EQ(9, mIndex++); 1238 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1239 } 1240 void endFrame(const Rect& repaintRect) override { 1241 EXPECT_EQ(11, mIndex++); 1242 } 1243 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1244 EXPECT_EQ(12, mIndex++); 1245 EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer); 1246 } 1247 }; 1248 1249 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 600, 600, // 500x500 triggers clipping 1250 [](RenderProperties& props, RecordingCanvas& canvas) { 1251 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped 1252 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped 1253 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped 1254 canvas.drawRect(200, 200, 300, 300, SkPaint()); 1255 canvas.restore(); 1256 canvas.restore(); 1257 canvas.restore(); 1258 }); 1259 1260 FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, 1261 sLightGeometry, Caches::getInstance()); 1262 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1263 1264 SaveLayerUnclippedComplexTestRenderer renderer; 1265 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1266 EXPECT_EQ(13, renderer.getIndex()); 1267} 1268 1269RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { 1270 class HwLayerSimpleTestRenderer : public TestRendererBase { 1271 public: 1272 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1273 EXPECT_EQ(0, mIndex++); 1274 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1275 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1276 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1277 } 1278 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1279 EXPECT_EQ(1, mIndex++); 1280 1281 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1282 << "Transform should be reset within layer"; 1283 1284 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1285 << "Damage rect should be used to clip layer content"; 1286 } 1287 void endLayer() override { 1288 EXPECT_EQ(2, mIndex++); 1289 } 1290 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1291 EXPECT_EQ(3, mIndex++); 1292 } 1293 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1294 EXPECT_EQ(4, mIndex++); 1295 } 1296 void endFrame(const Rect& repaintRect) override { 1297 EXPECT_EQ(5, mIndex++); 1298 } 1299 }; 1300 1301 auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, 1302 [](RenderProperties& props, RecordingCanvas& canvas) { 1303 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1304 SkPaint paint; 1305 paint.setColor(SK_ColorWHITE); 1306 canvas.drawRect(0, 0, 100, 100, paint); 1307 }); 1308 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1309 1310 // create RenderNode's layer here in same way prepareTree would 1311 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1312 *layerHandle = &layer; 1313 1314 auto syncedNode = TestUtils::getSyncedNode(node); 1315 1316 // only enqueue partial damage 1317 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1318 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1319 1320 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1321 sLightGeometry, Caches::getInstance()); 1322 frameBuilder.deferLayers(layerUpdateQueue); 1323 frameBuilder.deferRenderNode(*syncedNode); 1324 1325 HwLayerSimpleTestRenderer renderer; 1326 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1327 EXPECT_EQ(6, renderer.getIndex()); 1328 1329 // clean up layer pointer, so we can safely destruct RenderNode 1330 *layerHandle = nullptr; 1331} 1332 1333RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { 1334 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: 1335 * - startRepaintLayer(child), rect(grey), endLayer 1336 * - startTemporaryLayer, drawLayer(child), endLayer 1337 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer 1338 * - startFrame, drawLayer(parent), endLayerb 1339 */ 1340 class HwLayerComplexTestRenderer : public TestRendererBase { 1341 public: 1342 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1343 EXPECT_EQ(3, mIndex++); // savelayer first 1344 return (OffscreenBuffer*)0xabcd; 1345 } 1346 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1347 int index = mIndex++; 1348 if (index == 0) { 1349 // starting inner layer 1350 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1351 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1352 } else if (index == 6) { 1353 // starting outer layer 1354 EXPECT_EQ(200u, offscreenBuffer->viewportWidth); 1355 EXPECT_EQ(200u, offscreenBuffer->viewportHeight); 1356 } else { ADD_FAILURE(); } 1357 } 1358 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1359 int index = mIndex++; 1360 if (index == 1) { 1361 // inner layer's rect (white) 1362 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1363 } else if (index == 7) { 1364 // outer layer's rect (grey) 1365 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1366 } else { ADD_FAILURE(); } 1367 } 1368 void endLayer() override { 1369 int index = mIndex++; 1370 EXPECT_TRUE(index == 2 || index == 5 || index == 9); 1371 } 1372 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1373 EXPECT_EQ(10, mIndex++); 1374 } 1375 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1376 OffscreenBuffer* layer = *op.layerHandle; 1377 int index = mIndex++; 1378 if (index == 4) { 1379 EXPECT_EQ(100u, layer->viewportWidth); 1380 EXPECT_EQ(100u, layer->viewportHeight); 1381 } else if (index == 8) { 1382 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1383 } else if (index == 11) { 1384 EXPECT_EQ(200u, layer->viewportWidth); 1385 EXPECT_EQ(200u, layer->viewportHeight); 1386 } else { ADD_FAILURE(); } 1387 } 1388 void endFrame(const Rect& repaintRect) override { 1389 EXPECT_EQ(12, mIndex++); 1390 } 1391 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1392 EXPECT_EQ(13, mIndex++); 1393 } 1394 }; 1395 1396 auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, 1397 [](RenderProperties& props, RecordingCanvas& canvas) { 1398 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1399 SkPaint paint; 1400 paint.setColor(SK_ColorWHITE); 1401 canvas.drawRect(0, 0, 100, 100, paint); 1402 }); 1403 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1404 *(child->getLayerHandle()) = &childLayer; 1405 1406 RenderNode* childPtr = child.get(); 1407 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1408 [childPtr](RenderProperties& props, RecordingCanvas& canvas) { 1409 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1410 SkPaint paint; 1411 paint.setColor(SK_ColorDKGRAY); 1412 canvas.drawRect(0, 0, 200, 200, paint); 1413 1414 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); 1415 canvas.drawRenderNode(childPtr); 1416 canvas.restore(); 1417 }); 1418 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1419 *(parent->getLayerHandle()) = &parentLayer; 1420 1421 auto syncedNode = TestUtils::getSyncedNode(parent); 1422 1423 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1424 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); 1425 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); 1426 1427 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1428 sLightGeometry, Caches::getInstance()); 1429 frameBuilder.deferLayers(layerUpdateQueue); 1430 frameBuilder.deferRenderNode(*syncedNode); 1431 1432 HwLayerComplexTestRenderer renderer; 1433 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1434 EXPECT_EQ(14, renderer.getIndex()); 1435 1436 // clean up layer pointers, so we can safely destruct RenderNodes 1437 *(child->getLayerHandle()) = nullptr; 1438 *(parent->getLayerHandle()) = nullptr; 1439} 1440 1441 1442RENDERTHREAD_TEST(FrameBuilder, buildLayer) { 1443 class BuildLayerTestRenderer : public TestRendererBase { 1444 public: 1445 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1446 EXPECT_EQ(0, mIndex++); 1447 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1448 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1449 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1450 } 1451 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 1452 EXPECT_EQ(1, mIndex++); 1453 1454 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1455 << "Transform should be reset within layer"; 1456 1457 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1458 << "Damage rect should be used to clip layer content"; 1459 } 1460 void endLayer() override { 1461 EXPECT_EQ(2, mIndex++); 1462 } 1463 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1464 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1465 } 1466 void endFrame(const Rect& repaintRect) override { 1467 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1468 } 1469 }; 1470 1471 auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, 1472 [](RenderProperties& props, RecordingCanvas& canvas) { 1473 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1474 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 1475 }); 1476 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1477 1478 // create RenderNode's layer here in same way prepareTree would 1479 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1480 *layerHandle = &layer; 1481 1482 TestUtils::syncHierarchyPropertiesAndDisplayList(node); 1483 1484 // only enqueue partial damage 1485 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1486 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1487 1488 // Draw, but pass empty node list, so no work is done for primary frame 1489 FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance()); 1490 BuildLayerTestRenderer renderer; 1491 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1492 EXPECT_EQ(3, renderer.getIndex()); 1493 1494 // clean up layer pointer, so we can safely destruct RenderNode 1495 *layerHandle = nullptr; 1496} 1497 1498namespace { 1499 1500static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { 1501 SkPaint paint; 1502 // order put in blue channel, transparent so overlapped content doesn't get rejected 1503 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); 1504 canvas->drawRect(0, 0, 100, 100, paint); 1505} 1506static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) { 1507 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1508 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { 1509 drawOrderedRect(&canvas, expectedDrawOrder); 1510 }); 1511 node->mutateStagingProperties().setTranslationZ(z); 1512 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); 1513 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1514} 1515 1516static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, 1517 std::function<void(RenderProperties& props, RecordingCanvas& canvas)> setup) { 1518 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1519 [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) { 1520 drawOrderedRect(&canvas, expectedDrawOrder); 1521 if (setup) { 1522 setup(props, canvas); 1523 } 1524 }); 1525 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1526} 1527 1528class ZReorderTestRenderer : public TestRendererBase { 1529public: 1530 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1531 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel 1532 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 1533 } 1534}; 1535 1536} // end anonymous namespace 1537 1538RENDERTHREAD_TEST(FrameBuilder, zReorder) { 1539 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1540 [](RenderProperties& props, RecordingCanvas& canvas) { 1541 canvas.insertReorderBarrier(true); 1542 canvas.insertReorderBarrier(false); 1543 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 1544 drawOrderedRect(&canvas, 1); 1545 canvas.insertReorderBarrier(true); 1546 drawOrderedNode(&canvas, 6, 2.0f); 1547 drawOrderedRect(&canvas, 3); 1548 drawOrderedNode(&canvas, 4, 0.0f); 1549 drawOrderedRect(&canvas, 5); 1550 drawOrderedNode(&canvas, 2, -2.0f); 1551 drawOrderedNode(&canvas, 7, 2.0f); 1552 canvas.insertReorderBarrier(false); 1553 drawOrderedRect(&canvas, 8); 1554 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 1555 canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op 1556 drawOrderedRect(&canvas, 11); 1557 drawOrderedNode(&canvas, 10, -1.0f); 1558 canvas.insertReorderBarrier(false); 1559 canvas.insertReorderBarrier(true); //test with two empty reorder sections 1560 canvas.insertReorderBarrier(true); 1561 canvas.insertReorderBarrier(false); 1562 drawOrderedRect(&canvas, 12); 1563 }); 1564 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1565 sLightGeometry, Caches::getInstance()); 1566 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1567 1568 ZReorderTestRenderer renderer; 1569 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1570 EXPECT_EQ(13, renderer.getIndex()); 1571}; 1572 1573RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { 1574 static const int scrollX = 5; 1575 static const int scrollY = 10; 1576 class ProjectionReorderTestRenderer : public TestRendererBase { 1577 public: 1578 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1579 const int index = mIndex++; 1580 1581 Matrix4 expectedMatrix; 1582 switch (index) { 1583 case 0: 1584 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 1585 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1586 expectedMatrix.loadIdentity(); 1587 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1588 break; 1589 case 1: 1590 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 1591 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1592 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); 1593 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1594 EXPECT_EQ(Rect(-35, -30, 45, 50), 1595 Rect(state.computedState.localProjectionPathMask->getBounds())); 1596 break; 1597 case 2: 1598 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 1599 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 1600 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 1601 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1602 break; 1603 default: 1604 ADD_FAILURE(); 1605 } 1606 EXPECT_EQ(expectedMatrix, state.computedState.transform); 1607 } 1608 }; 1609 1610 /** 1611 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 1612 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 1613 * draw, but because it is projected backwards, it's drawn in between B and C. 1614 * 1615 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 1616 * (which isn't affected by scroll). 1617 */ 1618 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1619 [](RenderProperties& properties, RecordingCanvas& canvas) { 1620 properties.setProjectionReceiver(true); 1621 // scroll doesn't apply to background, so undone via translationX/Y 1622 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1623 properties.setTranslationX(scrollX); 1624 properties.setTranslationY(scrollY); 1625 1626 SkPaint paint; 1627 paint.setColor(SK_ColorWHITE); 1628 canvas.drawRect(0, 0, 100, 100, paint); 1629 }); 1630 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(50, 0, 100, 50, 1631 [](RenderProperties& properties, RecordingCanvas& canvas) { 1632 properties.setProjectBackwards(true); 1633 properties.setClipToBounds(false); 1634 SkPaint paint; 1635 paint.setColor(SK_ColorDKGRAY); 1636 canvas.drawRect(-10, -10, 60, 60, paint); 1637 }); 1638 auto child = TestUtils::createNode<RecordingCanvas>(0, 50, 100, 100, 1639 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1640 SkPaint paint; 1641 paint.setColor(SK_ColorBLUE); 1642 canvas.drawRect(0, 0, 100, 50, paint); 1643 canvas.drawRenderNode(projectingRipple.get()); 1644 }); 1645 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1646 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1647 // Set a rect outline for the projecting ripple to be masked against. 1648 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 1649 1650 canvas.save(SaveFlags::MatrixClip); 1651 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1652 canvas.drawRenderNode(receiverBackground.get()); 1653 canvas.drawRenderNode(child.get()); 1654 canvas.restore(); 1655 }); 1656 1657 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1658 sLightGeometry, Caches::getInstance()); 1659 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1660 1661 ProjectionReorderTestRenderer renderer; 1662 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1663 EXPECT_EQ(3, renderer.getIndex()); 1664} 1665 1666RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { 1667 static const int scrollX = 5; 1668 static const int scrollY = 10; 1669 class ProjectionHwLayerTestRenderer : public TestRendererBase { 1670 public: 1671 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1672 EXPECT_EQ(0, mIndex++); 1673 } 1674 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 1675 EXPECT_EQ(1, mIndex++); 1676 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1677 } 1678 void endLayer() override { 1679 EXPECT_EQ(2, mIndex++); 1680 } 1681 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1682 EXPECT_EQ(3, mIndex++); 1683 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1684 } 1685 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1686 EXPECT_EQ(4, mIndex++); 1687 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1688 Matrix4 expected; 1689 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); 1690 EXPECT_EQ(expected, state.computedState.transform); 1691 EXPECT_EQ(Rect(-85, -80, 295, 300), 1692 Rect(state.computedState.localProjectionPathMask->getBounds())); 1693 } 1694 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1695 EXPECT_EQ(5, mIndex++); 1696 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1697 } 1698 }; 1699 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1700 [](RenderProperties& properties, RecordingCanvas& canvas) { 1701 properties.setProjectionReceiver(true); 1702 // scroll doesn't apply to background, so undone via translationX/Y 1703 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1704 properties.setTranslationX(scrollX); 1705 properties.setTranslationY(scrollY); 1706 1707 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1708 }); 1709 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1710 [](RenderProperties& properties, RecordingCanvas& canvas) { 1711 properties.setProjectBackwards(true); 1712 properties.setClipToBounds(false); 1713 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 1714 }); 1715 auto child = TestUtils::createNode<RecordingCanvas>(100, 100, 300, 300, 1716 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1717 properties.mutateLayerProperties().setType(LayerType::RenderLayer); 1718 canvas.drawRenderNode(projectingRipple.get()); 1719 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); 1720 }); 1721 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1722 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1723 // Set a rect outline for the projecting ripple to be masked against. 1724 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 1725 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1726 canvas.drawRenderNode(receiverBackground.get()); 1727 canvas.drawRenderNode(child.get()); 1728 }); 1729 1730 OffscreenBuffer** layerHandle = child->getLayerHandle(); 1731 1732 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1733 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1734 Matrix4 windowTransform; 1735 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin 1736 layer.setWindowTransform(windowTransform); 1737 *layerHandle = &layer; 1738 1739 auto syncedNode = TestUtils::getSyncedNode(parent); 1740 1741 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1742 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); 1743 1744 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 1745 sLightGeometry, Caches::getInstance()); 1746 frameBuilder.deferLayers(layerUpdateQueue); 1747 frameBuilder.deferRenderNode(*syncedNode); 1748 1749 ProjectionHwLayerTestRenderer renderer; 1750 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1751 EXPECT_EQ(6, renderer.getIndex()); 1752 1753 // clean up layer pointer, so we can safely destruct RenderNode 1754 *layerHandle = nullptr; 1755} 1756 1757RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { 1758 static const int scrollX = 500000; 1759 static const int scrollY = 0; 1760 class ProjectionChildScrollTestRenderer : public TestRendererBase { 1761 public: 1762 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1763 EXPECT_EQ(0, mIndex++); 1764 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1765 } 1766 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1767 EXPECT_EQ(1, mIndex++); 1768 ASSERT_NE(nullptr, state.computedState.clipState); 1769 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1770 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect); 1771 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1772 } 1773 }; 1774 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1775 [](RenderProperties& properties, RecordingCanvas& canvas) { 1776 properties.setProjectionReceiver(true); 1777 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1778 }); 1779 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1780 [](RenderProperties& properties, RecordingCanvas& canvas) { 1781 // scroll doesn't apply to background, so undone via translationX/Y 1782 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1783 properties.setTranslationX(scrollX); 1784 properties.setTranslationY(scrollY); 1785 properties.setProjectBackwards(true); 1786 properties.setClipToBounds(false); 1787 canvas.drawOval(0, 0, 200, 200, SkPaint()); 1788 }); 1789 auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1790 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1791 // Record time clip will be ignored by projectee 1792 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect); 1793 1794 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1795 canvas.drawRenderNode(projectingRipple.get()); 1796 }); 1797 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1798 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1799 canvas.drawRenderNode(receiverBackground.get()); 1800 canvas.drawRenderNode(child.get()); 1801 }); 1802 1803 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 1804 sLightGeometry, Caches::getInstance()); 1805 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1806 1807 ProjectionChildScrollTestRenderer renderer; 1808 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1809 EXPECT_EQ(2, renderer.getIndex()); 1810} 1811 1812// creates a 100x100 shadow casting node with provided translationZ 1813static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1814 return TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1815 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1816 properties.setTranslationZ(translationZ); 1817 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1818 SkPaint paint; 1819 paint.setColor(SK_ColorWHITE); 1820 canvas.drawRect(0, 0, 100, 100, paint); 1821 }); 1822} 1823 1824RENDERTHREAD_TEST(FrameBuilder, shadow) { 1825 class ShadowTestRenderer : public TestRendererBase { 1826 public: 1827 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1828 EXPECT_EQ(0, mIndex++); 1829 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1830 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr)); 1831 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY); 1832 1833 Matrix4 expectedZ; 1834 expectedZ.loadTranslate(0, 0, 5); 1835 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ); 1836 } 1837 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1838 EXPECT_EQ(1, mIndex++); 1839 } 1840 }; 1841 1842 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1843 [](RenderProperties& props, RecordingCanvas& canvas) { 1844 canvas.insertReorderBarrier(true); 1845 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1846 }); 1847 1848 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1849 sLightGeometry, Caches::getInstance()); 1850 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1851 1852 ShadowTestRenderer renderer; 1853 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1854 EXPECT_EQ(2, renderer.getIndex()); 1855} 1856 1857RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { 1858 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1859 public: 1860 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1861 EXPECT_EQ(0, mIndex++); 1862 return nullptr; 1863 } 1864 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1865 EXPECT_EQ(1, mIndex++); 1866 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1867 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1868 } 1869 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1870 EXPECT_EQ(2, mIndex++); 1871 } 1872 void endLayer() override { 1873 EXPECT_EQ(3, mIndex++); 1874 } 1875 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1876 EXPECT_EQ(4, mIndex++); 1877 } 1878 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1879 EXPECT_EQ(5, mIndex++); 1880 } 1881 }; 1882 1883 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1884 [](RenderProperties& props, RecordingCanvas& canvas) { 1885 // save/restore outside of reorderBarrier, so they don't get moved out of place 1886 canvas.translate(20, 10); 1887 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); 1888 canvas.insertReorderBarrier(true); 1889 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1890 canvas.insertReorderBarrier(false); 1891 canvas.restoreToCount(count); 1892 }); 1893 1894 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1895 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1896 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1897 1898 ShadowSaveLayerTestRenderer renderer; 1899 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1900 EXPECT_EQ(6, renderer.getIndex()); 1901} 1902 1903RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { 1904 class ShadowHwLayerTestRenderer : public TestRendererBase { 1905 public: 1906 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1907 EXPECT_EQ(0, mIndex++); 1908 } 1909 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1910 EXPECT_EQ(1, mIndex++); 1911 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1912 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1913 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius); 1914 } 1915 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1916 EXPECT_EQ(2, mIndex++); 1917 } 1918 void endLayer() override { 1919 EXPECT_EQ(3, mIndex++); 1920 } 1921 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1922 EXPECT_EQ(4, mIndex++); 1923 } 1924 }; 1925 1926 auto parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160, 1927 [](RenderProperties& props, RecordingCanvas& canvas) { 1928 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1929 canvas.insertReorderBarrier(true); 1930 canvas.save(SaveFlags::MatrixClip); 1931 canvas.translate(20, 10); 1932 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1933 canvas.restore(); 1934 }); 1935 OffscreenBuffer** layerHandle = parent->getLayerHandle(); 1936 1937 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1938 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1939 Matrix4 windowTransform; 1940 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin 1941 layer.setWindowTransform(windowTransform); 1942 *layerHandle = &layer; 1943 1944 auto syncedNode = TestUtils::getSyncedNode(parent); 1945 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1946 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); 1947 1948 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1949 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance()); 1950 frameBuilder.deferLayers(layerUpdateQueue); 1951 frameBuilder.deferRenderNode(*syncedNode); 1952 1953 ShadowHwLayerTestRenderer renderer; 1954 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1955 EXPECT_EQ(5, renderer.getIndex()); 1956 1957 // clean up layer pointer, so we can safely destruct RenderNode 1958 *layerHandle = nullptr; 1959} 1960 1961RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { 1962 class ShadowLayeringTestRenderer : public TestRendererBase { 1963 public: 1964 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1965 int index = mIndex++; 1966 EXPECT_TRUE(index == 0 || index == 1); 1967 } 1968 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1969 int index = mIndex++; 1970 EXPECT_TRUE(index == 2 || index == 3); 1971 } 1972 }; 1973 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1974 [](RenderProperties& props, RecordingCanvas& canvas) { 1975 canvas.insertReorderBarrier(true); 1976 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1977 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 1978 }); 1979 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1980 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1981 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1982 1983 ShadowLayeringTestRenderer renderer; 1984 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1985 EXPECT_EQ(4, renderer.getIndex()); 1986} 1987 1988RENDERTHREAD_TEST(FrameBuilder, shadowClipping) { 1989 class ShadowClippingTestRenderer : public TestRendererBase { 1990 public: 1991 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1992 EXPECT_EQ(0, mIndex++); 1993 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect) 1994 << "Shadow must respect pre-barrier canvas clip value."; 1995 } 1996 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1997 EXPECT_EQ(1, mIndex++); 1998 } 1999 }; 2000 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2001 [](RenderProperties& props, RecordingCanvas& canvas) { 2002 // Apply a clip before the reorder barrier/shadow casting child is drawn. 2003 // This clip must be applied to the shadow cast by the child. 2004 canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect); 2005 canvas.insertReorderBarrier(true); 2006 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 2007 }); 2008 2009 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2010 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 2011 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 2012 2013 ShadowClippingTestRenderer renderer; 2014 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2015 EXPECT_EQ(2, renderer.getIndex()); 2016} 2017 2018static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, 2019 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 2020 class PropertyTestRenderer : public TestRendererBase { 2021 public: 2022 explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) 2023 : mCallback(callback) {} 2024 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2025 EXPECT_EQ(mIndex++, 0); 2026 mCallback(op, state); 2027 } 2028 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 2029 }; 2030 2031 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2032 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 2033 propSetupCallback(props); 2034 SkPaint paint; 2035 paint.setColor(SK_ColorWHITE); 2036 canvas.drawRect(0, 0, 100, 100, paint); 2037 }); 2038 2039 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, 2040 sLightGeometry, Caches::getInstance()); 2041 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 2042 2043 PropertyTestRenderer renderer(opValidateCallback); 2044 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2045 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 2046} 2047 2048RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 2049 testProperty([](RenderProperties& properties) { 2050 properties.setAlpha(0.5f); 2051 properties.setHasOverlappingRendering(false); 2052 }, [](const RectOp& op, const BakedOpState& state) { 2053 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 2054 }); 2055} 2056 2057RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { 2058 testProperty([](RenderProperties& properties) { 2059 properties.setClipToBounds(true); 2060 properties.setClipBounds(Rect(10, 20, 300, 400)); 2061 }, [](const RectOp& op, const BakedOpState& state) { 2062 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 2063 << "Clip rect should be intersection of node bounds and clip bounds"; 2064 }); 2065} 2066 2067RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { 2068 testProperty([](RenderProperties& properties) { 2069 properties.mutableRevealClip().set(true, 50, 50, 25); 2070 }, [](const RectOp& op, const BakedOpState& state) { 2071 ASSERT_NE(nullptr, state.roundRectClipState); 2072 EXPECT_TRUE(state.roundRectClipState->highPriority); 2073 EXPECT_EQ(25, state.roundRectClipState->radius); 2074 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 2075 }); 2076} 2077 2078RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { 2079 testProperty([](RenderProperties& properties) { 2080 properties.mutableOutline().setShouldClip(true); 2081 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 2082 }, [](const RectOp& op, const BakedOpState& state) { 2083 ASSERT_NE(nullptr, state.roundRectClipState); 2084 EXPECT_FALSE(state.roundRectClipState->highPriority); 2085 EXPECT_EQ(5, state.roundRectClipState->radius); 2086 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 2087 }); 2088} 2089 2090RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) { 2091 testProperty([](RenderProperties& properties) { 2092 properties.setLeftTopRightBottom(10, 10, 110, 110); 2093 2094 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 2095 properties.setStaticMatrix(&staticMatrix); 2096 2097 // ignored, since static overrides animation 2098 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 2099 properties.setAnimationMatrix(&animationMatrix); 2100 2101 properties.setTranslationX(10); 2102 properties.setTranslationY(20); 2103 properties.setScaleX(0.5f); 2104 properties.setScaleY(0.7f); 2105 }, [](const RectOp& op, const BakedOpState& state) { 2106 Matrix4 matrix; 2107 matrix.loadTranslate(10, 10, 0); // left, top 2108 matrix.scale(1.2f, 1.2f, 1); // static matrix 2109 // ignore animation matrix, since static overrides it 2110 2111 // translation xy 2112 matrix.translate(10, 20); 2113 2114 // scale xy (from default pivot - center) 2115 matrix.translate(50, 50); 2116 matrix.scale(0.5f, 0.7f, 1); 2117 matrix.translate(-50, -50); 2118 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 2119 << "Op draw matrix must match expected combination of transformation properties"; 2120 }); 2121} 2122 2123struct SaveLayerAlphaData { 2124 uint32_t layerWidth = 0; 2125 uint32_t layerHeight = 0; 2126 Rect rectClippedBounds; 2127 Matrix4 rectMatrix; 2128 Matrix4 drawLayerMatrix; 2129}; 2130/** 2131 * Constructs a view to hit the temporary layer alpha property implementation: 2132 * a) 0 < alpha < 1 2133 * b) too big for layer (larger than maxTextureSize) 2134 * c) overlapping rendering content 2135 * returning observed data about layer size and content clip/transform. 2136 * 2137 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 2138 * (for efficiency, and to fit in layer size constraints) based on parent clip. 2139 */ 2140void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 2141 std::function<void(RenderProperties&)> propSetupCallback) { 2142 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 2143 public: 2144 explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) 2145 : mOutData(outData) {} 2146 2147 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 2148 EXPECT_EQ(0, mIndex++); 2149 mOutData->layerWidth = width; 2150 mOutData->layerHeight = height; 2151 return nullptr; 2152 } 2153 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2154 EXPECT_EQ(1, mIndex++); 2155 2156 mOutData->rectClippedBounds = state.computedState.clippedBounds; 2157 mOutData->rectMatrix = state.computedState.transform; 2158 } 2159 void endLayer() override { 2160 EXPECT_EQ(2, mIndex++); 2161 } 2162 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 2163 EXPECT_EQ(3, mIndex++); 2164 mOutData->drawLayerMatrix = state.computedState.transform; 2165 } 2166 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 2167 EXPECT_EQ(4, mIndex++); 2168 } 2169 private: 2170 SaveLayerAlphaData* mOutData; 2171 }; 2172 2173 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 2174 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 2175 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 10000, 10000, 2176 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 2177 properties.setHasOverlappingRendering(true); 2178 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 2179 // apply other properties 2180 propSetupCallback(properties); 2181 2182 SkPaint paint; 2183 paint.setColor(SK_ColorWHITE); 2184 canvas.drawRect(0, 0, 10000, 10000, paint); 2185 }); 2186 auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height 2187 2188 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 2189 sLightGeometry, Caches::getInstance()); 2190 frameBuilder.deferRenderNode(*syncedNode); 2191 2192 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 2193 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2194 2195 // assert, since output won't be valid if we haven't seen a save layer triggered 2196 ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 2197} 2198 2199RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 2200 SaveLayerAlphaData observedData; 2201 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2202 properties.setTranslationX(10); // offset rendering content 2203 properties.setTranslationY(-2000); // offset rendering content 2204 }); 2205 EXPECT_EQ(190u, observedData.layerWidth); 2206 EXPECT_EQ(200u, observedData.layerHeight); 2207 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 2208 << "expect content to be clipped to screen area"; 2209 Matrix4 expected; 2210 expected.loadTranslate(0, -2000, 0); 2211 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 2212 << "expect content to be translated as part of being clipped"; 2213 expected.loadTranslate(10, 0, 0); 2214 EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix) 2215 << "expect drawLayer to be translated as part of being clipped"; 2216} 2217 2218RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 2219 SaveLayerAlphaData observedData; 2220 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2221 // Translate and rotate the view so that the only visible part is the top left corner of 2222 // the view. It will form an isosceles right triangle with a long side length of 200 at the 2223 // bottom of the viewport. 2224 properties.setTranslationX(100); 2225 properties.setTranslationY(100); 2226 properties.setPivotX(0); 2227 properties.setPivotY(0); 2228 properties.setRotation(45); 2229 }); 2230 // ceil(sqrt(2) / 2 * 200) = 142 2231 EXPECT_EQ(142u, observedData.layerWidth); 2232 EXPECT_EQ(142u, observedData.layerHeight); 2233 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 2234 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2235} 2236 2237RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 2238 SaveLayerAlphaData observedData; 2239 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2240 properties.setPivotX(0); 2241 properties.setPivotY(0); 2242 properties.setScaleX(2); 2243 properties.setScaleY(0.5f); 2244 }); 2245 EXPECT_EQ(100u, observedData.layerWidth); 2246 EXPECT_EQ(400u, observedData.layerHeight); 2247 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 2248 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2249} 2250 2251RENDERTHREAD_TEST(FrameBuilder, clip_replace) { 2252 class ClipReplaceTestRenderer : public TestRendererBase { 2253 public: 2254 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 2255 EXPECT_EQ(0, mIndex++); 2256 EXPECT_TRUE(op.localClip->intersectWithRoot); 2257 EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect) 2258 << "Expect resolved clip to be intersection of viewport clip and clip op"; 2259 } 2260 }; 2261 auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30, 2262 [](RenderProperties& props, RecordingCanvas& canvas) { 2263 canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_private_internal_do_not_use); 2264 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 2265 }); 2266 2267 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, 2268 sLightGeometry, Caches::getInstance()); 2269 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 2270 2271 ClipReplaceTestRenderer renderer; 2272 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2273 EXPECT_EQ(1, renderer.getIndex()); 2274} 2275 2276TEST(FrameBuilder, projectionReorderProjectedInMiddle) { 2277 /* R is backward projected on B 2278 A 2279 / \ 2280 B C 2281 | 2282 R 2283 */ 2284 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2285 [](RenderProperties& props, RecordingCanvas& canvas) { 2286 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2287 props.setProjectionReceiver(true); 2288 } ); //nodeB 2289 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2290 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2291 props.setProjectBackwards(true); 2292 props.setClipToBounds(false); 2293 } ); //nodeR 2294 } ); //nodeC 2295 }); //nodeA 2296 2297 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2298 sLightGeometry, Caches::getInstance()); 2299 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2300 2301 ZReorderTestRenderer renderer; 2302 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2303 EXPECT_EQ(3, renderer.getIndex()); 2304} 2305 2306TEST(FrameBuilder, projectionReorderProjectLast) { 2307 /* R is backward projected on E 2308 A 2309 / | \ 2310 / | \ 2311 B C E 2312 | 2313 R 2314 */ 2315 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2316 [](RenderProperties& props, RecordingCanvas& canvas) { 2317 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2318 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2319 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 2 2320 props.setProjectBackwards(true); 2321 props.setClipToBounds(false); 2322 } ); //nodeR 2323 } ); //nodeC 2324 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 3 2325 props.setProjectionReceiver(true); 2326 } ); //nodeE 2327 }); //nodeA 2328 2329 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2330 sLightGeometry, Caches::getInstance()); 2331 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2332 2333 ZReorderTestRenderer renderer; 2334 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2335 EXPECT_EQ(4, renderer.getIndex()); 2336} 2337 2338TEST(FrameBuilder, projectionReorderNoReceivable) { 2339 /* R is backward projected without receiver 2340 A 2341 / \ 2342 B C 2343 | 2344 R 2345 */ 2346 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2347 [](RenderProperties& props, RecordingCanvas& canvas) { 2348 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2349 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2350 drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { 2351 //not having a projection receiver is an undefined behavior 2352 props.setProjectBackwards(true); 2353 props.setClipToBounds(false); 2354 } ); //nodeR 2355 } ); //nodeC 2356 }); //nodeA 2357 2358 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2359 sLightGeometry, Caches::getInstance()); 2360 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2361 2362 ZReorderTestRenderer renderer; 2363 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2364 EXPECT_EQ(2, renderer.getIndex()); 2365} 2366 2367TEST(FrameBuilder, projectionReorderParentReceivable) { 2368 /* R is backward projected on C 2369 A 2370 / \ 2371 B C 2372 | 2373 R 2374 */ 2375 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2376 [](RenderProperties& props, RecordingCanvas& canvas) { 2377 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2378 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2379 props.setProjectionReceiver(true); 2380 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2381 props.setProjectBackwards(true); 2382 props.setClipToBounds(false); 2383 } ); //nodeR 2384 } ); //nodeC 2385 }); //nodeA 2386 2387 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2388 sLightGeometry, Caches::getInstance()); 2389 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2390 2391 ZReorderTestRenderer renderer; 2392 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2393 EXPECT_EQ(3, renderer.getIndex()); 2394} 2395 2396TEST(FrameBuilder, projectionReorderSameNodeReceivable) { 2397 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2398 [](RenderProperties& props, RecordingCanvas& canvas) { 2399 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2400 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2401 drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { 2402 //having a node that is projected on itself is an undefined/unexpected behavior 2403 props.setProjectionReceiver(true); 2404 props.setProjectBackwards(true); 2405 props.setClipToBounds(false); 2406 } ); //nodeR 2407 } ); //nodeC 2408 }); //nodeA 2409 2410 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2411 sLightGeometry, Caches::getInstance()); 2412 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2413 2414 ZReorderTestRenderer renderer; 2415 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2416 EXPECT_EQ(2, renderer.getIndex()); 2417} 2418 2419TEST(FrameBuilder, projectionReorderProjectedSibling) { 2420 //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a 2421 //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical 2422 //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling 2423 /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below), 2424 but for some reason it is drawn. 2425 A 2426 /|\ 2427 / | \ 2428 B C R 2429 */ 2430 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2431 [](RenderProperties& props, RecordingCanvas& canvas) { 2432 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2433 props.setProjectionReceiver(true); 2434 } ); //nodeB 2435 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2436 } ); //nodeC 2437 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2438 props.setProjectBackwards(true); 2439 props.setClipToBounds(false); 2440 } ); //nodeR 2441 }); //nodeA 2442 2443 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2444 sLightGeometry, Caches::getInstance()); 2445 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2446 2447 ZReorderTestRenderer renderer; 2448 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2449 EXPECT_EQ(3, renderer.getIndex()); 2450} 2451 2452TEST(FrameBuilder, projectionReorderProjectedSibling2) { 2453 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. 2454 A 2455 | 2456 G 2457 /|\ 2458 / | \ 2459 B C R 2460 */ 2461 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2462 [](RenderProperties& props, RecordingCanvas& canvas) { 2463 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2464 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2465 props.setProjectionReceiver(true); 2466 } ); //nodeB 2467 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2468 } ); //nodeC 2469 drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2470 props.setProjectBackwards(true); 2471 props.setClipToBounds(false); 2472 } ); //nodeR 2473 } ); //nodeG 2474 }); //nodeA 2475 2476 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2477 sLightGeometry, Caches::getInstance()); 2478 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2479 2480 ZReorderTestRenderer renderer; 2481 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2482 EXPECT_EQ(3, renderer.getIndex()); 2483} 2484 2485TEST(FrameBuilder, projectionReorderGrandparentReceivable) { 2486 /* R is backward projected on B 2487 A 2488 | 2489 B 2490 | 2491 C 2492 | 2493 R 2494 */ 2495 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2496 [](RenderProperties& props, RecordingCanvas& canvas) { 2497 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2498 props.setProjectionReceiver(true); 2499 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2500 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2501 props.setProjectBackwards(true); 2502 props.setClipToBounds(false); 2503 } ); //nodeR 2504 } ); //nodeC 2505 } ); //nodeB 2506 }); //nodeA 2507 2508 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2509 sLightGeometry, Caches::getInstance()); 2510 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2511 2512 ZReorderTestRenderer renderer; 2513 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2514 EXPECT_EQ(3, renderer.getIndex()); 2515} 2516 2517TEST(FrameBuilder, projectionReorderTwoReceivables) { 2518 /* B and G are receivables, R is backward projected 2519 A 2520 / \ 2521 B C 2522 / \ 2523 G R 2524 */ 2525 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2526 [](RenderProperties& props, RecordingCanvas& canvas) { 2527 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2528 props.setProjectionReceiver(true); 2529 } ); //nodeB 2530 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2531 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2532 props.setProjectionReceiver(true); 2533 } ); //nodeG 2534 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2535 props.setProjectBackwards(true); 2536 props.setClipToBounds(false); 2537 } ); //nodeR 2538 } ); //nodeC 2539 }); //nodeA 2540 2541 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2542 sLightGeometry, Caches::getInstance()); 2543 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2544 2545 ZReorderTestRenderer renderer; 2546 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2547 EXPECT_EQ(4, renderer.getIndex()); 2548} 2549 2550TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) { 2551 /* B and G are receivables, G is backward projected 2552 A 2553 / \ 2554 B C 2555 / \ 2556 G R 2557 */ 2558 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2559 [](RenderProperties& props, RecordingCanvas& canvas) { 2560 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2561 props.setProjectionReceiver(true); 2562 } ); //nodeB 2563 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2564 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2565 props.setProjectionReceiver(true); 2566 props.setProjectBackwards(true); 2567 props.setClipToBounds(false); 2568 } ); //nodeG 2569 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2570 } ); //nodeR 2571 } ); //nodeC 2572 }); //nodeA 2573 2574 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2575 sLightGeometry, Caches::getInstance()); 2576 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2577 2578 ZReorderTestRenderer renderer; 2579 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2580 EXPECT_EQ(4, renderer.getIndex()); 2581} 2582 2583TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) { 2584 /* B and G are receivables, R is backward projected 2585 A 2586 / \ 2587 B C 2588 / \ 2589 G D 2590 | 2591 R 2592 */ 2593 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2594 [](RenderProperties& props, RecordingCanvas& canvas) { 2595 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2596 props.setProjectionReceiver(true); 2597 } ); //nodeB 2598 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2599 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2600 props.setProjectionReceiver(true); 2601 } ); //nodeG 2602 drawOrderedNode(&canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { //D 2603 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2604 props.setProjectBackwards(true); 2605 props.setClipToBounds(false); 2606 } ); //nodeR 2607 } ); //nodeD 2608 } ); //nodeC 2609 }); //nodeA 2610 2611 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2612 sLightGeometry, Caches::getInstance()); 2613 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2614 2615 ZReorderTestRenderer renderer; 2616 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2617 EXPECT_EQ(5, renderer.getIndex()); 2618} 2619 2620} // namespace uirenderer 2621} // namespace android 2622