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