FrameBuilderTests.cpp revision 65182ccffc46a601bf22ffbbfa8df4e4df01102f
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 218// TODO: Disabled due to b/26793764 219TEST(FrameBuilder, DISABLED_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), sLightCenter); 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), sLightCenter); 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), sLightCenter); 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(SkCanvas::kMatrixClip_SaveFlag); 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), sLightCenter); 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(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 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), sLightCenter); 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), sLightCenter); 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, SkCanvas::kClipToLayer_SaveFlag); 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), sLightCenter); 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, SkCanvas::kClipToLayer_SaveFlag); 517 { 518 canvas.drawRect(0, 0, 800, 800, SkPaint()); 519 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); 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), sLightCenter); 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(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 539 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); 540 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); 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), sLightCenter); 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, (SkCanvas::SaveFlags)(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), sLightCenter); 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(SkCanvas::kMatrixClip_SaveFlag); 637 canvas.scale(2, 2); 638 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SkCanvas::kMatrixClip_SaveFlag); 639 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SkCanvas::kMatrixClip_SaveFlag); 640 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SkCanvas::kMatrixClip_SaveFlag); 641 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SkCanvas::kMatrixClip_SaveFlag); 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), sLightCenter); 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 653/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as: 654 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer 655 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe 656 */ 657TEST(FrameBuilder, saveLayerUnclipped_complex) { 658 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { 659 public: 660 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 661 EXPECT_EQ(0, mIndex++); // savelayer first 662 return (OffscreenBuffer*)0xabcd; 663 } 664 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 665 int index = mIndex++; 666 EXPECT_TRUE(index == 1 || index == 7); 667 } 668 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 669 int index = mIndex++; 670 EXPECT_TRUE(index == 2 || index == 8); 671 } 672 void onRectOp(const RectOp& op, const BakedOpState& state) override { 673 EXPECT_EQ(3, mIndex++); 674 Matrix4 expected; 675 expected.loadTranslate(-100, -100, 0); 676 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds); 677 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 678 } 679 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 680 int index = mIndex++; 681 EXPECT_TRUE(index == 4 || index == 10); 682 } 683 void endLayer() override { 684 EXPECT_EQ(5, mIndex++); 685 } 686 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 687 EXPECT_EQ(6, mIndex++); 688 } 689 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 690 EXPECT_EQ(9, mIndex++); 691 } 692 void endFrame(const Rect& repaintRect) override { 693 EXPECT_EQ(11, mIndex++); 694 } 695 }; 696 697 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping 698 [](RenderProperties& props, RecordingCanvas& canvas) { 699 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SkCanvas::SaveFlags)0); // unclipped 700 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); // clipped 701 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SkCanvas::SaveFlags)0); // unclipped 702 canvas.drawRect(200, 200, 300, 300, SkPaint()); 703 canvas.restore(); 704 canvas.restore(); 705 canvas.restore(); 706 }); 707 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600, 708 TestUtils::createSyncedNodeList(node), sLightCenter); 709 SaveLayerUnclippedComplexTestRenderer renderer; 710 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 711 EXPECT_EQ(12, renderer.getIndex()); 712} 713 714RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { 715 class HwLayerSimpleTestRenderer : public TestRendererBase { 716 public: 717 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 718 EXPECT_EQ(0, mIndex++); 719 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 720 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 721 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 722 } 723 void onRectOp(const RectOp& op, const BakedOpState& state) override { 724 EXPECT_EQ(1, mIndex++); 725 726 EXPECT_TRUE(state.computedState.transform.isIdentity()) 727 << "Transform should be reset within layer"; 728 729 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 730 << "Damage rect should be used to clip layer content"; 731 } 732 void endLayer() override { 733 EXPECT_EQ(2, mIndex++); 734 } 735 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 736 EXPECT_EQ(3, mIndex++); 737 } 738 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 739 EXPECT_EQ(4, mIndex++); 740 } 741 void endFrame(const Rect& repaintRect) override { 742 EXPECT_EQ(5, mIndex++); 743 } 744 }; 745 746 auto node = TestUtils::createNode(10, 10, 110, 110, 747 [](RenderProperties& props, RecordingCanvas& canvas) { 748 props.mutateLayerProperties().setType(LayerType::RenderLayer); 749 SkPaint paint; 750 paint.setColor(SK_ColorWHITE); 751 canvas.drawRect(0, 0, 100, 100, paint); 752 }); 753 OffscreenBuffer** layerHandle = node->getLayerHandle(); 754 755 // create RenderNode's layer here in same way prepareTree would 756 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 757 *layerHandle = &layer; 758 759 auto syncedNodeList = TestUtils::createSyncedNodeList(node); 760 761 // only enqueue partial damage 762 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 763 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 764 765 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 766 syncedNodeList, sLightCenter); 767 HwLayerSimpleTestRenderer renderer; 768 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 769 EXPECT_EQ(6, renderer.getIndex()); 770 771 // clean up layer pointer, so we can safely destruct RenderNode 772 *layerHandle = nullptr; 773} 774 775RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { 776 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: 777 * - startRepaintLayer(child), rect(grey), endLayer 778 * - startTemporaryLayer, drawLayer(child), endLayer 779 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer 780 * - startFrame, drawLayer(parent), endLayerb 781 */ 782 class HwLayerComplexTestRenderer : public TestRendererBase { 783 public: 784 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 785 EXPECT_EQ(3, mIndex++); // savelayer first 786 return (OffscreenBuffer*)0xabcd; 787 } 788 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 789 int index = mIndex++; 790 if (index == 0) { 791 // starting inner layer 792 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 793 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 794 } else if (index == 6) { 795 // starting outer layer 796 EXPECT_EQ(200u, offscreenBuffer->viewportWidth); 797 EXPECT_EQ(200u, offscreenBuffer->viewportHeight); 798 } else { ADD_FAILURE(); } 799 } 800 void onRectOp(const RectOp& op, const BakedOpState& state) override { 801 int index = mIndex++; 802 if (index == 1) { 803 // inner layer's rect (white) 804 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 805 } else if (index == 7) { 806 // outer layer's rect (grey) 807 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 808 } else { ADD_FAILURE(); } 809 } 810 void endLayer() override { 811 int index = mIndex++; 812 EXPECT_TRUE(index == 2 || index == 5 || index == 9); 813 } 814 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 815 EXPECT_EQ(10, mIndex++); 816 } 817 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 818 OffscreenBuffer* layer = *op.layerHandle; 819 int index = mIndex++; 820 if (index == 4) { 821 EXPECT_EQ(100u, layer->viewportWidth); 822 EXPECT_EQ(100u, layer->viewportHeight); 823 } else if (index == 8) { 824 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 825 } else if (index == 11) { 826 EXPECT_EQ(200u, layer->viewportWidth); 827 EXPECT_EQ(200u, layer->viewportHeight); 828 } else { ADD_FAILURE(); } 829 } 830 void endFrame(const Rect& repaintRect) override { 831 EXPECT_EQ(12, mIndex++); 832 } 833 }; 834 835 auto child = TestUtils::createNode(50, 50, 150, 150, 836 [](RenderProperties& props, RecordingCanvas& canvas) { 837 props.mutateLayerProperties().setType(LayerType::RenderLayer); 838 SkPaint paint; 839 paint.setColor(SK_ColorWHITE); 840 canvas.drawRect(0, 0, 100, 100, paint); 841 }); 842 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); 843 *(child->getLayerHandle()) = &childLayer; 844 845 RenderNode* childPtr = child.get(); 846 auto parent = TestUtils::createNode(0, 0, 200, 200, 847 [childPtr](RenderProperties& props, RecordingCanvas& canvas) { 848 props.mutateLayerProperties().setType(LayerType::RenderLayer); 849 SkPaint paint; 850 paint.setColor(SK_ColorDKGRAY); 851 canvas.drawRect(0, 0, 200, 200, paint); 852 853 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag); 854 canvas.drawRenderNode(childPtr); 855 canvas.restore(); 856 }); 857 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); 858 *(parent->getLayerHandle()) = &parentLayer; 859 860 auto syncedList = TestUtils::createSyncedNodeList(parent); 861 862 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 863 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); 864 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); 865 866 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 867 syncedList, sLightCenter); 868 HwLayerComplexTestRenderer renderer; 869 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 870 EXPECT_EQ(13, renderer.getIndex()); 871 872 // clean up layer pointers, so we can safely destruct RenderNodes 873 *(child->getLayerHandle()) = nullptr; 874 *(parent->getLayerHandle()) = nullptr; 875} 876 877static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { 878 SkPaint paint; 879 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel 880 canvas->drawRect(0, 0, 100, 100, paint); 881} 882static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { 883 auto node = TestUtils::createNode(0, 0, 100, 100, 884 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { 885 drawOrderedRect(&canvas, expectedDrawOrder); 886 }); 887 node->mutateStagingProperties().setTranslationZ(z); 888 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); 889 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 890} 891TEST(FrameBuilder, zReorder) { 892 class ZReorderTestRenderer : public TestRendererBase { 893 public: 894 void onRectOp(const RectOp& op, const BakedOpState& state) override { 895 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel 896 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 897 } 898 }; 899 900 auto parent = TestUtils::createNode(0, 0, 100, 100, 901 [](RenderProperties& props, RecordingCanvas& canvas) { 902 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 903 drawOrderedRect(&canvas, 1); 904 canvas.insertReorderBarrier(true); 905 drawOrderedNode(&canvas, 6, 2.0f); 906 drawOrderedRect(&canvas, 3); 907 drawOrderedNode(&canvas, 4, 0.0f); 908 drawOrderedRect(&canvas, 5); 909 drawOrderedNode(&canvas, 2, -2.0f); 910 drawOrderedNode(&canvas, 7, 2.0f); 911 canvas.insertReorderBarrier(false); 912 drawOrderedRect(&canvas, 8); 913 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 914 }); 915 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 916 TestUtils::createSyncedNodeList(parent), sLightCenter); 917 ZReorderTestRenderer renderer; 918 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 919 EXPECT_EQ(10, renderer.getIndex()); 920}; 921 922TEST(FrameBuilder, projectionReorder) { 923 static const int scrollX = 5; 924 static const int scrollY = 10; 925 class ProjectionReorderTestRenderer : public TestRendererBase { 926 public: 927 void onRectOp(const RectOp& op, const BakedOpState& state) override { 928 const int index = mIndex++; 929 930 Matrix4 expectedMatrix; 931 switch (index) { 932 case 0: 933 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 934 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 935 expectedMatrix.loadIdentity(); 936 break; 937 case 1: 938 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 939 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 940 expectedMatrix.loadTranslate(50, 50, 0); // TODO: should scroll be respected here? 941 break; 942 case 2: 943 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 944 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 945 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 946 break; 947 default: 948 ADD_FAILURE(); 949 } 950 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, state.computedState.transform); 951 } 952 }; 953 954 /** 955 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 956 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 957 * draw, but because it is projected backwards, it's drawn in between B and C. 958 * 959 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 960 * (which isn't affected by scroll). 961 */ 962 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100, 963 [](RenderProperties& properties, RecordingCanvas& canvas) { 964 properties.setProjectionReceiver(true); 965 // scroll doesn't apply to background, so undone via translationX/Y 966 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 967 properties.setTranslationX(scrollX); 968 properties.setTranslationY(scrollY); 969 970 SkPaint paint; 971 paint.setColor(SK_ColorWHITE); 972 canvas.drawRect(0, 0, 100, 100, paint); 973 }); 974 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50, 975 [](RenderProperties& properties, RecordingCanvas& canvas) { 976 properties.setProjectBackwards(true); 977 properties.setClipToBounds(false); 978 SkPaint paint; 979 paint.setColor(SK_ColorDKGRAY); 980 canvas.drawRect(-10, -10, 60, 60, paint); 981 }); 982 auto child = TestUtils::createNode(0, 50, 100, 100, 983 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 984 SkPaint paint; 985 paint.setColor(SK_ColorBLUE); 986 canvas.drawRect(0, 0, 100, 50, paint); 987 canvas.drawRenderNode(projectingRipple.get()); 988 }); 989 auto parent = TestUtils::createNode(0, 0, 100, 100, 990 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 991 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 992 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 993 canvas.drawRenderNode(receiverBackground.get()); 994 canvas.drawRenderNode(child.get()); 995 canvas.restore(); 996 }); 997 998 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 999 TestUtils::createSyncedNodeList(parent), sLightCenter); 1000 ProjectionReorderTestRenderer renderer; 1001 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1002 EXPECT_EQ(3, renderer.getIndex()); 1003} 1004 1005// creates a 100x100 shadow casting node with provided translationZ 1006static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1007 return TestUtils::createNode(0, 0, 100, 100, 1008 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1009 properties.setTranslationZ(translationZ); 1010 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1011 SkPaint paint; 1012 paint.setColor(SK_ColorWHITE); 1013 canvas.drawRect(0, 0, 100, 100, paint); 1014 }); 1015} 1016 1017TEST(FrameBuilder, shadow) { 1018 class ShadowTestRenderer : public TestRendererBase { 1019 public: 1020 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1021 EXPECT_EQ(0, mIndex++); 1022 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1023 EXPECT_TRUE(op.casterPath->isRect(nullptr)); 1024 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowMatrixXY); 1025 1026 Matrix4 expectedZ; 1027 expectedZ.loadTranslate(0, 0, 5); 1028 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowMatrixZ); 1029 } 1030 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1031 EXPECT_EQ(1, mIndex++); 1032 } 1033 }; 1034 1035 auto parent = TestUtils::createNode(0, 0, 200, 200, 1036 [](RenderProperties& props, RecordingCanvas& canvas) { 1037 canvas.insertReorderBarrier(true); 1038 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1039 }); 1040 1041 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1042 TestUtils::createSyncedNodeList(parent), sLightCenter); 1043 ShadowTestRenderer renderer; 1044 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1045 EXPECT_EQ(2, renderer.getIndex()); 1046} 1047 1048TEST(FrameBuilder, shadowSaveLayer) { 1049 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1050 public: 1051 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1052 EXPECT_EQ(0, mIndex++); 1053 return nullptr; 1054 } 1055 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1056 EXPECT_EQ(1, mIndex++); 1057 EXPECT_FLOAT_EQ(50, op.lightCenter.x); 1058 EXPECT_FLOAT_EQ(40, op.lightCenter.y); 1059 } 1060 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1061 EXPECT_EQ(2, mIndex++); 1062 } 1063 void endLayer() override { 1064 EXPECT_EQ(3, mIndex++); 1065 } 1066 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1067 EXPECT_EQ(4, mIndex++); 1068 } 1069 }; 1070 1071 auto parent = TestUtils::createNode(0, 0, 200, 200, 1072 [](RenderProperties& props, RecordingCanvas& canvas) { 1073 // save/restore outside of reorderBarrier, so they don't get moved out of place 1074 canvas.translate(20, 10); 1075 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag); 1076 canvas.insertReorderBarrier(true); 1077 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1078 canvas.insertReorderBarrier(false); 1079 canvas.restoreToCount(count); 1080 }); 1081 1082 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1083 TestUtils::createSyncedNodeList(parent), (Vector3) { 100, 100, 100 }); 1084 ShadowSaveLayerTestRenderer renderer; 1085 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1086 EXPECT_EQ(5, renderer.getIndex()); 1087} 1088 1089RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { 1090 class ShadowHwLayerTestRenderer : public TestRendererBase { 1091 public: 1092 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1093 EXPECT_EQ(0, mIndex++); 1094 } 1095 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1096 EXPECT_EQ(1, mIndex++); 1097 EXPECT_FLOAT_EQ(50, op.lightCenter.x); 1098 EXPECT_FLOAT_EQ(40, op.lightCenter.y); 1099 } 1100 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1101 EXPECT_EQ(2, mIndex++); 1102 } 1103 void endLayer() override { 1104 EXPECT_EQ(3, mIndex++); 1105 } 1106 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1107 EXPECT_EQ(4, mIndex++); 1108 } 1109 }; 1110 1111 auto parent = TestUtils::createNode(50, 60, 150, 160, 1112 [](RenderProperties& props, RecordingCanvas& canvas) { 1113 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1114 canvas.insertReorderBarrier(true); 1115 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); 1116 canvas.translate(20, 10); 1117 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1118 canvas.restore(); 1119 }); 1120 OffscreenBuffer** layerHandle = parent->getLayerHandle(); 1121 1122 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1123 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1124 Matrix4 windowTransform; 1125 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin 1126 layer.setWindowTransform(windowTransform); 1127 *layerHandle = &layer; 1128 1129 auto syncedList = TestUtils::createSyncedNodeList(parent); 1130 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1131 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); 1132 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1133 syncedList, (Vector3) { 100, 100, 100 }); 1134 ShadowHwLayerTestRenderer renderer; 1135 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1136 EXPECT_EQ(5, renderer.getIndex()); 1137 1138 // clean up layer pointer, so we can safely destruct RenderNode 1139 *layerHandle = nullptr; 1140} 1141 1142TEST(FrameBuilder, shadowLayering) { 1143 class ShadowLayeringTestRenderer : public TestRendererBase { 1144 public: 1145 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1146 int index = mIndex++; 1147 EXPECT_TRUE(index == 0 || index == 1); 1148 } 1149 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1150 int index = mIndex++; 1151 EXPECT_TRUE(index == 2 || index == 3); 1152 } 1153 }; 1154 auto parent = TestUtils::createNode(0, 0, 200, 200, 1155 [](RenderProperties& props, RecordingCanvas& canvas) { 1156 canvas.insertReorderBarrier(true); 1157 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1158 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 1159 }); 1160 1161 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1162 TestUtils::createSyncedNodeList(parent), sLightCenter); 1163 ShadowLayeringTestRenderer renderer; 1164 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1165 EXPECT_EQ(4, renderer.getIndex()); 1166} 1167 1168static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, 1169 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 1170 class PropertyTestRenderer : public TestRendererBase { 1171 public: 1172 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) 1173 : mCallback(callback) {} 1174 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1175 EXPECT_EQ(mIndex++, 0); 1176 mCallback(op, state); 1177 } 1178 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 1179 }; 1180 1181 auto node = TestUtils::createNode(0, 0, 100, 100, 1182 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 1183 propSetupCallback(props); 1184 SkPaint paint; 1185 paint.setColor(SK_ColorWHITE); 1186 canvas.drawRect(0, 0, 100, 100, paint); 1187 }); 1188 1189 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, 1190 TestUtils::createSyncedNodeList(node), sLightCenter); 1191 PropertyTestRenderer renderer(opValidateCallback); 1192 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1193 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 1194} 1195 1196TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 1197 testProperty([](RenderProperties& properties) { 1198 properties.setAlpha(0.5f); 1199 properties.setHasOverlappingRendering(false); 1200 }, [](const RectOp& op, const BakedOpState& state) { 1201 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 1202 }); 1203} 1204 1205TEST(FrameBuilder, renderPropClipping) { 1206 testProperty([](RenderProperties& properties) { 1207 properties.setClipToBounds(true); 1208 properties.setClipBounds(Rect(10, 20, 300, 400)); 1209 }, [](const RectOp& op, const BakedOpState& state) { 1210 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 1211 << "Clip rect should be intersection of node bounds and clip bounds"; 1212 }); 1213} 1214 1215TEST(FrameBuilder, renderPropRevealClip) { 1216 testProperty([](RenderProperties& properties) { 1217 properties.mutableRevealClip().set(true, 50, 50, 25); 1218 }, [](const RectOp& op, const BakedOpState& state) { 1219 ASSERT_NE(nullptr, state.roundRectClipState); 1220 EXPECT_TRUE(state.roundRectClipState->highPriority); 1221 EXPECT_EQ(25, state.roundRectClipState->radius); 1222 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 1223 }); 1224} 1225 1226TEST(FrameBuilder, renderPropOutlineClip) { 1227 testProperty([](RenderProperties& properties) { 1228 properties.mutableOutline().setShouldClip(true); 1229 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 1230 }, [](const RectOp& op, const BakedOpState& state) { 1231 ASSERT_NE(nullptr, state.roundRectClipState); 1232 EXPECT_FALSE(state.roundRectClipState->highPriority); 1233 EXPECT_EQ(5, state.roundRectClipState->radius); 1234 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 1235 }); 1236} 1237 1238TEST(FrameBuilder, renderPropTransform) { 1239 testProperty([](RenderProperties& properties) { 1240 properties.setLeftTopRightBottom(10, 10, 110, 110); 1241 1242 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 1243 properties.setStaticMatrix(&staticMatrix); 1244 1245 // ignored, since static overrides animation 1246 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 1247 properties.setAnimationMatrix(&animationMatrix); 1248 1249 properties.setTranslationX(10); 1250 properties.setTranslationY(20); 1251 properties.setScaleX(0.5f); 1252 properties.setScaleY(0.7f); 1253 }, [](const RectOp& op, const BakedOpState& state) { 1254 Matrix4 matrix; 1255 matrix.loadTranslate(10, 10, 0); // left, top 1256 matrix.scale(1.2f, 1.2f, 1); // static matrix 1257 // ignore animation matrix, since static overrides it 1258 1259 // translation xy 1260 matrix.translate(10, 20); 1261 1262 // scale xy (from default pivot - center) 1263 matrix.translate(50, 50); 1264 matrix.scale(0.5f, 0.7f, 1); 1265 matrix.translate(-50, -50); 1266 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 1267 << "Op draw matrix must match expected combination of transformation properties"; 1268 }); 1269} 1270 1271struct SaveLayerAlphaData { 1272 uint32_t layerWidth = 0; 1273 uint32_t layerHeight = 0; 1274 Rect rectClippedBounds; 1275 Matrix4 rectMatrix; 1276}; 1277/** 1278 * Constructs a view to hit the temporary layer alpha property implementation: 1279 * a) 0 < alpha < 1 1280 * b) too big for layer (larger than maxTextureSize) 1281 * c) overlapping rendering content 1282 * returning observed data about layer size and content clip/transform. 1283 * 1284 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 1285 * (for efficiency, and to fit in layer size constraints) based on parent clip. 1286 */ 1287void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 1288 std::function<void(RenderProperties&)> propSetupCallback) { 1289 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 1290 public: 1291 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) 1292 : mOutData(outData) {} 1293 1294 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1295 EXPECT_EQ(0, mIndex++); 1296 mOutData->layerWidth = width; 1297 mOutData->layerHeight = height; 1298 return nullptr; 1299 } 1300 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1301 EXPECT_EQ(1, mIndex++); 1302 1303 mOutData->rectClippedBounds = state.computedState.clippedBounds; 1304 mOutData->rectMatrix = state.computedState.transform; 1305 } 1306 void endLayer() override { 1307 EXPECT_EQ(2, mIndex++); 1308 } 1309 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1310 EXPECT_EQ(3, mIndex++); 1311 } 1312 private: 1313 SaveLayerAlphaData* mOutData; 1314 }; 1315 1316 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 1317 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 1318 auto node = TestUtils::createNode(0, 0, 10000, 10000, 1319 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 1320 properties.setHasOverlappingRendering(true); 1321 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 1322 // apply other properties 1323 propSetupCallback(properties); 1324 1325 SkPaint paint; 1326 paint.setColor(SK_ColorWHITE); 1327 canvas.drawRect(0, 0, 10000, 10000, paint); 1328 }); 1329 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height 1330 1331 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter); 1332 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 1333 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1334 1335 // assert, since output won't be valid if we haven't seen a save layer triggered 1336 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 1337} 1338 1339TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 1340 SaveLayerAlphaData observedData; 1341 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1342 properties.setTranslationX(10); // offset rendering content 1343 properties.setTranslationY(-2000); // offset rendering content 1344 }); 1345 EXPECT_EQ(190u, observedData.layerWidth); 1346 EXPECT_EQ(200u, observedData.layerHeight); 1347 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 1348 << "expect content to be clipped to screen area"; 1349 Matrix4 expected; 1350 expected.loadTranslate(0, -2000, 0); 1351 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 1352 << "expect content to be translated as part of being clipped"; 1353} 1354 1355TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 1356 SaveLayerAlphaData observedData; 1357 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1358 // Translate and rotate the view so that the only visible part is the top left corner of 1359 // the view. It will form an isosceles right triangle with a long side length of 200 at the 1360 // bottom of the viewport. 1361 properties.setTranslationX(100); 1362 properties.setTranslationY(100); 1363 properties.setPivotX(0); 1364 properties.setPivotY(0); 1365 properties.setRotation(45); 1366 }); 1367 // ceil(sqrt(2) / 2 * 200) = 142 1368 EXPECT_EQ(142u, observedData.layerWidth); 1369 EXPECT_EQ(142u, observedData.layerHeight); 1370 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 1371 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1372} 1373 1374TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 1375 SaveLayerAlphaData observedData; 1376 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1377 properties.setPivotX(0); 1378 properties.setPivotY(0); 1379 properties.setScaleX(2); 1380 properties.setScaleY(0.5f); 1381 }); 1382 EXPECT_EQ(100u, observedData.layerWidth); 1383 EXPECT_EQ(400u, observedData.layerHeight); 1384 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 1385 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1386} 1387 1388} // namespace uirenderer 1389} // namespace android 1390