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