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