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