FrameBuilderTests.cpp revision 3a5811b50157e7ba50854caf957e806aee794d39
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 110RENDERTHREAD_TEST(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, Caches::getInstance()); 137 SimpleTestRenderer renderer; 138 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 139 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end 140} 141 142RENDERTHREAD_TEST(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, Caches::getInstance()); 163 SimpleStrokeTestRenderer renderer; 164 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 165 EXPECT_EQ(1, renderer.getIndex()); 166} 167 168RENDERTHREAD_TEST(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, Caches::getInstance()); 178 179 FailRenderer renderer; 180 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 181} 182 183RENDERTHREAD_TEST(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, Caches::getInstance()); 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 219RENDERTHREAD_TEST(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, Caches::getInstance()); 254 ClippedMergingTestRenderer renderer; 255 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 256 EXPECT_EQ(4, renderer.getIndex()); 257} 258 259RENDERTHREAD_TEST(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::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped 278 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped 279 }); 280 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 281 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 282 TextMergingTestRenderer renderer; 283 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 284 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; 285} 286 287RENDERTHREAD_TEST(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::drawUtf8ToCanvas(&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, Caches::getInstance()); 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 322RENDERTHREAD_TEST(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::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); 365 } 366 }); 367 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 368 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 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, Caches::getInstance()); 402 TextureLayerTestRenderer renderer; 403 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 404 EXPECT_EQ(1, renderer.getIndex()); 405} 406 407RENDERTHREAD_TEST(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), 425 sLightGeometry, Caches::getInstance()); 426 FunctorTestRenderer renderer; 427 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 428 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected"; 429} 430 431RENDERTHREAD_TEST(FrameBuilder, renderNode) { 432 class RenderNodeTestRenderer : public TestRendererBase { 433 public: 434 void onRectOp(const RectOp& op, const BakedOpState& state) override { 435 switch(mIndex++) { 436 case 0: 437 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 438 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 439 break; 440 case 1: 441 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds); 442 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 443 break; 444 default: 445 ADD_FAILURE(); 446 } 447 } 448 }; 449 450 auto child = TestUtils::createNode(10, 10, 110, 110, 451 [](RenderProperties& props, RecordingCanvas& canvas) { 452 SkPaint paint; 453 paint.setColor(SK_ColorWHITE); 454 canvas.drawRect(0, 0, 100, 100, paint); 455 }); 456 457 auto parent = TestUtils::createNode(0, 0, 200, 200, 458 [&child](RenderProperties& props, RecordingCanvas& canvas) { 459 SkPaint paint; 460 paint.setColor(SK_ColorDKGRAY); 461 canvas.drawRect(0, 0, 200, 200, paint); 462 463 canvas.save(SaveFlags::MatrixClip); 464 canvas.translate(40, 40); 465 canvas.drawRenderNode(child.get()); 466 canvas.restore(); 467 }); 468 469 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 470 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 471 RenderNodeTestRenderer renderer; 472 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 473 EXPECT_EQ(2, renderer.getIndex()); 474} 475 476RENDERTHREAD_TEST(FrameBuilder, clipped) { 477 class ClippedTestRenderer : public TestRendererBase { 478 public: 479 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 480 EXPECT_EQ(0, mIndex++); 481 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds); 482 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect()); 483 EXPECT_TRUE(state.computedState.transform.isIdentity()); 484 } 485 }; 486 487 auto node = TestUtils::createNode(0, 0, 200, 200, 488 [](RenderProperties& props, RecordingCanvas& canvas) { 489 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200); 490 canvas.drawBitmap(bitmap, 0, 0, nullptr); 491 }); 492 493 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, 494 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver 495 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 496 ClippedTestRenderer renderer; 497 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 498} 499 500RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { 501 class SaveLayerSimpleTestRenderer : public TestRendererBase { 502 public: 503 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 504 EXPECT_EQ(0, mIndex++); 505 EXPECT_EQ(180u, width); 506 EXPECT_EQ(180u, height); 507 return nullptr; 508 } 509 void endLayer() override { 510 EXPECT_EQ(2, mIndex++); 511 } 512 void onRectOp(const RectOp& op, const BakedOpState& state) override { 513 EXPECT_EQ(1, mIndex++); 514 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds); 515 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds); 516 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect()); 517 518 Matrix4 expectedTransform; 519 expectedTransform.loadTranslate(-10, -10, 0); 520 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform); 521 } 522 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 523 EXPECT_EQ(3, mIndex++); 524 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 525 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 526 EXPECT_TRUE(state.computedState.transform.isIdentity()); 527 } 528 }; 529 530 auto node = TestUtils::createNode(0, 0, 200, 200, 531 [](RenderProperties& props, RecordingCanvas& canvas) { 532 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); 533 canvas.drawRect(10, 10, 190, 190, SkPaint()); 534 canvas.restore(); 535 }); 536 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 537 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 538 SaveLayerSimpleTestRenderer renderer; 539 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 540 EXPECT_EQ(4, renderer.getIndex()); 541} 542 543RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { 544 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as: 545 * - startTemporaryLayer2, rect2 endLayer2 546 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1 547 * - startFrame, layerOp1, endFrame 548 */ 549 class SaveLayerNestedTestRenderer : public TestRendererBase { 550 public: 551 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 552 const int index = mIndex++; 553 if (index == 0) { 554 EXPECT_EQ(400u, width); 555 EXPECT_EQ(400u, height); 556 return (OffscreenBuffer*) 0x400; 557 } else if (index == 3) { 558 EXPECT_EQ(800u, width); 559 EXPECT_EQ(800u, height); 560 return (OffscreenBuffer*) 0x800; 561 } else { ADD_FAILURE(); } 562 return (OffscreenBuffer*) nullptr; 563 } 564 void endLayer() override { 565 int index = mIndex++; 566 EXPECT_TRUE(index == 2 || index == 6); 567 } 568 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 569 EXPECT_EQ(7, mIndex++); 570 } 571 void endFrame(const Rect& repaintRect) override { 572 EXPECT_EQ(9, mIndex++); 573 } 574 void onRectOp(const RectOp& op, const BakedOpState& state) override { 575 const int index = mIndex++; 576 if (index == 1) { 577 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect 578 } else if (index == 4) { 579 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect 580 } else { ADD_FAILURE(); } 581 } 582 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 583 const int index = mIndex++; 584 if (index == 5) { 585 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle); 586 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer 587 } else if (index == 8) { 588 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle); 589 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer 590 } else { ADD_FAILURE(); } 591 } 592 }; 593 594 auto node = TestUtils::createNode(0, 0, 800, 800, 595 [](RenderProperties& props, RecordingCanvas& canvas) { 596 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); 597 { 598 canvas.drawRect(0, 0, 800, 800, SkPaint()); 599 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); 600 { 601 canvas.drawRect(0, 0, 400, 400, SkPaint()); 602 } 603 canvas.restore(); 604 } 605 canvas.restore(); 606 }); 607 608 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800, 609 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 610 SaveLayerNestedTestRenderer renderer; 611 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 612 EXPECT_EQ(10, renderer.getIndex()); 613} 614 615RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { 616 auto node = TestUtils::createNode(0, 0, 200, 200, 617 [](RenderProperties& props, RecordingCanvas& canvas) { 618 canvas.save(SaveFlags::MatrixClip); 619 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); 620 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); 621 622 // draw within save layer may still be recorded, but shouldn't be drawn 623 canvas.drawRect(200, 200, 400, 400, SkPaint()); 624 625 canvas.restore(); 626 canvas.restore(); 627 }); 628 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 629 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 630 631 FailRenderer renderer; 632 // should see no ops, even within the layer, since the layer should be rejected 633 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 634} 635 636RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { 637 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase { 638 public: 639 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 640 EXPECT_EQ(0, mIndex++); 641 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 642 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 643 EXPECT_TRUE(state.computedState.transform.isIdentity()); 644 } 645 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 646 EXPECT_EQ(1, mIndex++); 647 ASSERT_NE(nullptr, op.paint); 648 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); 649 } 650 void onRectOp(const RectOp& op, const BakedOpState& state) override { 651 EXPECT_EQ(2, mIndex++); 652 EXPECT_EQ(Rect(200, 200), op.unmappedBounds); 653 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 654 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 655 EXPECT_TRUE(state.computedState.transform.isIdentity()); 656 } 657 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 658 EXPECT_EQ(3, mIndex++); 659 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 660 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 661 EXPECT_TRUE(state.computedState.transform.isIdentity()); 662 } 663 }; 664 665 auto node = TestUtils::createNode(0, 0, 200, 200, 666 [](RenderProperties& props, RecordingCanvas& canvas) { 667 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 668 canvas.drawRect(0, 0, 200, 200, SkPaint()); 669 canvas.restore(); 670 }); 671 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 672 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 673 SaveLayerUnclippedSimpleTestRenderer renderer; 674 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 675 EXPECT_EQ(4, renderer.getIndex()); 676} 677 678RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { 679 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase { 680 public: 681 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 682 int index = mIndex++; 683 EXPECT_GT(4, index); 684 EXPECT_EQ(5, op.unmappedBounds.getWidth()); 685 EXPECT_EQ(5, op.unmappedBounds.getHeight()); 686 if (index == 0) { 687 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds); 688 } else if (index == 1) { 689 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds); 690 } else if (index == 2) { 691 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds); 692 } else if (index == 3) { 693 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds); 694 } 695 } 696 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 697 EXPECT_EQ(4, mIndex++); 698 ASSERT_EQ(op.vertexCount, 16u); 699 for (size_t i = 0; i < op.vertexCount; i++) { 700 auto v = op.vertices[i]; 701 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200); 702 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200); 703 } 704 } 705 void onRectOp(const RectOp& op, const BakedOpState& state) override { 706 EXPECT_EQ(5, mIndex++); 707 } 708 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 709 EXPECT_LT(5, mIndex++); 710 } 711 }; 712 713 auto node = TestUtils::createNode(0, 0, 200, 200, 714 [](RenderProperties& props, RecordingCanvas& canvas) { 715 716 int restoreTo = canvas.save(SaveFlags::MatrixClip); 717 canvas.scale(2, 2); 718 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); 719 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); 720 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); 721 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); 722 canvas.drawRect(0, 0, 100, 100, SkPaint()); 723 canvas.restoreToCount(restoreTo); 724 }); 725 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 726 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 727 SaveLayerUnclippedMergedClearsTestRenderer renderer; 728 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 729 EXPECT_EQ(10, renderer.getIndex()) 730 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect."; 731} 732 733RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { 734 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase { 735 public: 736 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 737 EXPECT_EQ(0, mIndex++); 738 } 739 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 740 EXPECT_EQ(1, mIndex++); 741 ASSERT_NE(nullptr, op.paint); 742 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); 743 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds) 744 << "Expect dirty rect as clip"; 745 ASSERT_NE(nullptr, state.computedState.clipState); 746 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect); 747 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 748 } 749 void onRectOp(const RectOp& op, const BakedOpState& state) override { 750 EXPECT_EQ(2, mIndex++); 751 } 752 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 753 EXPECT_EQ(3, mIndex++); 754 } 755 }; 756 757 auto node = TestUtils::createNode(0, 0, 200, 200, 758 [](RenderProperties& props, RecordingCanvas& canvas) { 759 // save smaller than clip, so we get unclipped behavior 760 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 761 canvas.drawRect(0, 0, 200, 200, SkPaint()); 762 canvas.restore(); 763 }); 764 765 // draw with partial screen dirty, and assert we see that rect later 766 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, 767 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 768 SaveLayerUnclippedClearClipTestRenderer renderer; 769 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 770 EXPECT_EQ(4, renderer.getIndex()); 771} 772 773RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { 774 auto node = TestUtils::createNode(0, 0, 200, 200, 775 [](RenderProperties& props, RecordingCanvas& canvas) { 776 // unclipped savelayer + rect both in area that won't intersect with dirty 777 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); 778 canvas.drawRect(100, 100, 200, 200, SkPaint()); 779 canvas.restore(); 780 }); 781 782 // draw with partial screen dirty that doesn't intersect with savelayer 783 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, 784 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 785 FailRenderer renderer; 786 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 787} 788 789/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as: 790 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer 791 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe 792 */ 793RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { 794 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { 795 public: 796 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 797 EXPECT_EQ(0, mIndex++); // savelayer first 798 return (OffscreenBuffer*)0xabcd; 799 } 800 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 801 int index = mIndex++; 802 EXPECT_TRUE(index == 1 || index == 7); 803 } 804 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 805 int index = mIndex++; 806 EXPECT_TRUE(index == 2 || index == 8); 807 } 808 void onRectOp(const RectOp& op, const BakedOpState& state) override { 809 EXPECT_EQ(3, mIndex++); 810 Matrix4 expected; 811 expected.loadTranslate(-100, -100, 0); 812 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds); 813 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 814 } 815 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 816 int index = mIndex++; 817 EXPECT_TRUE(index == 4 || index == 10); 818 } 819 void endLayer() override { 820 EXPECT_EQ(5, mIndex++); 821 } 822 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 823 EXPECT_EQ(6, mIndex++); 824 } 825 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 826 EXPECT_EQ(9, mIndex++); 827 } 828 void endFrame(const Rect& repaintRect) override { 829 EXPECT_EQ(11, mIndex++); 830 } 831 }; 832 833 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping 834 [](RenderProperties& props, RecordingCanvas& canvas) { 835 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped 836 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped 837 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped 838 canvas.drawRect(200, 200, 300, 300, SkPaint()); 839 canvas.restore(); 840 canvas.restore(); 841 canvas.restore(); 842 }); 843 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600, 844 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 845 SaveLayerUnclippedComplexTestRenderer renderer; 846 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 847 EXPECT_EQ(12, renderer.getIndex()); 848} 849 850RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { 851 class HwLayerSimpleTestRenderer : public TestRendererBase { 852 public: 853 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 854 EXPECT_EQ(0, mIndex++); 855 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 856 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 857 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 858 } 859 void onRectOp(const RectOp& op, const BakedOpState& state) override { 860 EXPECT_EQ(1, mIndex++); 861 862 EXPECT_TRUE(state.computedState.transform.isIdentity()) 863 << "Transform should be reset within layer"; 864 865 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 866 << "Damage rect should be used to clip layer content"; 867 } 868 void endLayer() override { 869 EXPECT_EQ(2, mIndex++); 870 } 871 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 872 EXPECT_EQ(3, mIndex++); 873 } 874 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 875 EXPECT_EQ(4, mIndex++); 876 } 877 void endFrame(const Rect& repaintRect) override { 878 EXPECT_EQ(5, mIndex++); 879 } 880 }; 881 882 auto node = TestUtils::createNode(10, 10, 110, 110, 883 [](RenderProperties& props, RecordingCanvas& canvas) { 884 props.mutateLayerProperties().setType(LayerType::RenderLayer); 885 SkPaint paint; 886 paint.setColor(SK_ColorWHITE); 887 canvas.drawRect(0, 0, 100, 100, paint); 888 }); 889 OffscreenBuffer** layerHandle = node->getLayerHandle(); 890 891 // create RenderNode's layer here in same way prepareTree would 892 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 893 *layerHandle = &layer; 894 895 auto syncedNodeList = TestUtils::createSyncedNodeList(node); 896 897 // only enqueue partial damage 898 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 899 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 900 901 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 902 syncedNodeList, sLightGeometry, Caches::getInstance()); 903 HwLayerSimpleTestRenderer renderer; 904 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 905 EXPECT_EQ(6, renderer.getIndex()); 906 907 // clean up layer pointer, so we can safely destruct RenderNode 908 *layerHandle = nullptr; 909} 910 911RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { 912 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: 913 * - startRepaintLayer(child), rect(grey), endLayer 914 * - startTemporaryLayer, drawLayer(child), endLayer 915 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer 916 * - startFrame, drawLayer(parent), endLayerb 917 */ 918 class HwLayerComplexTestRenderer : public TestRendererBase { 919 public: 920 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 921 EXPECT_EQ(3, mIndex++); // savelayer first 922 return (OffscreenBuffer*)0xabcd; 923 } 924 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 925 int index = mIndex++; 926 if (index == 0) { 927 // starting inner layer 928 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 929 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 930 } else if (index == 6) { 931 // starting outer layer 932 EXPECT_EQ(200u, offscreenBuffer->viewportWidth); 933 EXPECT_EQ(200u, offscreenBuffer->viewportHeight); 934 } else { ADD_FAILURE(); } 935 } 936 void onRectOp(const RectOp& op, const BakedOpState& state) override { 937 int index = mIndex++; 938 if (index == 1) { 939 // inner layer's rect (white) 940 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 941 } else if (index == 7) { 942 // outer layer's rect (grey) 943 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 944 } else { ADD_FAILURE(); } 945 } 946 void endLayer() override { 947 int index = mIndex++; 948 EXPECT_TRUE(index == 2 || index == 5 || index == 9); 949 } 950 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 951 EXPECT_EQ(10, mIndex++); 952 } 953 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 954 OffscreenBuffer* layer = *op.layerHandle; 955 int index = mIndex++; 956 if (index == 4) { 957 EXPECT_EQ(100u, layer->viewportWidth); 958 EXPECT_EQ(100u, layer->viewportHeight); 959 } else if (index == 8) { 960 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 961 } else if (index == 11) { 962 EXPECT_EQ(200u, layer->viewportWidth); 963 EXPECT_EQ(200u, layer->viewportHeight); 964 } else { ADD_FAILURE(); } 965 } 966 void endFrame(const Rect& repaintRect) override { 967 EXPECT_EQ(12, mIndex++); 968 } 969 }; 970 971 auto child = TestUtils::createNode(50, 50, 150, 150, 972 [](RenderProperties& props, RecordingCanvas& canvas) { 973 props.mutateLayerProperties().setType(LayerType::RenderLayer); 974 SkPaint paint; 975 paint.setColor(SK_ColorWHITE); 976 canvas.drawRect(0, 0, 100, 100, paint); 977 }); 978 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); 979 *(child->getLayerHandle()) = &childLayer; 980 981 RenderNode* childPtr = child.get(); 982 auto parent = TestUtils::createNode(0, 0, 200, 200, 983 [childPtr](RenderProperties& props, RecordingCanvas& canvas) { 984 props.mutateLayerProperties().setType(LayerType::RenderLayer); 985 SkPaint paint; 986 paint.setColor(SK_ColorDKGRAY); 987 canvas.drawRect(0, 0, 200, 200, paint); 988 989 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); 990 canvas.drawRenderNode(childPtr); 991 canvas.restore(); 992 }); 993 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); 994 *(parent->getLayerHandle()) = &parentLayer; 995 996 auto syncedList = TestUtils::createSyncedNodeList(parent); 997 998 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 999 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); 1000 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); 1001 1002 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1003 syncedList, sLightGeometry, Caches::getInstance()); 1004 HwLayerComplexTestRenderer renderer; 1005 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1006 EXPECT_EQ(13, renderer.getIndex()); 1007 1008 // clean up layer pointers, so we can safely destruct RenderNodes 1009 *(child->getLayerHandle()) = nullptr; 1010 *(parent->getLayerHandle()) = nullptr; 1011} 1012 1013static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { 1014 SkPaint paint; 1015 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel 1016 canvas->drawRect(0, 0, 100, 100, paint); 1017} 1018static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { 1019 auto node = TestUtils::createNode(0, 0, 100, 100, 1020 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { 1021 drawOrderedRect(&canvas, expectedDrawOrder); 1022 }); 1023 node->mutateStagingProperties().setTranslationZ(z); 1024 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); 1025 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1026} 1027RENDERTHREAD_TEST(FrameBuilder, zReorder) { 1028 class ZReorderTestRenderer : public TestRendererBase { 1029 public: 1030 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1031 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel 1032 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 1033 } 1034 }; 1035 1036 auto parent = TestUtils::createNode(0, 0, 100, 100, 1037 [](RenderProperties& props, RecordingCanvas& canvas) { 1038 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 1039 drawOrderedRect(&canvas, 1); 1040 canvas.insertReorderBarrier(true); 1041 drawOrderedNode(&canvas, 6, 2.0f); 1042 drawOrderedRect(&canvas, 3); 1043 drawOrderedNode(&canvas, 4, 0.0f); 1044 drawOrderedRect(&canvas, 5); 1045 drawOrderedNode(&canvas, 2, -2.0f); 1046 drawOrderedNode(&canvas, 7, 2.0f); 1047 canvas.insertReorderBarrier(false); 1048 drawOrderedRect(&canvas, 8); 1049 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 1050 }); 1051 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 1052 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1053 ZReorderTestRenderer renderer; 1054 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1055 EXPECT_EQ(10, renderer.getIndex()); 1056}; 1057 1058RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { 1059 static const int scrollX = 5; 1060 static const int scrollY = 10; 1061 class ProjectionReorderTestRenderer : public TestRendererBase { 1062 public: 1063 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1064 const int index = mIndex++; 1065 1066 Matrix4 expectedMatrix; 1067 switch (index) { 1068 case 0: 1069 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 1070 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1071 expectedMatrix.loadIdentity(); 1072 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1073 break; 1074 case 1: 1075 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 1076 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1077 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); 1078 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1079 EXPECT_EQ(Rect(-35, -30, 45, 50), 1080 Rect(state.computedState.localProjectionPathMask->getBounds())); 1081 break; 1082 case 2: 1083 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 1084 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 1085 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 1086 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1087 break; 1088 default: 1089 ADD_FAILURE(); 1090 } 1091 EXPECT_EQ(expectedMatrix, state.computedState.transform); 1092 } 1093 }; 1094 1095 /** 1096 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 1097 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 1098 * draw, but because it is projected backwards, it's drawn in between B and C. 1099 * 1100 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 1101 * (which isn't affected by scroll). 1102 */ 1103 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100, 1104 [](RenderProperties& properties, RecordingCanvas& canvas) { 1105 properties.setProjectionReceiver(true); 1106 // scroll doesn't apply to background, so undone via translationX/Y 1107 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1108 properties.setTranslationX(scrollX); 1109 properties.setTranslationY(scrollY); 1110 1111 SkPaint paint; 1112 paint.setColor(SK_ColorWHITE); 1113 canvas.drawRect(0, 0, 100, 100, paint); 1114 }); 1115 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50, 1116 [](RenderProperties& properties, RecordingCanvas& canvas) { 1117 properties.setProjectBackwards(true); 1118 properties.setClipToBounds(false); 1119 SkPaint paint; 1120 paint.setColor(SK_ColorDKGRAY); 1121 canvas.drawRect(-10, -10, 60, 60, paint); 1122 }); 1123 auto child = TestUtils::createNode(0, 50, 100, 100, 1124 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1125 SkPaint paint; 1126 paint.setColor(SK_ColorBLUE); 1127 canvas.drawRect(0, 0, 100, 50, paint); 1128 canvas.drawRenderNode(projectingRipple.get()); 1129 }); 1130 auto parent = TestUtils::createNode(0, 0, 100, 100, 1131 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1132 // Set a rect outline for the projecting ripple to be masked against. 1133 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 1134 1135 canvas.save(SaveFlags::MatrixClip); 1136 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1137 canvas.drawRenderNode(receiverBackground.get()); 1138 canvas.drawRenderNode(child.get()); 1139 canvas.restore(); 1140 }); 1141 1142 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 1143 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1144 ProjectionReorderTestRenderer renderer; 1145 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1146 EXPECT_EQ(3, renderer.getIndex()); 1147} 1148 1149RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { 1150 static const int scrollX = 5; 1151 static const int scrollY = 10; 1152 class ProjectionHwLayerTestRenderer : public TestRendererBase { 1153 public: 1154 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1155 EXPECT_EQ(0, mIndex++); 1156 } 1157 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 1158 EXPECT_EQ(1, mIndex++); 1159 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1160 } 1161 void endLayer() override { 1162 EXPECT_EQ(2, mIndex++); 1163 } 1164 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1165 EXPECT_EQ(3, mIndex++); 1166 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1167 } 1168 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1169 EXPECT_EQ(4, mIndex++); 1170 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1171 Matrix4 expected; 1172 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); 1173 EXPECT_EQ(expected, state.computedState.transform); 1174 EXPECT_EQ(Rect(-85, -80, 295, 300), 1175 Rect(state.computedState.localProjectionPathMask->getBounds())); 1176 } 1177 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1178 EXPECT_EQ(5, mIndex++); 1179 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1180 } 1181 }; 1182 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1183 [](RenderProperties& properties, RecordingCanvas& canvas) { 1184 properties.setProjectionReceiver(true); 1185 // scroll doesn't apply to background, so undone via translationX/Y 1186 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1187 properties.setTranslationX(scrollX); 1188 properties.setTranslationY(scrollY); 1189 1190 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1191 }); 1192 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1193 [](RenderProperties& properties, RecordingCanvas& canvas) { 1194 properties.setProjectBackwards(true); 1195 properties.setClipToBounds(false); 1196 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 1197 }); 1198 auto child = TestUtils::createNode(100, 100, 300, 300, 1199 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1200 properties.mutateLayerProperties().setType(LayerType::RenderLayer); 1201 canvas.drawRenderNode(projectingRipple.get()); 1202 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); 1203 }); 1204 auto parent = TestUtils::createNode(0, 0, 400, 400, 1205 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1206 // Set a rect outline for the projecting ripple to be masked against. 1207 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 1208 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1209 canvas.drawRenderNode(receiverBackground.get()); 1210 canvas.drawRenderNode(child.get()); 1211 }); 1212 1213 OffscreenBuffer** layerHandle = child->getLayerHandle(); 1214 1215 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1216 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1217 Matrix4 windowTransform; 1218 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin 1219 layer.setWindowTransform(windowTransform); 1220 *layerHandle = &layer; 1221 1222 auto syncedList = TestUtils::createSyncedNodeList(parent); 1223 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1224 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); 1225 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 1226 syncedList, sLightGeometry, Caches::getInstance()); 1227 ProjectionHwLayerTestRenderer renderer; 1228 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1229 EXPECT_EQ(6, renderer.getIndex()); 1230 1231 // clean up layer pointer, so we can safely destruct RenderNode 1232 *layerHandle = nullptr; 1233} 1234 1235RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { 1236 static const int scrollX = 500000; 1237 static const int scrollY = 0; 1238 class ProjectionChildScrollTestRenderer : public TestRendererBase { 1239 public: 1240 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1241 EXPECT_EQ(0, mIndex++); 1242 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1243 } 1244 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1245 EXPECT_EQ(1, mIndex++); 1246 ASSERT_NE(nullptr, state.computedState.clipState); 1247 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1248 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect); 1249 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1250 } 1251 }; 1252 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1253 [](RenderProperties& properties, RecordingCanvas& canvas) { 1254 properties.setProjectionReceiver(true); 1255 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1256 }); 1257 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1258 [](RenderProperties& properties, RecordingCanvas& canvas) { 1259 // scroll doesn't apply to background, so undone via translationX/Y 1260 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1261 properties.setTranslationX(scrollX); 1262 properties.setTranslationY(scrollY); 1263 properties.setProjectBackwards(true); 1264 properties.setClipToBounds(false); 1265 canvas.drawOval(0, 0, 200, 200, SkPaint()); 1266 }); 1267 auto child = TestUtils::createNode(0, 0, 400, 400, 1268 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1269 // Record time clip will be ignored by projectee 1270 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op); 1271 1272 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1273 canvas.drawRenderNode(projectingRipple.get()); 1274 }); 1275 auto parent = TestUtils::createNode(0, 0, 400, 400, 1276 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1277 canvas.drawRenderNode(receiverBackground.get()); 1278 canvas.drawRenderNode(child.get()); 1279 }); 1280 1281 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 1282 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1283 ProjectionChildScrollTestRenderer renderer; 1284 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1285 EXPECT_EQ(2, renderer.getIndex()); 1286} 1287 1288// creates a 100x100 shadow casting node with provided translationZ 1289static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1290 return TestUtils::createNode(0, 0, 100, 100, 1291 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1292 properties.setTranslationZ(translationZ); 1293 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1294 SkPaint paint; 1295 paint.setColor(SK_ColorWHITE); 1296 canvas.drawRect(0, 0, 100, 100, paint); 1297 }); 1298} 1299 1300RENDERTHREAD_TEST(FrameBuilder, shadow) { 1301 class ShadowTestRenderer : public TestRendererBase { 1302 public: 1303 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1304 EXPECT_EQ(0, mIndex++); 1305 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1306 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr)); 1307 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY); 1308 1309 Matrix4 expectedZ; 1310 expectedZ.loadTranslate(0, 0, 5); 1311 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ); 1312 } 1313 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1314 EXPECT_EQ(1, mIndex++); 1315 } 1316 }; 1317 1318 auto parent = TestUtils::createNode(0, 0, 200, 200, 1319 [](RenderProperties& props, RecordingCanvas& canvas) { 1320 canvas.insertReorderBarrier(true); 1321 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1322 }); 1323 1324 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1325 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1326 ShadowTestRenderer renderer; 1327 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1328 EXPECT_EQ(2, renderer.getIndex()); 1329} 1330 1331RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { 1332 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1333 public: 1334 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1335 EXPECT_EQ(0, mIndex++); 1336 return nullptr; 1337 } 1338 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1339 EXPECT_EQ(1, mIndex++); 1340 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1341 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1342 } 1343 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1344 EXPECT_EQ(2, mIndex++); 1345 } 1346 void endLayer() override { 1347 EXPECT_EQ(3, mIndex++); 1348 } 1349 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1350 EXPECT_EQ(4, mIndex++); 1351 } 1352 }; 1353 1354 auto parent = TestUtils::createNode(0, 0, 200, 200, 1355 [](RenderProperties& props, RecordingCanvas& canvas) { 1356 // save/restore outside of reorderBarrier, so they don't get moved out of place 1357 canvas.translate(20, 10); 1358 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); 1359 canvas.insertReorderBarrier(true); 1360 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1361 canvas.insertReorderBarrier(false); 1362 canvas.restoreToCount(count); 1363 }); 1364 1365 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1366 TestUtils::createSyncedNodeList(parent), 1367 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, 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}, Caches::getInstance()); 1420 ShadowHwLayerTestRenderer renderer; 1421 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1422 EXPECT_EQ(5, renderer.getIndex()); 1423 1424 // clean up layer pointer, so we can safely destruct RenderNode 1425 *layerHandle = nullptr; 1426} 1427 1428RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { 1429 class ShadowLayeringTestRenderer : public TestRendererBase { 1430 public: 1431 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1432 int index = mIndex++; 1433 EXPECT_TRUE(index == 0 || index == 1); 1434 } 1435 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1436 int index = mIndex++; 1437 EXPECT_TRUE(index == 2 || index == 3); 1438 } 1439 }; 1440 auto parent = TestUtils::createNode(0, 0, 200, 200, 1441 [](RenderProperties& props, RecordingCanvas& canvas) { 1442 canvas.insertReorderBarrier(true); 1443 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1444 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 1445 }); 1446 1447 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1448 TestUtils::createSyncedNodeList(parent), 1449 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1450 ShadowLayeringTestRenderer renderer; 1451 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1452 EXPECT_EQ(4, renderer.getIndex()); 1453} 1454 1455static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, 1456 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 1457 class PropertyTestRenderer : public TestRendererBase { 1458 public: 1459 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) 1460 : mCallback(callback) {} 1461 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1462 EXPECT_EQ(mIndex++, 0); 1463 mCallback(op, state); 1464 } 1465 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 1466 }; 1467 1468 auto node = TestUtils::createNode(0, 0, 100, 100, 1469 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 1470 propSetupCallback(props); 1471 SkPaint paint; 1472 paint.setColor(SK_ColorWHITE); 1473 canvas.drawRect(0, 0, 100, 100, paint); 1474 }); 1475 1476 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, 1477 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 1478 PropertyTestRenderer renderer(opValidateCallback); 1479 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1480 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 1481} 1482 1483RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 1484 testProperty([](RenderProperties& properties) { 1485 properties.setAlpha(0.5f); 1486 properties.setHasOverlappingRendering(false); 1487 }, [](const RectOp& op, const BakedOpState& state) { 1488 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 1489 }); 1490} 1491 1492RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { 1493 testProperty([](RenderProperties& properties) { 1494 properties.setClipToBounds(true); 1495 properties.setClipBounds(Rect(10, 20, 300, 400)); 1496 }, [](const RectOp& op, const BakedOpState& state) { 1497 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 1498 << "Clip rect should be intersection of node bounds and clip bounds"; 1499 }); 1500} 1501 1502RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { 1503 testProperty([](RenderProperties& properties) { 1504 properties.mutableRevealClip().set(true, 50, 50, 25); 1505 }, [](const RectOp& op, const BakedOpState& state) { 1506 ASSERT_NE(nullptr, state.roundRectClipState); 1507 EXPECT_TRUE(state.roundRectClipState->highPriority); 1508 EXPECT_EQ(25, state.roundRectClipState->radius); 1509 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 1510 }); 1511} 1512 1513RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { 1514 testProperty([](RenderProperties& properties) { 1515 properties.mutableOutline().setShouldClip(true); 1516 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 1517 }, [](const RectOp& op, const BakedOpState& state) { 1518 ASSERT_NE(nullptr, state.roundRectClipState); 1519 EXPECT_FALSE(state.roundRectClipState->highPriority); 1520 EXPECT_EQ(5, state.roundRectClipState->radius); 1521 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 1522 }); 1523} 1524 1525RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) { 1526 testProperty([](RenderProperties& properties) { 1527 properties.setLeftTopRightBottom(10, 10, 110, 110); 1528 1529 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 1530 properties.setStaticMatrix(&staticMatrix); 1531 1532 // ignored, since static overrides animation 1533 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 1534 properties.setAnimationMatrix(&animationMatrix); 1535 1536 properties.setTranslationX(10); 1537 properties.setTranslationY(20); 1538 properties.setScaleX(0.5f); 1539 properties.setScaleY(0.7f); 1540 }, [](const RectOp& op, const BakedOpState& state) { 1541 Matrix4 matrix; 1542 matrix.loadTranslate(10, 10, 0); // left, top 1543 matrix.scale(1.2f, 1.2f, 1); // static matrix 1544 // ignore animation matrix, since static overrides it 1545 1546 // translation xy 1547 matrix.translate(10, 20); 1548 1549 // scale xy (from default pivot - center) 1550 matrix.translate(50, 50); 1551 matrix.scale(0.5f, 0.7f, 1); 1552 matrix.translate(-50, -50); 1553 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 1554 << "Op draw matrix must match expected combination of transformation properties"; 1555 }); 1556} 1557 1558struct SaveLayerAlphaData { 1559 uint32_t layerWidth = 0; 1560 uint32_t layerHeight = 0; 1561 Rect rectClippedBounds; 1562 Matrix4 rectMatrix; 1563}; 1564/** 1565 * Constructs a view to hit the temporary layer alpha property implementation: 1566 * a) 0 < alpha < 1 1567 * b) too big for layer (larger than maxTextureSize) 1568 * c) overlapping rendering content 1569 * returning observed data about layer size and content clip/transform. 1570 * 1571 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 1572 * (for efficiency, and to fit in layer size constraints) based on parent clip. 1573 */ 1574void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 1575 std::function<void(RenderProperties&)> propSetupCallback) { 1576 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 1577 public: 1578 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) 1579 : mOutData(outData) {} 1580 1581 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1582 EXPECT_EQ(0, mIndex++); 1583 mOutData->layerWidth = width; 1584 mOutData->layerHeight = height; 1585 return nullptr; 1586 } 1587 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1588 EXPECT_EQ(1, mIndex++); 1589 1590 mOutData->rectClippedBounds = state.computedState.clippedBounds; 1591 mOutData->rectMatrix = state.computedState.transform; 1592 } 1593 void endLayer() override { 1594 EXPECT_EQ(2, mIndex++); 1595 } 1596 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1597 EXPECT_EQ(3, mIndex++); 1598 } 1599 private: 1600 SaveLayerAlphaData* mOutData; 1601 }; 1602 1603 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 1604 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 1605 auto node = TestUtils::createNode(0, 0, 10000, 10000, 1606 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 1607 properties.setHasOverlappingRendering(true); 1608 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 1609 // apply other properties 1610 propSetupCallback(properties); 1611 1612 SkPaint paint; 1613 paint.setColor(SK_ColorWHITE); 1614 canvas.drawRect(0, 0, 10000, 10000, paint); 1615 }); 1616 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height 1617 1618 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1619 nodes, sLightGeometry, Caches::getInstance()); 1620 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 1621 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1622 1623 // assert, since output won't be valid if we haven't seen a save layer triggered 1624 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 1625} 1626 1627RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 1628 SaveLayerAlphaData observedData; 1629 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1630 properties.setTranslationX(10); // offset rendering content 1631 properties.setTranslationY(-2000); // offset rendering content 1632 }); 1633 EXPECT_EQ(190u, observedData.layerWidth); 1634 EXPECT_EQ(200u, observedData.layerHeight); 1635 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 1636 << "expect content to be clipped to screen area"; 1637 Matrix4 expected; 1638 expected.loadTranslate(0, -2000, 0); 1639 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 1640 << "expect content to be translated as part of being clipped"; 1641} 1642 1643RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 1644 SaveLayerAlphaData observedData; 1645 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1646 // Translate and rotate the view so that the only visible part is the top left corner of 1647 // the view. It will form an isosceles right triangle with a long side length of 200 at the 1648 // bottom of the viewport. 1649 properties.setTranslationX(100); 1650 properties.setTranslationY(100); 1651 properties.setPivotX(0); 1652 properties.setPivotY(0); 1653 properties.setRotation(45); 1654 }); 1655 // ceil(sqrt(2) / 2 * 200) = 142 1656 EXPECT_EQ(142u, observedData.layerWidth); 1657 EXPECT_EQ(142u, observedData.layerHeight); 1658 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 1659 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1660} 1661 1662RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 1663 SaveLayerAlphaData observedData; 1664 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1665 properties.setPivotX(0); 1666 properties.setPivotY(0); 1667 properties.setScaleX(2); 1668 properties.setScaleY(0.5f); 1669 }); 1670 EXPECT_EQ(100u, observedData.layerWidth); 1671 EXPECT_EQ(400u, observedData.layerHeight); 1672 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 1673 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1674} 1675 1676} // namespace uirenderer 1677} // namespace android 1678