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