FrameBuilderTests.cpp revision db45a4bfaff1120a9b23073e46a0cc6d39f56023
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, SkRegion::kIntersect_Op); // intersection should be empty 178 canvas.drawRect(0, 0, 400, 400, SkPaint()); 179 canvas.restore(); 180 }); 181 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 182 sLightGeometry, Caches::getInstance()); 183 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 184 185 FailRenderer renderer; 186 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 187} 188 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, SkRegion::kReplace_Op); 457 canvas.drawBitmap(*bitmap, 0, 40, nullptr); 458 459 // top side clipped (to inset top half) 460 canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op); 461 canvas.drawBitmap(*bitmap, 40, 0, nullptr); 462 463 // right side clipped (to inset right half) 464 canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op); 465 canvas.drawBitmap(*bitmap, 80, 40, nullptr); 466 467 // bottom not clipped, just abutting (inset bottom half) 468 canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op); 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, SkRegion::kIntersect_Op); 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, SkRegion::kIntersect_Op); 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, SkRegion::kIntersect_Op); 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 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 1534 drawOrderedRect(&canvas, 1); 1535 canvas.insertReorderBarrier(true); 1536 drawOrderedNode(&canvas, 6, 2.0f); 1537 drawOrderedRect(&canvas, 3); 1538 drawOrderedNode(&canvas, 4, 0.0f); 1539 drawOrderedRect(&canvas, 5); 1540 drawOrderedNode(&canvas, 2, -2.0f); 1541 drawOrderedNode(&canvas, 7, 2.0f); 1542 canvas.insertReorderBarrier(false); 1543 drawOrderedRect(&canvas, 8); 1544 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 1545 }); 1546 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1547 sLightGeometry, Caches::getInstance()); 1548 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1549 1550 ZReorderTestRenderer renderer; 1551 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1552 EXPECT_EQ(10, renderer.getIndex()); 1553}; 1554 1555RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { 1556 static const int scrollX = 5; 1557 static const int scrollY = 10; 1558 class ProjectionReorderTestRenderer : public TestRendererBase { 1559 public: 1560 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1561 const int index = mIndex++; 1562 1563 Matrix4 expectedMatrix; 1564 switch (index) { 1565 case 0: 1566 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 1567 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1568 expectedMatrix.loadIdentity(); 1569 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1570 break; 1571 case 1: 1572 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 1573 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1574 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); 1575 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1576 EXPECT_EQ(Rect(-35, -30, 45, 50), 1577 Rect(state.computedState.localProjectionPathMask->getBounds())); 1578 break; 1579 case 2: 1580 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 1581 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 1582 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 1583 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1584 break; 1585 default: 1586 ADD_FAILURE(); 1587 } 1588 EXPECT_EQ(expectedMatrix, state.computedState.transform); 1589 } 1590 }; 1591 1592 /** 1593 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 1594 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 1595 * draw, but because it is projected backwards, it's drawn in between B and C. 1596 * 1597 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 1598 * (which isn't affected by scroll). 1599 */ 1600 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1601 [](RenderProperties& properties, RecordingCanvas& canvas) { 1602 properties.setProjectionReceiver(true); 1603 // scroll doesn't apply to background, so undone via translationX/Y 1604 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1605 properties.setTranslationX(scrollX); 1606 properties.setTranslationY(scrollY); 1607 1608 SkPaint paint; 1609 paint.setColor(SK_ColorWHITE); 1610 canvas.drawRect(0, 0, 100, 100, paint); 1611 }); 1612 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(50, 0, 100, 50, 1613 [](RenderProperties& properties, RecordingCanvas& canvas) { 1614 properties.setProjectBackwards(true); 1615 properties.setClipToBounds(false); 1616 SkPaint paint; 1617 paint.setColor(SK_ColorDKGRAY); 1618 canvas.drawRect(-10, -10, 60, 60, paint); 1619 }); 1620 auto child = TestUtils::createNode<RecordingCanvas>(0, 50, 100, 100, 1621 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1622 SkPaint paint; 1623 paint.setColor(SK_ColorBLUE); 1624 canvas.drawRect(0, 0, 100, 50, paint); 1625 canvas.drawRenderNode(projectingRipple.get()); 1626 }); 1627 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1628 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1629 // Set a rect outline for the projecting ripple to be masked against. 1630 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 1631 1632 canvas.save(SaveFlags::MatrixClip); 1633 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1634 canvas.drawRenderNode(receiverBackground.get()); 1635 canvas.drawRenderNode(child.get()); 1636 canvas.restore(); 1637 }); 1638 1639 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1640 sLightGeometry, Caches::getInstance()); 1641 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1642 1643 ProjectionReorderTestRenderer renderer; 1644 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1645 EXPECT_EQ(3, renderer.getIndex()); 1646} 1647 1648RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { 1649 static const int scrollX = 5; 1650 static const int scrollY = 10; 1651 class ProjectionHwLayerTestRenderer : public TestRendererBase { 1652 public: 1653 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1654 EXPECT_EQ(0, mIndex++); 1655 } 1656 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 1657 EXPECT_EQ(1, mIndex++); 1658 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1659 } 1660 void endLayer() override { 1661 EXPECT_EQ(2, mIndex++); 1662 } 1663 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1664 EXPECT_EQ(3, mIndex++); 1665 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1666 } 1667 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1668 EXPECT_EQ(4, mIndex++); 1669 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1670 Matrix4 expected; 1671 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); 1672 EXPECT_EQ(expected, state.computedState.transform); 1673 EXPECT_EQ(Rect(-85, -80, 295, 300), 1674 Rect(state.computedState.localProjectionPathMask->getBounds())); 1675 } 1676 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1677 EXPECT_EQ(5, mIndex++); 1678 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1679 } 1680 }; 1681 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1682 [](RenderProperties& properties, RecordingCanvas& canvas) { 1683 properties.setProjectionReceiver(true); 1684 // scroll doesn't apply to background, so undone via translationX/Y 1685 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1686 properties.setTranslationX(scrollX); 1687 properties.setTranslationY(scrollY); 1688 1689 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1690 }); 1691 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1692 [](RenderProperties& properties, RecordingCanvas& canvas) { 1693 properties.setProjectBackwards(true); 1694 properties.setClipToBounds(false); 1695 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 1696 }); 1697 auto child = TestUtils::createNode<RecordingCanvas>(100, 100, 300, 300, 1698 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1699 properties.mutateLayerProperties().setType(LayerType::RenderLayer); 1700 canvas.drawRenderNode(projectingRipple.get()); 1701 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); 1702 }); 1703 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1704 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1705 // Set a rect outline for the projecting ripple to be masked against. 1706 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 1707 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1708 canvas.drawRenderNode(receiverBackground.get()); 1709 canvas.drawRenderNode(child.get()); 1710 }); 1711 1712 OffscreenBuffer** layerHandle = child->getLayerHandle(); 1713 1714 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1715 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1716 Matrix4 windowTransform; 1717 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin 1718 layer.setWindowTransform(windowTransform); 1719 *layerHandle = &layer; 1720 1721 auto syncedNode = TestUtils::getSyncedNode(parent); 1722 1723 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1724 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); 1725 1726 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 1727 sLightGeometry, Caches::getInstance()); 1728 frameBuilder.deferLayers(layerUpdateQueue); 1729 frameBuilder.deferRenderNode(*syncedNode); 1730 1731 ProjectionHwLayerTestRenderer renderer; 1732 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1733 EXPECT_EQ(6, renderer.getIndex()); 1734 1735 // clean up layer pointer, so we can safely destruct RenderNode 1736 *layerHandle = nullptr; 1737} 1738 1739RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { 1740 static const int scrollX = 500000; 1741 static const int scrollY = 0; 1742 class ProjectionChildScrollTestRenderer : public TestRendererBase { 1743 public: 1744 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1745 EXPECT_EQ(0, mIndex++); 1746 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1747 } 1748 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1749 EXPECT_EQ(1, mIndex++); 1750 ASSERT_NE(nullptr, state.computedState.clipState); 1751 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1752 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect); 1753 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1754 } 1755 }; 1756 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1757 [](RenderProperties& properties, RecordingCanvas& canvas) { 1758 properties.setProjectionReceiver(true); 1759 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1760 }); 1761 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1762 [](RenderProperties& properties, RecordingCanvas& canvas) { 1763 // scroll doesn't apply to background, so undone via translationX/Y 1764 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1765 properties.setTranslationX(scrollX); 1766 properties.setTranslationY(scrollY); 1767 properties.setProjectBackwards(true); 1768 properties.setClipToBounds(false); 1769 canvas.drawOval(0, 0, 200, 200, SkPaint()); 1770 }); 1771 auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1772 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1773 // Record time clip will be ignored by projectee 1774 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op); 1775 1776 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1777 canvas.drawRenderNode(projectingRipple.get()); 1778 }); 1779 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, 1780 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1781 canvas.drawRenderNode(receiverBackground.get()); 1782 canvas.drawRenderNode(child.get()); 1783 }); 1784 1785 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, 1786 sLightGeometry, Caches::getInstance()); 1787 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1788 1789 ProjectionChildScrollTestRenderer renderer; 1790 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1791 EXPECT_EQ(2, renderer.getIndex()); 1792} 1793 1794// creates a 100x100 shadow casting node with provided translationZ 1795static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1796 return TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1797 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1798 properties.setTranslationZ(translationZ); 1799 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1800 SkPaint paint; 1801 paint.setColor(SK_ColorWHITE); 1802 canvas.drawRect(0, 0, 100, 100, paint); 1803 }); 1804} 1805 1806RENDERTHREAD_TEST(FrameBuilder, shadow) { 1807 class ShadowTestRenderer : public TestRendererBase { 1808 public: 1809 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1810 EXPECT_EQ(0, mIndex++); 1811 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1812 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr)); 1813 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY); 1814 1815 Matrix4 expectedZ; 1816 expectedZ.loadTranslate(0, 0, 5); 1817 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ); 1818 } 1819 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1820 EXPECT_EQ(1, mIndex++); 1821 } 1822 }; 1823 1824 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1825 [](RenderProperties& props, RecordingCanvas& canvas) { 1826 canvas.insertReorderBarrier(true); 1827 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1828 }); 1829 1830 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1831 sLightGeometry, Caches::getInstance()); 1832 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1833 1834 ShadowTestRenderer renderer; 1835 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1836 EXPECT_EQ(2, renderer.getIndex()); 1837} 1838 1839RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { 1840 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1841 public: 1842 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1843 EXPECT_EQ(0, mIndex++); 1844 return nullptr; 1845 } 1846 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1847 EXPECT_EQ(1, mIndex++); 1848 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1849 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1850 } 1851 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1852 EXPECT_EQ(2, mIndex++); 1853 } 1854 void endLayer() override { 1855 EXPECT_EQ(3, mIndex++); 1856 } 1857 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1858 EXPECT_EQ(4, mIndex++); 1859 } 1860 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1861 EXPECT_EQ(5, mIndex++); 1862 } 1863 }; 1864 1865 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1866 [](RenderProperties& props, RecordingCanvas& canvas) { 1867 // save/restore outside of reorderBarrier, so they don't get moved out of place 1868 canvas.translate(20, 10); 1869 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); 1870 canvas.insertReorderBarrier(true); 1871 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1872 canvas.insertReorderBarrier(false); 1873 canvas.restoreToCount(count); 1874 }); 1875 1876 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1877 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1878 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1879 1880 ShadowSaveLayerTestRenderer renderer; 1881 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1882 EXPECT_EQ(6, renderer.getIndex()); 1883} 1884 1885RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { 1886 class ShadowHwLayerTestRenderer : public TestRendererBase { 1887 public: 1888 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1889 EXPECT_EQ(0, mIndex++); 1890 } 1891 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1892 EXPECT_EQ(1, mIndex++); 1893 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1894 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1895 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius); 1896 } 1897 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1898 EXPECT_EQ(2, mIndex++); 1899 } 1900 void endLayer() override { 1901 EXPECT_EQ(3, mIndex++); 1902 } 1903 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1904 EXPECT_EQ(4, mIndex++); 1905 } 1906 }; 1907 1908 auto parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160, 1909 [](RenderProperties& props, RecordingCanvas& canvas) { 1910 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1911 canvas.insertReorderBarrier(true); 1912 canvas.save(SaveFlags::MatrixClip); 1913 canvas.translate(20, 10); 1914 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1915 canvas.restore(); 1916 }); 1917 OffscreenBuffer** layerHandle = parent->getLayerHandle(); 1918 1919 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1920 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1921 Matrix4 windowTransform; 1922 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin 1923 layer.setWindowTransform(windowTransform); 1924 *layerHandle = &layer; 1925 1926 auto syncedNode = TestUtils::getSyncedNode(parent); 1927 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1928 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); 1929 1930 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1931 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance()); 1932 frameBuilder.deferLayers(layerUpdateQueue); 1933 frameBuilder.deferRenderNode(*syncedNode); 1934 1935 ShadowHwLayerTestRenderer renderer; 1936 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1937 EXPECT_EQ(5, renderer.getIndex()); 1938 1939 // clean up layer pointer, so we can safely destruct RenderNode 1940 *layerHandle = nullptr; 1941} 1942 1943RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { 1944 class ShadowLayeringTestRenderer : public TestRendererBase { 1945 public: 1946 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1947 int index = mIndex++; 1948 EXPECT_TRUE(index == 0 || index == 1); 1949 } 1950 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1951 int index = mIndex++; 1952 EXPECT_TRUE(index == 2 || index == 3); 1953 } 1954 }; 1955 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, 1956 [](RenderProperties& props, RecordingCanvas& canvas) { 1957 canvas.insertReorderBarrier(true); 1958 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1959 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 1960 }); 1961 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1962 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1963 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1964 1965 ShadowLayeringTestRenderer renderer; 1966 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1967 EXPECT_EQ(4, renderer.getIndex()); 1968} 1969 1970RENDERTHREAD_TEST(FrameBuilder, shadowClipping) { 1971 class ShadowClippingTestRenderer : public TestRendererBase { 1972 public: 1973 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1974 EXPECT_EQ(0, mIndex++); 1975 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect) 1976 << "Shadow must respect pre-barrier canvas clip value."; 1977 } 1978 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1979 EXPECT_EQ(1, mIndex++); 1980 } 1981 }; 1982 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 1983 [](RenderProperties& props, RecordingCanvas& canvas) { 1984 // Apply a clip before the reorder barrier/shadow casting child is drawn. 1985 // This clip must be applied to the shadow cast by the child. 1986 canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op); 1987 canvas.insertReorderBarrier(true); 1988 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1989 }); 1990 1991 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 1992 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1993 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1994 1995 ShadowClippingTestRenderer renderer; 1996 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1997 EXPECT_EQ(2, renderer.getIndex()); 1998} 1999 2000static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, 2001 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 2002 class PropertyTestRenderer : public TestRendererBase { 2003 public: 2004 explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) 2005 : mCallback(callback) {} 2006 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2007 EXPECT_EQ(mIndex++, 0); 2008 mCallback(op, state); 2009 } 2010 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 2011 }; 2012 2013 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2014 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 2015 propSetupCallback(props); 2016 SkPaint paint; 2017 paint.setColor(SK_ColorWHITE); 2018 canvas.drawRect(0, 0, 100, 100, paint); 2019 }); 2020 2021 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, 2022 sLightGeometry, Caches::getInstance()); 2023 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 2024 2025 PropertyTestRenderer renderer(opValidateCallback); 2026 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2027 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 2028} 2029 2030RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 2031 testProperty([](RenderProperties& properties) { 2032 properties.setAlpha(0.5f); 2033 properties.setHasOverlappingRendering(false); 2034 }, [](const RectOp& op, const BakedOpState& state) { 2035 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 2036 }); 2037} 2038 2039RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { 2040 testProperty([](RenderProperties& properties) { 2041 properties.setClipToBounds(true); 2042 properties.setClipBounds(Rect(10, 20, 300, 400)); 2043 }, [](const RectOp& op, const BakedOpState& state) { 2044 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 2045 << "Clip rect should be intersection of node bounds and clip bounds"; 2046 }); 2047} 2048 2049RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { 2050 testProperty([](RenderProperties& properties) { 2051 properties.mutableRevealClip().set(true, 50, 50, 25); 2052 }, [](const RectOp& op, const BakedOpState& state) { 2053 ASSERT_NE(nullptr, state.roundRectClipState); 2054 EXPECT_TRUE(state.roundRectClipState->highPriority); 2055 EXPECT_EQ(25, state.roundRectClipState->radius); 2056 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 2057 }); 2058} 2059 2060RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { 2061 testProperty([](RenderProperties& properties) { 2062 properties.mutableOutline().setShouldClip(true); 2063 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 2064 }, [](const RectOp& op, const BakedOpState& state) { 2065 ASSERT_NE(nullptr, state.roundRectClipState); 2066 EXPECT_FALSE(state.roundRectClipState->highPriority); 2067 EXPECT_EQ(5, state.roundRectClipState->radius); 2068 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 2069 }); 2070} 2071 2072RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) { 2073 testProperty([](RenderProperties& properties) { 2074 properties.setLeftTopRightBottom(10, 10, 110, 110); 2075 2076 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 2077 properties.setStaticMatrix(&staticMatrix); 2078 2079 // ignored, since static overrides animation 2080 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 2081 properties.setAnimationMatrix(&animationMatrix); 2082 2083 properties.setTranslationX(10); 2084 properties.setTranslationY(20); 2085 properties.setScaleX(0.5f); 2086 properties.setScaleY(0.7f); 2087 }, [](const RectOp& op, const BakedOpState& state) { 2088 Matrix4 matrix; 2089 matrix.loadTranslate(10, 10, 0); // left, top 2090 matrix.scale(1.2f, 1.2f, 1); // static matrix 2091 // ignore animation matrix, since static overrides it 2092 2093 // translation xy 2094 matrix.translate(10, 20); 2095 2096 // scale xy (from default pivot - center) 2097 matrix.translate(50, 50); 2098 matrix.scale(0.5f, 0.7f, 1); 2099 matrix.translate(-50, -50); 2100 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 2101 << "Op draw matrix must match expected combination of transformation properties"; 2102 }); 2103} 2104 2105struct SaveLayerAlphaData { 2106 uint32_t layerWidth = 0; 2107 uint32_t layerHeight = 0; 2108 Rect rectClippedBounds; 2109 Matrix4 rectMatrix; 2110 Matrix4 drawLayerMatrix; 2111}; 2112/** 2113 * Constructs a view to hit the temporary layer alpha property implementation: 2114 * a) 0 < alpha < 1 2115 * b) too big for layer (larger than maxTextureSize) 2116 * c) overlapping rendering content 2117 * returning observed data about layer size and content clip/transform. 2118 * 2119 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 2120 * (for efficiency, and to fit in layer size constraints) based on parent clip. 2121 */ 2122void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 2123 std::function<void(RenderProperties&)> propSetupCallback) { 2124 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 2125 public: 2126 explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) 2127 : mOutData(outData) {} 2128 2129 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 2130 EXPECT_EQ(0, mIndex++); 2131 mOutData->layerWidth = width; 2132 mOutData->layerHeight = height; 2133 return nullptr; 2134 } 2135 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2136 EXPECT_EQ(1, mIndex++); 2137 2138 mOutData->rectClippedBounds = state.computedState.clippedBounds; 2139 mOutData->rectMatrix = state.computedState.transform; 2140 } 2141 void endLayer() override { 2142 EXPECT_EQ(2, mIndex++); 2143 } 2144 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 2145 EXPECT_EQ(3, mIndex++); 2146 mOutData->drawLayerMatrix = state.computedState.transform; 2147 } 2148 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 2149 EXPECT_EQ(4, mIndex++); 2150 } 2151 private: 2152 SaveLayerAlphaData* mOutData; 2153 }; 2154 2155 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 2156 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 2157 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 10000, 10000, 2158 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 2159 properties.setHasOverlappingRendering(true); 2160 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 2161 // apply other properties 2162 propSetupCallback(properties); 2163 2164 SkPaint paint; 2165 paint.setColor(SK_ColorWHITE); 2166 canvas.drawRect(0, 0, 10000, 10000, paint); 2167 }); 2168 auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height 2169 2170 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 2171 sLightGeometry, Caches::getInstance()); 2172 frameBuilder.deferRenderNode(*syncedNode); 2173 2174 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 2175 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2176 2177 // assert, since output won't be valid if we haven't seen a save layer triggered 2178 ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 2179} 2180 2181RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 2182 SaveLayerAlphaData observedData; 2183 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2184 properties.setTranslationX(10); // offset rendering content 2185 properties.setTranslationY(-2000); // offset rendering content 2186 }); 2187 EXPECT_EQ(190u, observedData.layerWidth); 2188 EXPECT_EQ(200u, observedData.layerHeight); 2189 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 2190 << "expect content to be clipped to screen area"; 2191 Matrix4 expected; 2192 expected.loadTranslate(0, -2000, 0); 2193 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 2194 << "expect content to be translated as part of being clipped"; 2195 expected.loadTranslate(10, 0, 0); 2196 EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix) 2197 << "expect drawLayer to be translated as part of being clipped"; 2198} 2199 2200RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 2201 SaveLayerAlphaData observedData; 2202 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2203 // Translate and rotate the view so that the only visible part is the top left corner of 2204 // the view. It will form an isosceles right triangle with a long side length of 200 at the 2205 // bottom of the viewport. 2206 properties.setTranslationX(100); 2207 properties.setTranslationY(100); 2208 properties.setPivotX(0); 2209 properties.setPivotY(0); 2210 properties.setRotation(45); 2211 }); 2212 // ceil(sqrt(2) / 2 * 200) = 142 2213 EXPECT_EQ(142u, observedData.layerWidth); 2214 EXPECT_EQ(142u, observedData.layerHeight); 2215 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 2216 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2217} 2218 2219RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 2220 SaveLayerAlphaData observedData; 2221 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2222 properties.setPivotX(0); 2223 properties.setPivotY(0); 2224 properties.setScaleX(2); 2225 properties.setScaleY(0.5f); 2226 }); 2227 EXPECT_EQ(100u, observedData.layerWidth); 2228 EXPECT_EQ(400u, observedData.layerHeight); 2229 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 2230 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2231} 2232 2233RENDERTHREAD_TEST(FrameBuilder, clip_replace) { 2234 class ClipReplaceTestRenderer : public TestRendererBase { 2235 public: 2236 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 2237 EXPECT_EQ(0, mIndex++); 2238 EXPECT_TRUE(op.localClip->intersectWithRoot); 2239 EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect) 2240 << "Expect resolved clip to be intersection of viewport clip and clip op"; 2241 } 2242 }; 2243 auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30, 2244 [](RenderProperties& props, RecordingCanvas& canvas) { 2245 canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op); 2246 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 2247 }); 2248 2249 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, 2250 sLightGeometry, Caches::getInstance()); 2251 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 2252 2253 ClipReplaceTestRenderer renderer; 2254 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2255 EXPECT_EQ(1, renderer.getIndex()); 2256} 2257 2258TEST(FrameBuilder, projectionReorderProjectedInMiddle) { 2259 /* R is backward projected on B 2260 A 2261 / \ 2262 B C 2263 | 2264 R 2265 */ 2266 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2267 [](RenderProperties& props, RecordingCanvas& canvas) { 2268 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2269 props.setProjectionReceiver(true); 2270 } ); //nodeB 2271 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2272 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2273 props.setProjectBackwards(true); 2274 props.setClipToBounds(false); 2275 } ); //nodeR 2276 } ); //nodeC 2277 }); //nodeA 2278 2279 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2280 sLightGeometry, Caches::getInstance()); 2281 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2282 2283 ZReorderTestRenderer renderer; 2284 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2285 EXPECT_EQ(3, renderer.getIndex()); 2286} 2287 2288TEST(FrameBuilder, projectionReorderProjectLast) { 2289 /* R is backward projected on E 2290 A 2291 / | \ 2292 / | \ 2293 B C E 2294 | 2295 R 2296 */ 2297 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2298 [](RenderProperties& props, RecordingCanvas& canvas) { 2299 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2300 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2301 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 2 2302 props.setProjectBackwards(true); 2303 props.setClipToBounds(false); 2304 } ); //nodeR 2305 } ); //nodeC 2306 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 3 2307 props.setProjectionReceiver(true); 2308 } ); //nodeE 2309 }); //nodeA 2310 2311 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2312 sLightGeometry, Caches::getInstance()); 2313 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2314 2315 ZReorderTestRenderer renderer; 2316 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2317 EXPECT_EQ(4, renderer.getIndex()); 2318} 2319 2320TEST(FrameBuilder, projectionReorderNoReceivable) { 2321 /* R is backward projected without receiver 2322 A 2323 / \ 2324 B C 2325 | 2326 R 2327 */ 2328 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2329 [](RenderProperties& props, RecordingCanvas& canvas) { 2330 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2331 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2332 drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { 2333 //not having a projection receiver is an undefined behavior 2334 props.setProjectBackwards(true); 2335 props.setClipToBounds(false); 2336 } ); //nodeR 2337 } ); //nodeC 2338 }); //nodeA 2339 2340 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2341 sLightGeometry, Caches::getInstance()); 2342 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2343 2344 ZReorderTestRenderer renderer; 2345 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2346 EXPECT_EQ(2, renderer.getIndex()); 2347} 2348 2349TEST(FrameBuilder, projectionReorderParentReceivable) { 2350 /* R is backward projected on C 2351 A 2352 / \ 2353 B C 2354 | 2355 R 2356 */ 2357 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2358 [](RenderProperties& props, RecordingCanvas& canvas) { 2359 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2360 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2361 props.setProjectionReceiver(true); 2362 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2363 props.setProjectBackwards(true); 2364 props.setClipToBounds(false); 2365 } ); //nodeR 2366 } ); //nodeC 2367 }); //nodeA 2368 2369 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2370 sLightGeometry, Caches::getInstance()); 2371 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2372 2373 ZReorderTestRenderer renderer; 2374 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2375 EXPECT_EQ(3, renderer.getIndex()); 2376} 2377 2378TEST(FrameBuilder, projectionReorderSameNodeReceivable) { 2379 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2380 [](RenderProperties& props, RecordingCanvas& canvas) { 2381 drawOrderedNode(&canvas, 0, nullptr); //nodeB 2382 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2383 drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { 2384 //having a node that is projected on itself is an undefined/unexpected behavior 2385 props.setProjectionReceiver(true); 2386 props.setProjectBackwards(true); 2387 props.setClipToBounds(false); 2388 } ); //nodeR 2389 } ); //nodeC 2390 }); //nodeA 2391 2392 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2393 sLightGeometry, Caches::getInstance()); 2394 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2395 2396 ZReorderTestRenderer renderer; 2397 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2398 EXPECT_EQ(2, renderer.getIndex()); 2399} 2400 2401TEST(FrameBuilder, projectionReorderProjectedSibling) { 2402 //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a 2403 //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical 2404 //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling 2405 /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below), 2406 but for some reason it is drawn. 2407 A 2408 /|\ 2409 / | \ 2410 B C R 2411 */ 2412 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2413 [](RenderProperties& props, RecordingCanvas& canvas) { 2414 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2415 props.setProjectionReceiver(true); 2416 } ); //nodeB 2417 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2418 } ); //nodeC 2419 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2420 props.setProjectBackwards(true); 2421 props.setClipToBounds(false); 2422 } ); //nodeR 2423 }); //nodeA 2424 2425 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2426 sLightGeometry, Caches::getInstance()); 2427 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2428 2429 ZReorderTestRenderer renderer; 2430 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2431 EXPECT_EQ(3, renderer.getIndex()); 2432} 2433 2434TEST(FrameBuilder, projectionReorderProjectedSibling2) { 2435 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. 2436 A 2437 | 2438 G 2439 /|\ 2440 / | \ 2441 B C R 2442 */ 2443 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2444 [](RenderProperties& props, RecordingCanvas& canvas) { 2445 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2446 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2447 props.setProjectionReceiver(true); 2448 } ); //nodeB 2449 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2450 } ); //nodeC 2451 drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2452 props.setProjectBackwards(true); 2453 props.setClipToBounds(false); 2454 } ); //nodeR 2455 } ); //nodeG 2456 }); //nodeA 2457 2458 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2459 sLightGeometry, Caches::getInstance()); 2460 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2461 2462 ZReorderTestRenderer renderer; 2463 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2464 EXPECT_EQ(3, renderer.getIndex()); 2465} 2466 2467TEST(FrameBuilder, projectionReorderGrandparentReceivable) { 2468 /* R is backward projected on B 2469 A 2470 | 2471 B 2472 | 2473 C 2474 | 2475 R 2476 */ 2477 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2478 [](RenderProperties& props, RecordingCanvas& canvas) { 2479 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2480 props.setProjectionReceiver(true); 2481 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2482 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2483 props.setProjectBackwards(true); 2484 props.setClipToBounds(false); 2485 } ); //nodeR 2486 } ); //nodeC 2487 } ); //nodeB 2488 }); //nodeA 2489 2490 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2491 sLightGeometry, Caches::getInstance()); 2492 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2493 2494 ZReorderTestRenderer renderer; 2495 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2496 EXPECT_EQ(3, renderer.getIndex()); 2497} 2498 2499TEST(FrameBuilder, projectionReorderTwoReceivables) { 2500 /* B and G are receivables, R is backward projected 2501 A 2502 / \ 2503 B C 2504 / \ 2505 G R 2506 */ 2507 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2508 [](RenderProperties& props, RecordingCanvas& canvas) { 2509 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2510 props.setProjectionReceiver(true); 2511 } ); //nodeB 2512 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2513 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2514 props.setProjectionReceiver(true); 2515 } ); //nodeG 2516 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2517 props.setProjectBackwards(true); 2518 props.setClipToBounds(false); 2519 } ); //nodeR 2520 } ); //nodeC 2521 }); //nodeA 2522 2523 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2524 sLightGeometry, Caches::getInstance()); 2525 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2526 2527 ZReorderTestRenderer renderer; 2528 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2529 EXPECT_EQ(4, renderer.getIndex()); 2530} 2531 2532TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) { 2533 /* B and G are receivables, G is backward projected 2534 A 2535 / \ 2536 B C 2537 / \ 2538 G R 2539 */ 2540 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2541 [](RenderProperties& props, RecordingCanvas& canvas) { 2542 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2543 props.setProjectionReceiver(true); 2544 } ); //nodeB 2545 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2546 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2547 props.setProjectionReceiver(true); 2548 props.setProjectBackwards(true); 2549 props.setClipToBounds(false); 2550 } ); //nodeG 2551 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2552 } ); //nodeR 2553 } ); //nodeC 2554 }); //nodeA 2555 2556 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2557 sLightGeometry, Caches::getInstance()); 2558 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2559 2560 ZReorderTestRenderer renderer; 2561 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2562 EXPECT_EQ(4, renderer.getIndex()); 2563} 2564 2565TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) { 2566 /* B and G are receivables, R is backward projected 2567 A 2568 / \ 2569 B C 2570 / \ 2571 G D 2572 | 2573 R 2574 */ 2575 auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, 2576 [](RenderProperties& props, RecordingCanvas& canvas) { 2577 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B 2578 props.setProjectionReceiver(true); 2579 } ); //nodeB 2580 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //C 2581 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //G 2582 props.setProjectionReceiver(true); 2583 } ); //nodeG 2584 drawOrderedNode(&canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { //D 2585 drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R 2586 props.setProjectBackwards(true); 2587 props.setClipToBounds(false); 2588 } ); //nodeR 2589 } ); //nodeD 2590 } ); //nodeC 2591 }); //nodeA 2592 2593 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2594 sLightGeometry, Caches::getInstance()); 2595 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2596 2597 ZReorderTestRenderer renderer; 2598 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2599 EXPECT_EQ(5, renderer.getIndex()); 2600} 2601 2602} // namespace uirenderer 2603} // namespace android 2604