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