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