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