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