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