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