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