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