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 <GlLayer.h> 23#include <LayerUpdateQueue.h> 24#include <RecordedOp.h> 25#include <RecordingCanvas.h> 26#include <tests/common/TestUtils.h> 27 28#include <unordered_map> 29 30namespace android { 31namespace uirenderer { 32 33const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50}; 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() << "Temporary layers not expected in this test"; 51 return nullptr; 52 } 53 virtual void recycleTemporaryLayer(OffscreenBuffer*) { 54 ADD_FAILURE() << "Temporary layers not expected in this test"; 55 } 56 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) { 57 ADD_FAILURE() << "Layer repaint not expected in this test"; 58 } 59 virtual void endLayer() { ADD_FAILURE() << "Layer updates not expected in this test"; } 60 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {} 61 virtual void endFrame(const Rect& repaintRect) {} 62 63// define virtual defaults for single draw methods 64#define X(Type) \ 65 virtual void on##Type(const Type&, const BakedOpState&) { \ 66 ADD_FAILURE() << #Type " not expected in this test"; \ 67 } 68 MAP_RENDERABLE_OPS(X) 69#undef X 70 71// define virtual defaults for merged draw methods 72#define X(Type) \ 73 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \ 74 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \ 75 } 76 MAP_MERGEABLE_OPS(X) 77#undef X 78 79 int getIndex() { return mIndex; } 80 81protected: 82 int mIndex = 0; 83}; 84 85/** 86 * Dispatches all static methods to similar formed methods on renderer, which fail by default but 87 * are overridden by subclasses per test. 88 */ 89class TestDispatcher { 90public: 91// define single op methods, which redirect to TestRendererBase 92#define X(Type) \ 93 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \ 94 renderer.on##Type(op, state); \ 95 } 96 MAP_RENDERABLE_OPS(X); 97#undef X 98 99// define merged op methods, which redirect to TestRendererBase 100#define X(Type) \ 101 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \ 102 renderer.onMerged##Type##s(opList); \ 103 } 104 MAP_MERGEABLE_OPS(X); 105#undef X 106}; 107 108class FailRenderer : public TestRendererBase {}; 109 110RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) { 111 class SimpleTestRenderer : public TestRendererBase { 112 public: 113 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 114 EXPECT_EQ(0, mIndex++); 115 EXPECT_EQ(100u, width); 116 EXPECT_EQ(200u, height); 117 } 118 void onRectOp(const RectOp& op, const BakedOpState& state) override { 119 EXPECT_EQ(1, mIndex++); 120 } 121 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 122 EXPECT_EQ(2, mIndex++); 123 } 124 void endFrame(const Rect& repaintRect) override { EXPECT_EQ(3, mIndex++); } 125 }; 126 127 auto node = TestUtils::createNode<RecordingCanvas>( 128 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 129 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); 130 canvas.drawRect(0, 0, 100, 200, SkPaint()); 131 canvas.drawBitmap(*bitmap, 10, 10, nullptr); 132 }); 133 FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, 134 Caches::getInstance()); 135 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 136 137 SimpleTestRenderer renderer; 138 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 139 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end 140} 141 142RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) { 143 class SimpleStrokeTestRenderer : public TestRendererBase { 144 public: 145 void onPointsOp(const PointsOp& op, const BakedOpState& state) override { 146 EXPECT_EQ(0, mIndex++); 147 // even though initial bounds are empty... 148 EXPECT_TRUE(op.unmappedBounds.isEmpty()) 149 << "initial bounds should be empty, since they're unstroked"; 150 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds) 151 << "final bounds should account for stroke"; 152 } 153 }; 154 155 auto node = TestUtils::createNode<RecordingCanvas>( 156 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 157 SkPaint strokedPaint; 158 strokedPaint.setStrokeWidth(10); 159 canvas.drawPoint(50, 50, strokedPaint); 160 }); 161 FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, 162 Caches::getInstance()); 163 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 164 165 SimpleStrokeTestRenderer renderer; 166 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 167 EXPECT_EQ(1, renderer.getIndex()); 168} 169 170RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, arcStrokeClip) { 171 class ArcStrokeClipTestRenderer : public TestRendererBase { 172 public: 173 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 174 EXPECT_EQ(0, mIndex++); 175 EXPECT_EQ(Rect(25, 25, 175, 175), op.unmappedBounds); 176 EXPECT_EQ(Rect(25, 25, 175, 175), state.computedState.clippedBounds); 177 EXPECT_EQ(OpClipSideFlags::Full, state.computedState.clipSideFlags) 178 << "Arc op clipped conservatively, since path texture may be expanded"; 179 } 180 }; 181 182 auto node = TestUtils::createNode<RecordingCanvas>( 183 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 184 canvas.clipRect(25, 25, 175, 175, SkClipOp::kIntersect); 185 SkPaint aaPaint; 186 aaPaint.setAntiAlias(true); 187 canvas.drawArc(25, 25, 175, 175, 40, 180, true, aaPaint); 188 }); 189 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 190 Caches::getInstance()); 191 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 192 193 ArcStrokeClipTestRenderer renderer; 194 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 195 EXPECT_EQ(1, renderer.getIndex()); 196} 197 198RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) { 199 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, 200 RecordingCanvas& canvas) { 201 canvas.save(SaveFlags::MatrixClip); 202 canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty 203 canvas.drawRect(0, 0, 400, 400, SkPaint()); 204 canvas.restore(); 205 }); 206 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 207 Caches::getInstance()); 208 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 209 210 FailRenderer renderer; 211 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 212} 213 214RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) { 215 const int LOOPS = 5; 216 class SimpleBatchingTestRenderer : public TestRendererBase { 217 public: 218 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 219 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects"; 220 } 221 void onRectOp(const RectOp& op, const BakedOpState& state) override { 222 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps"; 223 } 224 }; 225 226 auto node = TestUtils::createNode<RecordingCanvas>( 227 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 228 229 sk_sp<Bitmap> bitmap(TestUtils::createBitmap( 230 10, 10, 231 kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap 232 233 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. 234 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. 235 canvas.save(SaveFlags::MatrixClip); 236 for (int i = 0; i < LOOPS; i++) { 237 canvas.translate(0, 10); 238 canvas.drawRect(0, 0, 10, 10, SkPaint()); 239 canvas.drawBitmap(*bitmap, 5, 0, nullptr); 240 } 241 canvas.restore(); 242 }); 243 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 244 Caches::getInstance()); 245 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 246 247 SimpleBatchingTestRenderer renderer; 248 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 249 EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count"; 250} 251 252RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) { 253 class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase { 254 public: 255 void onRectOp(const RectOp& op, const BakedOpState& state) override { 256 EXPECT_EQ(0, mIndex++); 257 EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds); 258 EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, 259 state.computedState.clipSideFlags); 260 } 261 }; 262 263 auto node = TestUtils::createNode<RecordingCanvas>( 264 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 265 canvas.drawRect(0, 0, 100, 100, SkPaint()); 266 }); 267 268 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 269 Caches::getInstance()); 270 frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node 271 *TestUtils::getSyncedNode(node)); 272 273 DeferRenderNodeTranslateClipTestRenderer renderer; 274 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 275 EXPECT_EQ(1, renderer.getIndex()); 276} 277 278RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) { 279 class DeferRenderNodeSceneTestRenderer : public TestRendererBase { 280 public: 281 void onRectOp(const RectOp& op, const BakedOpState& state) override { 282 const Rect& clippedBounds = state.computedState.clippedBounds; 283 Matrix4 expected; 284 switch (mIndex++) { 285 case 0: 286 // background - left side 287 EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds); 288 expected.loadTranslate(100, 100, 0); 289 break; 290 case 1: 291 // background - top side 292 EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds); 293 expected.loadTranslate(100, 100, 0); 294 break; 295 case 2: 296 // content 297 EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds); 298 expected.loadTranslate(-50, -50, 0); 299 break; 300 case 3: 301 // overlay 302 EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds); 303 break; 304 default: 305 ADD_FAILURE() << "Too many rects observed"; 306 } 307 EXPECT_EQ(expected, state.computedState.transform); 308 } 309 }; 310 311 std::vector<sp<RenderNode>> nodes; 312 SkPaint transparentPaint; 313 transparentPaint.setAlpha(128); 314 315 // backdrop 316 nodes.push_back(TestUtils::createNode<RecordingCanvas>( 317 100, 100, 700, 500, // 600x400 318 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 319 canvas.drawRect(0, 0, 600, 400, transparentPaint); 320 })); 321 322 // content 323 Rect contentDrawBounds(150, 150, 650, 450); // 500x300 324 nodes.push_back(TestUtils::createNode<RecordingCanvas>( 325 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 326 canvas.drawRect(0, 0, 800, 600, transparentPaint); 327 })); 328 329 // overlay 330 nodes.push_back(TestUtils::createNode<RecordingCanvas>( 331 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { 332 canvas.drawRect(0, 0, 800, 200, transparentPaint); 333 })); 334 335 for (auto& node : nodes) { 336 TestUtils::syncHierarchyPropertiesAndDisplayList(node); 337 } 338 339 { 340 FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry, 341 Caches::getInstance()); 342 frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds); 343 344 DeferRenderNodeSceneTestRenderer renderer; 345 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 346 EXPECT_EQ(4, renderer.getIndex()); 347 } 348 349 for (auto& node : nodes) { 350 EXPECT_TRUE(node->isValid()); 351 EXPECT_FALSE(node->nothingToDraw()); 352 node->setStagingDisplayList(nullptr); 353 EXPECT_FALSE(node->isValid()); 354 EXPECT_FALSE(node->nothingToDraw()); 355 node->destroyHardwareResources(); 356 EXPECT_TRUE(node->nothingToDraw()); 357 EXPECT_FALSE(node->isValid()); 358 } 359 360 { 361 // Validate no crashes if any nodes are missing DisplayLists 362 FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry, 363 Caches::getInstance()); 364 frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds); 365 366 FailRenderer renderer; 367 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 368 } 369} 370 371RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_noFbo0) { 372 class EmptyNoFbo0TestRenderer : public TestRendererBase { 373 public: 374 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 375 ADD_FAILURE() << "Primary frame draw not expected in this test"; 376 } 377 void endFrame(const Rect& repaintRect) override { 378 ADD_FAILURE() << "Primary frame draw not expected in this test"; 379 } 380 }; 381 382 // Use layer update constructor, so no work is enqueued for Fbo0 383 LayerUpdateQueue emptyLayerUpdateQueue; 384 FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance()); 385 EmptyNoFbo0TestRenderer renderer; 386 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 387} 388 389RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) { 390 class EmptyWithFbo0TestRenderer : public TestRendererBase { 391 public: 392 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 393 EXPECT_EQ(0, mIndex++); 394 } 395 void endFrame(const Rect& repaintRect) override { EXPECT_EQ(1, mIndex++); } 396 }; 397 auto node = TestUtils::createNode<RecordingCanvas>( 398 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { 399 // no drawn content 400 }); 401 402 // Draw, but pass node without draw content, so no work is done for primary frame 403 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 404 Caches::getInstance()); 405 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 406 407 EmptyWithFbo0TestRenderer renderer; 408 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 409 EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," 410 " but fbo0 update lifecycle should still be observed"; 411} 412 413RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) { 414 class AvoidOverdrawRectsTestRenderer : public TestRendererBase { 415 public: 416 void onRectOp(const RectOp& op, const BakedOpState& state) override { 417 EXPECT_EQ(mIndex++, 0) << "Should be one rect"; 418 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds) 419 << "Last rect should occlude others."; 420 } 421 }; 422 auto node = TestUtils::createNode<RecordingCanvas>( 423 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 424 canvas.drawRect(0, 0, 200, 200, SkPaint()); 425 canvas.drawRect(0, 0, 200, 200, SkPaint()); 426 canvas.drawRect(10, 10, 190, 190, SkPaint()); 427 }); 428 429 // Damage (and therefore clip) is same as last draw, subset of renderable area. 430 // This means last op occludes other contents, and they'll be rejected to avoid overdraw. 431 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, sLightGeometry, 432 Caches::getInstance()); 433 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 434 435 EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) 436 << "Recording must not have rejected ops, in order for this test to be valid"; 437 438 AvoidOverdrawRectsTestRenderer renderer; 439 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 440 EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op"; 441} 442 443RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_bitmaps) { 444 static sk_sp<Bitmap> opaqueBitmap( 445 TestUtils::createBitmap(50, 50, SkColorType::kRGB_565_SkColorType)); 446 static sk_sp<Bitmap> transpBitmap( 447 TestUtils::createBitmap(50, 50, SkColorType::kAlpha_8_SkColorType)); 448 class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { 449 public: 450 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 451 switch (mIndex++) { 452 case 0: 453 EXPECT_EQ(opaqueBitmap.get(), op.bitmap); 454 break; 455 case 1: 456 EXPECT_EQ(transpBitmap.get(), op.bitmap); 457 break; 458 default: 459 ADD_FAILURE() << "Only two ops expected."; 460 } 461 } 462 }; 463 464 auto node = TestUtils::createNode<RecordingCanvas>( 465 0, 0, 50, 50, [](RenderProperties& props, RecordingCanvas& canvas) { 466 canvas.drawRect(0, 0, 50, 50, SkPaint()); 467 canvas.drawRect(0, 0, 50, 50, SkPaint()); 468 canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); 469 470 // only the below draws should remain, since they're 471 canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr); 472 canvas.drawBitmap(*transpBitmap, 0, 0, nullptr); 473 }); 474 FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, sLightGeometry, 475 Caches::getInstance()); 476 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 477 478 EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) 479 << "Recording must not have rejected ops, in order for this test to be valid"; 480 481 AvoidOverdrawBitmapsTestRenderer renderer; 482 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 483 EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops"; 484} 485 486RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) { 487 class ClippedMergingTestRenderer : public TestRendererBase { 488 public: 489 void onMergedBitmapOps(const MergedBakedOpList& opList) override { 490 EXPECT_EQ(0, mIndex); 491 mIndex += opList.count; 492 EXPECT_EQ(4u, opList.count); 493 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip); 494 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right, 495 opList.clipSideFlags); 496 } 497 }; 498 auto node = TestUtils::createNode<RecordingCanvas>( 499 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 500 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20)); 501 502 // left side clipped (to inset left half) 503 canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace_deprecated); 504 canvas.drawBitmap(*bitmap, 0, 40, nullptr); 505 506 // top side clipped (to inset top half) 507 canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace_deprecated); 508 canvas.drawBitmap(*bitmap, 40, 0, nullptr); 509 510 // right side clipped (to inset right half) 511 canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace_deprecated); 512 canvas.drawBitmap(*bitmap, 80, 40, nullptr); 513 514 // bottom not clipped, just abutting (inset bottom half) 515 canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace_deprecated); 516 canvas.drawBitmap(*bitmap, 40, 70, nullptr); 517 }); 518 519 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 520 Caches::getInstance()); 521 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 522 523 ClippedMergingTestRenderer renderer; 524 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 525 EXPECT_EQ(4, renderer.getIndex()); 526} 527 528RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) { 529 class RegionClipStopsMergeTestRenderer : public TestRendererBase { 530 public: 531 void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; } 532 }; 533 auto node = TestUtils::createNode<RecordingCanvas>( 534 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) { 535 SkPath path; 536 path.addCircle(200, 200, 200, SkPath::kCW_Direction); 537 canvas.save(SaveFlags::MatrixClip); 538 canvas.clipPath(&path, SkClipOp::kIntersect); 539 SkPaint paint; 540 paint.setAntiAlias(true); 541 paint.setTextSize(50); 542 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); 543 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200); 544 canvas.restore(); 545 }); 546 547 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, 548 Caches::getInstance()); 549 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 550 551 RegionClipStopsMergeTestRenderer renderer; 552 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 553 EXPECT_EQ(2, renderer.getIndex()); 554} 555 556RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) { 557 class TextMergingTestRenderer : public TestRendererBase { 558 public: 559 void onMergedTextOps(const MergedBakedOpList& opList) override { 560 EXPECT_EQ(0, mIndex); 561 mIndex += opList.count; 562 EXPECT_EQ(2u, opList.count); 563 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags); 564 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags); 565 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags); 566 } 567 }; 568 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, [](RenderProperties& props, 569 RecordingCanvas& canvas) { 570 SkPaint paint; 571 paint.setAntiAlias(true); 572 paint.setTextSize(50); 573 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped 574 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped 575 }); 576 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, 577 Caches::getInstance()); 578 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 579 580 TextMergingTestRenderer renderer; 581 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 582 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; 583} 584 585RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) { 586 const int LOOPS = 5; 587 class TextStrikethroughTestRenderer : public TestRendererBase { 588 public: 589 void onRectOp(const RectOp& op, const BakedOpState& state) override { 590 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text"; 591 } 592 void onMergedTextOps(const MergedBakedOpList& opList) override { 593 EXPECT_EQ(0, mIndex); 594 mIndex += opList.count; 595 EXPECT_EQ(5u, opList.count); 596 } 597 }; 598 auto node = TestUtils::createNode<RecordingCanvas>( 599 0, 0, 200, 2000, [](RenderProperties& props, RecordingCanvas& canvas) { 600 SkPaint textPaint; 601 textPaint.setAntiAlias(true); 602 textPaint.setTextSize(20); 603 textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag); 604 for (int i = 0; i < LOOPS; i++) { 605 TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); 606 } 607 }); 608 609 FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, sLightGeometry, 610 Caches::getInstance()); 611 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 612 613 TextStrikethroughTestRenderer renderer; 614 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 615 EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count"; 616} 617 618static auto styles = {SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style}; 619 620RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) { 621 class TextStyleTestRenderer : public TestRendererBase { 622 public: 623 void onMergedTextOps(const MergedBakedOpList& opList) override { 624 ASSERT_EQ(0, mIndex); 625 ASSERT_EQ(3u, opList.count); 626 mIndex += opList.count; 627 628 int index = 0; 629 for (auto style : styles) { 630 auto state = opList.states[index++]; 631 ASSERT_EQ(style, state->op->paint->getStyle()) 632 << "Remainder of validation relies upon stable merged order"; 633 ASSERT_EQ(0, state->computedState.clipSideFlags) 634 << "Clipped bounds validation requires unclipped ops"; 635 } 636 637 Rect fill = opList.states[0]->computedState.clippedBounds; 638 Rect stroke = opList.states[1]->computedState.clippedBounds; 639 EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds) 640 << "Stroke+Fill should be same as stroke"; 641 642 EXPECT_TRUE(stroke.contains(fill)); 643 EXPECT_FALSE(fill.contains(stroke)); 644 645 // outset by half the stroke width 646 Rect outsetFill(fill); 647 outsetFill.outset(5); 648 EXPECT_EQ(stroke, outsetFill); 649 } 650 }; 651 auto node = TestUtils::createNode<RecordingCanvas>( 652 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) { 653 SkPaint paint; 654 paint.setAntiAlias(true); 655 paint.setTextSize(50); 656 paint.setStrokeWidth(10); 657 658 // draw 3 copies of the same text overlapping, each with a different style. 659 // They'll get merged, but with 660 for (auto style : styles) { 661 paint.setStyle(style); 662 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); 663 } 664 }); 665 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, 666 Caches::getInstance()); 667 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 668 TextStyleTestRenderer renderer; 669 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 670 EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops"; 671} 672 673RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { 674 class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase { 675 public: 676 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 677 EXPECT_EQ(0, mIndex++); 678 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect()); 679 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds); 680 681 Matrix4 expected; 682 expected.loadTranslate(5, 5, 0); 683 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 684 } 685 }; 686 687 auto layerUpdater = 688 TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); 689 690 auto node = TestUtils::createNode<RecordingCanvas>( 691 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 692 canvas.save(SaveFlags::MatrixClip); 693 canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect); 694 canvas.drawLayer(layerUpdater.get()); 695 canvas.restore(); 696 }); 697 698 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 699 Caches::getInstance()); 700 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 701 702 TextureLayerClipLocalMatrixTestRenderer renderer; 703 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 704 EXPECT_EQ(1, renderer.getIndex()); 705} 706 707RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) { 708 class TextureLayerCombineMatricesTestRenderer : public TestRendererBase { 709 public: 710 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 711 EXPECT_EQ(0, mIndex++); 712 713 Matrix4 expected; 714 expected.loadTranslate(35, 45, 0); 715 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 716 } 717 }; 718 719 auto layerUpdater = 720 TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); 721 722 auto node = TestUtils::createNode<RecordingCanvas>( 723 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 724 canvas.save(SaveFlags::MatrixClip); 725 canvas.translate(30, 40); 726 canvas.drawLayer(layerUpdater.get()); 727 canvas.restore(); 728 }); 729 730 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 731 Caches::getInstance()); 732 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 733 734 TextureLayerCombineMatricesTestRenderer renderer; 735 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 736 EXPECT_EQ(1, renderer.getIndex()); 737} 738 739RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) { 740 auto layerUpdater = 741 TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5)); 742 EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi()); 743 744 GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); 745 glLayer->setRenderTarget(GL_NONE); // Should be rejected 746 747 auto node = TestUtils::createNode<RecordingCanvas>( 748 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 749 canvas.drawLayer(layerUpdater.get()); 750 }); 751 752 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 753 Caches::getInstance()); 754 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 755 756 FailRenderer renderer; 757 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 758} 759 760RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) { 761 class FunctorTestRenderer : public TestRendererBase { 762 public: 763 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override { 764 EXPECT_EQ(0, mIndex++); 765 } 766 }; 767 Functor noopFunctor; 768 769 // 1 million pixel tall view, scrolled down 80% 770 auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>( 771 0, 0, 400, 1000000, [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { 772 canvas.translate(0, -800000); 773 canvas.callDrawGLFunction(&noopFunctor, nullptr); 774 }); 775 776 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 777 Caches::getInstance()); 778 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView)); 779 780 FunctorTestRenderer renderer; 781 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 782 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected"; 783} 784 785RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) { 786 class ColorTestRenderer : public TestRendererBase { 787 public: 788 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 789 EXPECT_EQ(0, mIndex++); 790 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds) 791 << "Color op should be expanded to bounds of surrounding"; 792 } 793 }; 794 795 auto unclippedColorView = TestUtils::createNode<RecordingCanvas>( 796 0, 0, 10, 10, [](RenderProperties& props, RecordingCanvas& canvas) { 797 props.setClipToBounds(false); 798 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 799 }); 800 801 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 802 Caches::getInstance()); 803 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView)); 804 805 ColorTestRenderer renderer; 806 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 807 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected"; 808} 809 810RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) { 811 class RenderNodeTestRenderer : public TestRendererBase { 812 public: 813 void onRectOp(const RectOp& op, const BakedOpState& state) override { 814 switch (mIndex++) { 815 case 0: 816 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 817 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 818 break; 819 case 1: 820 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds); 821 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 822 break; 823 default: 824 ADD_FAILURE(); 825 } 826 } 827 }; 828 829 auto child = TestUtils::createNode<RecordingCanvas>( 830 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { 831 SkPaint paint; 832 paint.setColor(SK_ColorWHITE); 833 canvas.drawRect(0, 0, 100, 100, paint); 834 }); 835 836 auto parent = TestUtils::createNode<RecordingCanvas>( 837 0, 0, 200, 200, [&child](RenderProperties& props, RecordingCanvas& canvas) { 838 SkPaint paint; 839 paint.setColor(SK_ColorDKGRAY); 840 canvas.drawRect(0, 0, 200, 200, paint); 841 842 canvas.save(SaveFlags::MatrixClip); 843 canvas.translate(40, 40); 844 canvas.drawRenderNode(child.get()); 845 canvas.restore(); 846 }); 847 848 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 849 Caches::getInstance()); 850 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 851 852 RenderNodeTestRenderer renderer; 853 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 854 EXPECT_EQ(2, renderer.getIndex()); 855} 856 857RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) { 858 class ClippedTestRenderer : public TestRendererBase { 859 public: 860 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 861 EXPECT_EQ(0, mIndex++); 862 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds); 863 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect()); 864 EXPECT_TRUE(state.computedState.transform.isIdentity()); 865 } 866 }; 867 868 auto node = TestUtils::createNode<RecordingCanvas>( 869 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 870 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200)); 871 canvas.drawBitmap(*bitmap, 0, 0, nullptr); 872 }); 873 874 // clip to small area, should see in receiver 875 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200, sLightGeometry, 876 Caches::getInstance()); 877 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 878 879 ClippedTestRenderer renderer; 880 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 881} 882 883RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) { 884 class SaveLayerSimpleTestRenderer : public TestRendererBase { 885 public: 886 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 887 EXPECT_EQ(0, mIndex++); 888 EXPECT_EQ(180u, width); 889 EXPECT_EQ(180u, height); 890 return nullptr; 891 } 892 void endLayer() override { EXPECT_EQ(2, mIndex++); } 893 void onRectOp(const RectOp& op, const BakedOpState& state) override { 894 EXPECT_EQ(1, mIndex++); 895 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds); 896 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds); 897 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect()); 898 899 Matrix4 expectedTransform; 900 expectedTransform.loadTranslate(-10, -10, 0); 901 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform); 902 } 903 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 904 EXPECT_EQ(3, mIndex++); 905 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 906 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 907 EXPECT_TRUE(state.computedState.transform.isIdentity()); 908 } 909 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 910 EXPECT_EQ(4, mIndex++); 911 EXPECT_EQ(nullptr, offscreenBuffer); 912 } 913 }; 914 915 auto node = TestUtils::createNode<RecordingCanvas>( 916 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 917 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); 918 canvas.drawRect(10, 10, 190, 190, SkPaint()); 919 canvas.restore(); 920 }); 921 922 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 923 Caches::getInstance()); 924 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 925 926 SaveLayerSimpleTestRenderer renderer; 927 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 928 EXPECT_EQ(5, renderer.getIndex()); 929} 930 931RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) { 932 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as: 933 * - startTemporaryLayer2, rect2 endLayer2 934 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1 935 * - startFrame, layerOp1, endFrame 936 */ 937 class SaveLayerNestedTestRenderer : public TestRendererBase { 938 public: 939 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 940 const int index = mIndex++; 941 if (index == 0) { 942 EXPECT_EQ(400u, width); 943 EXPECT_EQ(400u, height); 944 return (OffscreenBuffer*)0x400; 945 } else if (index == 3) { 946 EXPECT_EQ(800u, width); 947 EXPECT_EQ(800u, height); 948 return (OffscreenBuffer*)0x800; 949 } else { 950 ADD_FAILURE(); 951 } 952 return (OffscreenBuffer*)nullptr; 953 } 954 void endLayer() override { 955 int index = mIndex++; 956 EXPECT_TRUE(index == 2 || index == 6); 957 } 958 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 959 EXPECT_EQ(7, mIndex++); 960 } 961 void endFrame(const Rect& repaintRect) override { EXPECT_EQ(9, mIndex++); } 962 void onRectOp(const RectOp& op, const BakedOpState& state) override { 963 const int index = mIndex++; 964 if (index == 1) { 965 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect 966 } else if (index == 4) { 967 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect 968 } else { 969 ADD_FAILURE(); 970 } 971 } 972 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 973 const int index = mIndex++; 974 if (index == 5) { 975 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle); 976 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer 977 } else if (index == 8) { 978 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle); 979 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer 980 } else { 981 ADD_FAILURE(); 982 } 983 } 984 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 985 const int index = mIndex++; 986 // order isn't important, but we need to see both 987 if (index == 10) { 988 EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer); 989 } else if (index == 11) { 990 EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer); 991 } else { 992 ADD_FAILURE(); 993 } 994 } 995 }; 996 997 auto node = TestUtils::createNode<RecordingCanvas>( 998 0, 0, 800, 800, [](RenderProperties& props, RecordingCanvas& canvas) { 999 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); 1000 { 1001 canvas.drawRect(0, 0, 800, 800, SkPaint()); 1002 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); 1003 { canvas.drawRect(0, 0, 400, 400, SkPaint()); } 1004 canvas.restore(); 1005 } 1006 canvas.restore(); 1007 }); 1008 1009 FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800, sLightGeometry, 1010 Caches::getInstance()); 1011 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1012 1013 SaveLayerNestedTestRenderer renderer; 1014 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1015 EXPECT_EQ(12, renderer.getIndex()); 1016} 1017 1018RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_contentRejection) { 1019 auto node = TestUtils::createNode<RecordingCanvas>( 1020 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 1021 canvas.save(SaveFlags::MatrixClip); 1022 canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); 1023 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); 1024 1025 // draw within save layer may still be recorded, but shouldn't be drawn 1026 canvas.drawRect(200, 200, 400, 400, SkPaint()); 1027 1028 canvas.restore(); 1029 canvas.restore(); 1030 }); 1031 1032 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 1033 Caches::getInstance()); 1034 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1035 1036 FailRenderer renderer; 1037 // should see no ops, even within the layer, since the layer should be rejected 1038 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1039} 1040 1041RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) { 1042 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase { 1043 public: 1044 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1045 EXPECT_EQ(0, mIndex++); 1046 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 1047 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 1048 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1049 } 1050 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1051 EXPECT_EQ(1, mIndex++); 1052 ASSERT_NE(nullptr, op.paint); 1053 ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint)); 1054 } 1055 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1056 EXPECT_EQ(2, mIndex++); 1057 EXPECT_EQ(Rect(200, 200), op.unmappedBounds); 1058 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 1059 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 1060 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1061 } 1062 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1063 EXPECT_EQ(3, mIndex++); 1064 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 1065 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 1066 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1067 } 1068 }; 1069 1070 auto node = TestUtils::createNode<RecordingCanvas>( 1071 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 1072 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 1073 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1074 canvas.restore(); 1075 }); 1076 1077 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 1078 Caches::getInstance()); 1079 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1080 1081 SaveLayerUnclippedSimpleTestRenderer renderer; 1082 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1083 EXPECT_EQ(4, renderer.getIndex()); 1084} 1085 1086RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) { 1087 class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase { 1088 public: 1089 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1090 EXPECT_EQ(0, mIndex++); 1091 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds) 1092 << "Bounds rect should round out"; 1093 } 1094 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {} 1095 void onRectOp(const RectOp& op, const BakedOpState& state) override {} 1096 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1097 EXPECT_EQ(1, mIndex++); 1098 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds) 1099 << "Bounds rect should round out"; 1100 } 1101 }; 1102 1103 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props, 1104 RecordingCanvas& canvas) { 1105 canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out 1106 128, (SaveFlags::Flags)(0)); 1107 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1108 canvas.restore(); 1109 }); 1110 1111 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 1112 Caches::getInstance()); 1113 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1114 1115 SaveLayerUnclippedRoundTestRenderer renderer; 1116 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1117 EXPECT_EQ(2, renderer.getIndex()); 1118} 1119 1120RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { 1121 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase { 1122 public: 1123 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1124 int index = mIndex++; 1125 EXPECT_GT(4, index); 1126 EXPECT_EQ(5, op.unmappedBounds.getWidth()); 1127 EXPECT_EQ(5, op.unmappedBounds.getHeight()); 1128 if (index == 0) { 1129 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds); 1130 } else if (index == 1) { 1131 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds); 1132 } else if (index == 2) { 1133 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds); 1134 } else if (index == 3) { 1135 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds); 1136 } 1137 } 1138 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1139 EXPECT_EQ(4, mIndex++); 1140 ASSERT_EQ(op.vertexCount, 16u); 1141 for (size_t i = 0; i < op.vertexCount; i++) { 1142 auto v = op.vertices[i]; 1143 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200); 1144 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200); 1145 } 1146 } 1147 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1148 EXPECT_EQ(5, mIndex++); 1149 } 1150 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1151 EXPECT_LT(5, mIndex++); 1152 } 1153 }; 1154 1155 auto node = TestUtils::createNode<RecordingCanvas>( 1156 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 1157 1158 int restoreTo = canvas.save(SaveFlags::MatrixClip); 1159 canvas.scale(2, 2); 1160 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); 1161 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); 1162 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); 1163 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); 1164 canvas.drawRect(0, 0, 100, 100, SkPaint()); 1165 canvas.restoreToCount(restoreTo); 1166 }); 1167 1168 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 1169 Caches::getInstance()); 1170 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1171 1172 SaveLayerUnclippedMergedClearsTestRenderer renderer; 1173 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1174 EXPECT_EQ(10, renderer.getIndex()) 1175 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect."; 1176} 1177 1178RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { 1179 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase { 1180 public: 1181 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1182 EXPECT_EQ(0, mIndex++); 1183 } 1184 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1185 EXPECT_EQ(1, mIndex++); 1186 ASSERT_NE(nullptr, op.paint); 1187 EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint)); 1188 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds) 1189 << "Expect dirty rect as clip"; 1190 ASSERT_NE(nullptr, state.computedState.clipState); 1191 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect); 1192 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1193 } 1194 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1195 EXPECT_EQ(2, mIndex++); 1196 } 1197 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1198 EXPECT_EQ(3, mIndex++); 1199 } 1200 }; 1201 1202 auto node = TestUtils::createNode<RecordingCanvas>( 1203 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 1204 // save smaller than clip, so we get unclipped behavior 1205 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 1206 canvas.drawRect(0, 0, 200, 200, SkPaint()); 1207 canvas.restore(); 1208 }); 1209 1210 // draw with partial screen dirty, and assert we see that rect later 1211 FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, sLightGeometry, 1212 Caches::getInstance()); 1213 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1214 1215 SaveLayerUnclippedClearClipTestRenderer renderer; 1216 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1217 EXPECT_EQ(4, renderer.getIndex()); 1218} 1219 1220RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_reject) { 1221 auto node = TestUtils::createNode<RecordingCanvas>( 1222 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 1223 // unclipped savelayer + rect both in area that won't intersect with dirty 1224 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); 1225 canvas.drawRect(100, 100, 200, 200, SkPaint()); 1226 canvas.restore(); 1227 }); 1228 1229 // draw with partial screen dirty that doesn't intersect with savelayer 1230 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry, 1231 Caches::getInstance()); 1232 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1233 1234 FailRenderer renderer; 1235 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1236} 1237 1238/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as: 1239 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer 1240 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe 1241 */ 1242RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) { 1243 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { 1244 public: 1245 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1246 EXPECT_EQ(0, mIndex++); // savelayer first 1247 return (OffscreenBuffer*)0xabcd; 1248 } 1249 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 1250 int index = mIndex++; 1251 EXPECT_TRUE(index == 1 || index == 7); 1252 } 1253 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 1254 int index = mIndex++; 1255 EXPECT_TRUE(index == 2 || index == 8); 1256 } 1257 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1258 EXPECT_EQ(3, mIndex++); 1259 Matrix4 expected; 1260 expected.loadTranslate(-100, -100, 0); 1261 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds); 1262 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 1263 } 1264 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 1265 int index = mIndex++; 1266 EXPECT_TRUE(index == 4 || index == 10); 1267 } 1268 void endLayer() override { EXPECT_EQ(5, mIndex++); } 1269 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1270 EXPECT_EQ(6, mIndex++); 1271 } 1272 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1273 EXPECT_EQ(9, mIndex++); 1274 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1275 } 1276 void endFrame(const Rect& repaintRect) override { EXPECT_EQ(11, mIndex++); } 1277 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1278 EXPECT_EQ(12, mIndex++); 1279 EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer); 1280 } 1281 }; 1282 1283 auto node = TestUtils::createNode<RecordingCanvas>( 1284 0, 0, 600, 600, // 500x500 triggers clipping 1285 [](RenderProperties& props, RecordingCanvas& canvas) { 1286 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped 1287 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped 1288 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped 1289 canvas.drawRect(200, 200, 300, 300, SkPaint()); 1290 canvas.restore(); 1291 canvas.restore(); 1292 canvas.restore(); 1293 }); 1294 1295 FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, sLightGeometry, 1296 Caches::getInstance()); 1297 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 1298 1299 SaveLayerUnclippedComplexTestRenderer renderer; 1300 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1301 EXPECT_EQ(13, renderer.getIndex()); 1302} 1303 1304RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) { 1305 class HwLayerSimpleTestRenderer : public TestRendererBase { 1306 public: 1307 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1308 EXPECT_EQ(0, mIndex++); 1309 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1310 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1311 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1312 } 1313 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1314 EXPECT_EQ(1, mIndex++); 1315 1316 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1317 << "Transform should be reset within layer"; 1318 1319 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1320 << "Damage rect should be used to clip layer content"; 1321 } 1322 void endLayer() override { EXPECT_EQ(2, mIndex++); } 1323 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1324 EXPECT_EQ(3, mIndex++); 1325 } 1326 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1327 EXPECT_EQ(4, mIndex++); 1328 } 1329 void endFrame(const Rect& repaintRect) override { EXPECT_EQ(5, mIndex++); } 1330 }; 1331 1332 auto node = TestUtils::createNode<RecordingCanvas>( 1333 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { 1334 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1335 SkPaint paint; 1336 paint.setColor(SK_ColorWHITE); 1337 canvas.drawRect(0, 0, 100, 100, paint); 1338 }); 1339 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1340 1341 // create RenderNode's layer here in same way prepareTree would 1342 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1343 *layerHandle = &layer; 1344 1345 auto syncedNode = TestUtils::getSyncedNode(node); 1346 1347 // only enqueue partial damage 1348 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1349 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1350 1351 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 1352 Caches::getInstance()); 1353 frameBuilder.deferLayers(layerUpdateQueue); 1354 frameBuilder.deferRenderNode(*syncedNode); 1355 1356 HwLayerSimpleTestRenderer renderer; 1357 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1358 EXPECT_EQ(6, renderer.getIndex()); 1359 1360 // clean up layer pointer, so we can safely destruct RenderNode 1361 *layerHandle = nullptr; 1362} 1363 1364RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) { 1365 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: 1366 * - startRepaintLayer(child), rect(grey), endLayer 1367 * - startTemporaryLayer, drawLayer(child), endLayer 1368 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer 1369 * - startFrame, drawLayer(parent), endLayerb 1370 */ 1371 class HwLayerComplexTestRenderer : public TestRendererBase { 1372 public: 1373 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1374 EXPECT_EQ(3, mIndex++); // savelayer first 1375 return (OffscreenBuffer*)0xabcd; 1376 } 1377 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1378 int index = mIndex++; 1379 if (index == 0) { 1380 // starting inner layer 1381 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1382 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1383 } else if (index == 6) { 1384 // starting outer layer 1385 EXPECT_EQ(200u, offscreenBuffer->viewportWidth); 1386 EXPECT_EQ(200u, offscreenBuffer->viewportHeight); 1387 } else { 1388 ADD_FAILURE(); 1389 } 1390 } 1391 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1392 int index = mIndex++; 1393 if (index == 1) { 1394 // inner layer's rect (white) 1395 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1396 } else if (index == 7) { 1397 // outer layer's rect (grey) 1398 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1399 } else { 1400 ADD_FAILURE(); 1401 } 1402 } 1403 void endLayer() override { 1404 int index = mIndex++; 1405 EXPECT_TRUE(index == 2 || index == 5 || index == 9); 1406 } 1407 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1408 EXPECT_EQ(10, mIndex++); 1409 } 1410 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1411 OffscreenBuffer* layer = *op.layerHandle; 1412 int index = mIndex++; 1413 if (index == 4) { 1414 EXPECT_EQ(100u, layer->viewportWidth); 1415 EXPECT_EQ(100u, layer->viewportHeight); 1416 } else if (index == 8) { 1417 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1418 } else if (index == 11) { 1419 EXPECT_EQ(200u, layer->viewportWidth); 1420 EXPECT_EQ(200u, layer->viewportHeight); 1421 } else { 1422 ADD_FAILURE(); 1423 } 1424 } 1425 void endFrame(const Rect& repaintRect) override { EXPECT_EQ(12, mIndex++); } 1426 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1427 EXPECT_EQ(13, mIndex++); 1428 } 1429 }; 1430 1431 auto child = TestUtils::createNode<RecordingCanvas>( 1432 50, 50, 150, 150, [](RenderProperties& props, RecordingCanvas& canvas) { 1433 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1434 SkPaint paint; 1435 paint.setColor(SK_ColorWHITE); 1436 canvas.drawRect(0, 0, 100, 100, paint); 1437 }); 1438 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1439 *(child->getLayerHandle()) = &childLayer; 1440 1441 RenderNode* childPtr = child.get(); 1442 auto parent = TestUtils::createNode<RecordingCanvas>( 1443 0, 0, 200, 200, [childPtr](RenderProperties& props, RecordingCanvas& canvas) { 1444 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1445 SkPaint paint; 1446 paint.setColor(SK_ColorDKGRAY); 1447 canvas.drawRect(0, 0, 200, 200, paint); 1448 1449 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); 1450 canvas.drawRenderNode(childPtr); 1451 canvas.restore(); 1452 }); 1453 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1454 *(parent->getLayerHandle()) = &parentLayer; 1455 1456 auto syncedNode = TestUtils::getSyncedNode(parent); 1457 1458 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1459 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); 1460 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); 1461 1462 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 1463 Caches::getInstance()); 1464 frameBuilder.deferLayers(layerUpdateQueue); 1465 frameBuilder.deferRenderNode(*syncedNode); 1466 1467 HwLayerComplexTestRenderer renderer; 1468 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1469 EXPECT_EQ(14, renderer.getIndex()); 1470 1471 // clean up layer pointers, so we can safely destruct RenderNodes 1472 *(child->getLayerHandle()) = nullptr; 1473 *(parent->getLayerHandle()) = nullptr; 1474} 1475 1476RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) { 1477 class BuildLayerTestRenderer : public TestRendererBase { 1478 public: 1479 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1480 EXPECT_EQ(0, mIndex++); 1481 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1482 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1483 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1484 } 1485 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 1486 EXPECT_EQ(1, mIndex++); 1487 1488 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1489 << "Transform should be reset within layer"; 1490 1491 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1492 << "Damage rect should be used to clip layer content"; 1493 } 1494 void endLayer() override { EXPECT_EQ(2, mIndex++); } 1495 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1496 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1497 } 1498 void endFrame(const Rect& repaintRect) override { 1499 ADD_FAILURE() << "Primary frame draw not expected in this test"; 1500 } 1501 }; 1502 1503 auto node = TestUtils::createNode<RecordingCanvas>( 1504 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { 1505 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1506 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 1507 }); 1508 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1509 1510 // create RenderNode's layer here in same way prepareTree would 1511 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1512 *layerHandle = &layer; 1513 1514 TestUtils::syncHierarchyPropertiesAndDisplayList(node); 1515 1516 // only enqueue partial damage 1517 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1518 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1519 1520 // Draw, but pass empty node list, so no work is done for primary frame 1521 FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance()); 1522 BuildLayerTestRenderer renderer; 1523 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1524 EXPECT_EQ(3, renderer.getIndex()); 1525 1526 // clean up layer pointer, so we can safely destruct RenderNode 1527 *layerHandle = nullptr; 1528} 1529 1530namespace { 1531 1532static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { 1533 SkPaint paint; 1534 // order put in blue channel, transparent so overlapped content doesn't get rejected 1535 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); 1536 canvas->drawRect(0, 0, 100, 100, paint); 1537} 1538static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) { 1539 auto node = TestUtils::createNode<RecordingCanvas>( 1540 0, 0, 100, 100, [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { 1541 drawOrderedRect(&canvas, expectedDrawOrder); 1542 }); 1543 node->mutateStagingProperties().setTranslationZ(z); 1544 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); 1545 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1546} 1547 1548static void drawOrderedNode( 1549 Canvas* canvas, uint8_t expectedDrawOrder, 1550 std::function<void(RenderProperties& props, RecordingCanvas& canvas)> setup) { 1551 auto node = TestUtils::createNode<RecordingCanvas>( 1552 0, 0, 100, 100, 1553 [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) { 1554 drawOrderedRect(&canvas, expectedDrawOrder); 1555 if (setup) { 1556 setup(props, canvas); 1557 } 1558 }); 1559 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1560} 1561 1562class ZReorderTestRenderer : public TestRendererBase { 1563public: 1564 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1565 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel 1566 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 1567 } 1568}; 1569 1570} // end anonymous namespace 1571 1572RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, zReorder) { 1573 auto parent = TestUtils::createNode<RecordingCanvas>( 1574 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 1575 canvas.insertReorderBarrier(true); 1576 canvas.insertReorderBarrier(false); 1577 drawOrderedNode(&canvas, 0, 1578 10.0f); // in reorder=false at this point, so played inorder 1579 drawOrderedRect(&canvas, 1); 1580 canvas.insertReorderBarrier(true); 1581 drawOrderedNode(&canvas, 6, 2.0f); 1582 drawOrderedRect(&canvas, 3); 1583 drawOrderedNode(&canvas, 4, 0.0f); 1584 drawOrderedRect(&canvas, 5); 1585 drawOrderedNode(&canvas, 2, -2.0f); 1586 drawOrderedNode(&canvas, 7, 2.0f); 1587 canvas.insertReorderBarrier(false); 1588 drawOrderedRect(&canvas, 8); 1589 drawOrderedNode(&canvas, 9, 1590 -10.0f); // in reorder=false at this point, so played inorder 1591 canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op 1592 drawOrderedRect(&canvas, 11); 1593 drawOrderedNode(&canvas, 10, -1.0f); 1594 canvas.insertReorderBarrier(false); 1595 canvas.insertReorderBarrier(true); // test with two empty reorder sections 1596 canvas.insertReorderBarrier(true); 1597 canvas.insertReorderBarrier(false); 1598 drawOrderedRect(&canvas, 12); 1599 }); 1600 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 1601 Caches::getInstance()); 1602 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1603 1604 ZReorderTestRenderer renderer; 1605 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1606 EXPECT_EQ(13, renderer.getIndex()); 1607}; 1608 1609RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) { 1610 static const int scrollX = 5; 1611 static const int scrollY = 10; 1612 class ProjectionReorderTestRenderer : public TestRendererBase { 1613 public: 1614 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1615 const int index = mIndex++; 1616 1617 Matrix4 expectedMatrix; 1618 switch (index) { 1619 case 0: 1620 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 1621 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1622 expectedMatrix.loadIdentity(); 1623 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1624 break; 1625 case 1: 1626 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 1627 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1628 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); 1629 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1630 EXPECT_EQ(Rect(-35, -30, 45, 50), 1631 Rect(state.computedState.localProjectionPathMask->getBounds())); 1632 break; 1633 case 2: 1634 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 1635 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 1636 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 1637 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1638 break; 1639 default: 1640 ADD_FAILURE(); 1641 } 1642 EXPECT_EQ(expectedMatrix, state.computedState.transform); 1643 } 1644 }; 1645 1646 /** 1647 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 1648 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 1649 * draw, but because it is projected backwards, it's drawn in between B and C. 1650 * 1651 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 1652 * (which isn't affected by scroll). 1653 */ 1654 auto receiverBackground = TestUtils::createNode<RecordingCanvas>( 1655 0, 0, 100, 100, [](RenderProperties& properties, RecordingCanvas& canvas) { 1656 properties.setProjectionReceiver(true); 1657 // scroll doesn't apply to background, so undone via translationX/Y 1658 // NOTE: translationX/Y only! no other transform properties may be set for a proj 1659 // receiver! 1660 properties.setTranslationX(scrollX); 1661 properties.setTranslationY(scrollY); 1662 1663 SkPaint paint; 1664 paint.setColor(SK_ColorWHITE); 1665 canvas.drawRect(0, 0, 100, 100, paint); 1666 }); 1667 auto projectingRipple = TestUtils::createNode<RecordingCanvas>( 1668 50, 0, 100, 50, [](RenderProperties& properties, RecordingCanvas& canvas) { 1669 properties.setProjectBackwards(true); 1670 properties.setClipToBounds(false); 1671 SkPaint paint; 1672 paint.setColor(SK_ColorDKGRAY); 1673 canvas.drawRect(-10, -10, 60, 60, paint); 1674 }); 1675 auto child = TestUtils::createNode<RecordingCanvas>( 1676 0, 50, 100, 100, 1677 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1678 SkPaint paint; 1679 paint.setColor(SK_ColorBLUE); 1680 canvas.drawRect(0, 0, 100, 50, paint); 1681 canvas.drawRenderNode(projectingRipple.get()); 1682 }); 1683 auto parent = TestUtils::createNode<RecordingCanvas>( 1684 0, 0, 100, 100, 1685 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1686 // Set a rect outline for the projecting ripple to be masked against. 1687 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 1688 1689 canvas.save(SaveFlags::MatrixClip); 1690 canvas.translate(-scrollX, 1691 -scrollY); // Apply scroll (note: bg undoes this internally) 1692 canvas.drawRenderNode(receiverBackground.get()); 1693 canvas.drawRenderNode(child.get()); 1694 canvas.restore(); 1695 }); 1696 1697 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 1698 Caches::getInstance()); 1699 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1700 1701 ProjectionReorderTestRenderer renderer; 1702 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1703 EXPECT_EQ(3, renderer.getIndex()); 1704} 1705 1706RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) { 1707 static const int scrollX = 5; 1708 static const int scrollY = 10; 1709 class ProjectionHwLayerTestRenderer : public TestRendererBase { 1710 public: 1711 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1712 EXPECT_EQ(0, mIndex++); 1713 } 1714 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 1715 EXPECT_EQ(1, mIndex++); 1716 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1717 } 1718 void endLayer() override { EXPECT_EQ(2, mIndex++); } 1719 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1720 EXPECT_EQ(3, mIndex++); 1721 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1722 } 1723 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1724 EXPECT_EQ(4, mIndex++); 1725 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1726 Matrix4 expected; 1727 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); 1728 EXPECT_EQ(expected, state.computedState.transform); 1729 EXPECT_EQ(Rect(-85, -80, 295, 300), 1730 Rect(state.computedState.localProjectionPathMask->getBounds())); 1731 } 1732 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1733 EXPECT_EQ(5, mIndex++); 1734 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1735 } 1736 }; 1737 auto receiverBackground = TestUtils::createNode<RecordingCanvas>( 1738 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) { 1739 properties.setProjectionReceiver(true); 1740 // scroll doesn't apply to background, so undone via translationX/Y 1741 // NOTE: translationX/Y only! no other transform properties may be set for a proj 1742 // receiver! 1743 properties.setTranslationX(scrollX); 1744 properties.setTranslationY(scrollY); 1745 1746 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1747 }); 1748 auto projectingRipple = TestUtils::createNode<RecordingCanvas>( 1749 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) { 1750 properties.setProjectBackwards(true); 1751 properties.setClipToBounds(false); 1752 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 1753 }); 1754 auto child = TestUtils::createNode<RecordingCanvas>( 1755 100, 100, 300, 300, 1756 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1757 properties.mutateLayerProperties().setType(LayerType::RenderLayer); 1758 canvas.drawRenderNode(projectingRipple.get()); 1759 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); 1760 }); 1761 auto parent = TestUtils::createNode<RecordingCanvas>( 1762 0, 0, 400, 400, 1763 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1764 // Set a rect outline for the projecting ripple to be masked against. 1765 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 1766 canvas.translate(-scrollX, 1767 -scrollY); // Apply scroll (note: bg undoes this internally) 1768 canvas.drawRenderNode(receiverBackground.get()); 1769 canvas.drawRenderNode(child.get()); 1770 }); 1771 1772 OffscreenBuffer** layerHandle = child->getLayerHandle(); 1773 1774 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1775 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1776 Matrix4 windowTransform; 1777 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin 1778 layer.setWindowTransform(windowTransform); 1779 *layerHandle = &layer; 1780 1781 auto syncedNode = TestUtils::getSyncedNode(parent); 1782 1783 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1784 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); 1785 1786 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, 1787 Caches::getInstance()); 1788 frameBuilder.deferLayers(layerUpdateQueue); 1789 frameBuilder.deferRenderNode(*syncedNode); 1790 1791 ProjectionHwLayerTestRenderer renderer; 1792 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1793 EXPECT_EQ(6, renderer.getIndex()); 1794 1795 // clean up layer pointer, so we can safely destruct RenderNode 1796 *layerHandle = nullptr; 1797} 1798 1799RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) { 1800 static const int scrollX = 500000; 1801 static const int scrollY = 0; 1802 class ProjectionChildScrollTestRenderer : public TestRendererBase { 1803 public: 1804 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1805 EXPECT_EQ(0, mIndex++); 1806 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1807 } 1808 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1809 EXPECT_EQ(1, mIndex++); 1810 ASSERT_NE(nullptr, state.computedState.clipState); 1811 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1812 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect); 1813 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1814 } 1815 }; 1816 auto receiverBackground = TestUtils::createNode<RecordingCanvas>( 1817 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) { 1818 properties.setProjectionReceiver(true); 1819 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1820 }); 1821 auto projectingRipple = TestUtils::createNode<RecordingCanvas>( 1822 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) { 1823 // scroll doesn't apply to background, so undone via translationX/Y 1824 // NOTE: translationX/Y only! no other transform properties may be set for a proj 1825 // receiver! 1826 properties.setTranslationX(scrollX); 1827 properties.setTranslationY(scrollY); 1828 properties.setProjectBackwards(true); 1829 properties.setClipToBounds(false); 1830 canvas.drawOval(0, 0, 200, 200, SkPaint()); 1831 }); 1832 auto child = TestUtils::createNode<RecordingCanvas>( 1833 0, 0, 400, 400, 1834 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1835 // Record time clip will be ignored by projectee 1836 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect); 1837 1838 canvas.translate(-scrollX, 1839 -scrollY); // Apply scroll (note: bg undoes this internally) 1840 canvas.drawRenderNode(projectingRipple.get()); 1841 }); 1842 auto parent = TestUtils::createNode<RecordingCanvas>( 1843 0, 0, 400, 400, 1844 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1845 canvas.drawRenderNode(receiverBackground.get()); 1846 canvas.drawRenderNode(child.get()); 1847 }); 1848 1849 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry, 1850 Caches::getInstance()); 1851 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1852 1853 ProjectionChildScrollTestRenderer renderer; 1854 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1855 EXPECT_EQ(2, renderer.getIndex()); 1856} 1857 1858// creates a 100x100 shadow casting node with provided translationZ 1859static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1860 return TestUtils::createNode<RecordingCanvas>( 1861 0, 0, 100, 100, [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1862 properties.setTranslationZ(translationZ); 1863 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1864 SkPaint paint; 1865 paint.setColor(SK_ColorWHITE); 1866 canvas.drawRect(0, 0, 100, 100, paint); 1867 }); 1868} 1869 1870RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) { 1871 class ShadowTestRenderer : public TestRendererBase { 1872 public: 1873 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1874 EXPECT_EQ(0, mIndex++); 1875 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1876 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr)); 1877 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY); 1878 1879 Matrix4 expectedZ; 1880 expectedZ.loadTranslate(0, 0, 5); 1881 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ); 1882 } 1883 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1884 EXPECT_EQ(1, mIndex++); 1885 } 1886 }; 1887 1888 auto parent = TestUtils::createNode<RecordingCanvas>( 1889 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 1890 canvas.insertReorderBarrier(true); 1891 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1892 }); 1893 1894 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 1895 Caches::getInstance()); 1896 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1897 1898 ShadowTestRenderer renderer; 1899 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1900 EXPECT_EQ(2, renderer.getIndex()); 1901} 1902 1903RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) { 1904 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1905 public: 1906 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1907 EXPECT_EQ(0, mIndex++); 1908 return nullptr; 1909 } 1910 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1911 EXPECT_EQ(1, mIndex++); 1912 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1913 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1914 } 1915 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1916 EXPECT_EQ(2, mIndex++); 1917 } 1918 void endLayer() override { EXPECT_EQ(3, mIndex++); } 1919 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1920 EXPECT_EQ(4, mIndex++); 1921 } 1922 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 1923 EXPECT_EQ(5, mIndex++); 1924 } 1925 }; 1926 1927 auto parent = TestUtils::createNode<RecordingCanvas>( 1928 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 1929 // save/restore outside of reorderBarrier, so they don't get moved out of place 1930 canvas.translate(20, 10); 1931 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); 1932 canvas.insertReorderBarrier(true); 1933 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1934 canvas.insertReorderBarrier(false); 1935 canvas.restoreToCount(count); 1936 }); 1937 1938 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1939 (FrameBuilder::LightGeometry){{100, 100, 100}, 50}, 1940 Caches::getInstance()); 1941 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 1942 1943 ShadowSaveLayerTestRenderer renderer; 1944 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1945 EXPECT_EQ(6, renderer.getIndex()); 1946} 1947 1948RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) { 1949 class ShadowHwLayerTestRenderer : public TestRendererBase { 1950 public: 1951 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1952 EXPECT_EQ(0, mIndex++); 1953 } 1954 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1955 EXPECT_EQ(1, mIndex++); 1956 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1957 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1958 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius); 1959 } 1960 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1961 EXPECT_EQ(2, mIndex++); 1962 } 1963 void endLayer() override { EXPECT_EQ(3, mIndex++); } 1964 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1965 EXPECT_EQ(4, mIndex++); 1966 } 1967 }; 1968 1969 auto parent = TestUtils::createNode<RecordingCanvas>( 1970 50, 60, 150, 160, [](RenderProperties& props, RecordingCanvas& canvas) { 1971 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1972 canvas.insertReorderBarrier(true); 1973 canvas.save(SaveFlags::MatrixClip); 1974 canvas.translate(20, 10); 1975 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1976 canvas.restore(); 1977 }); 1978 OffscreenBuffer** layerHandle = parent->getLayerHandle(); 1979 1980 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1981 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1982 Matrix4 windowTransform; 1983 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin 1984 layer.setWindowTransform(windowTransform); 1985 *layerHandle = &layer; 1986 1987 auto syncedNode = TestUtils::getSyncedNode(parent); 1988 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1989 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); 1990 1991 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 1992 (FrameBuilder::LightGeometry){{100, 100, 100}, 30}, 1993 Caches::getInstance()); 1994 frameBuilder.deferLayers(layerUpdateQueue); 1995 frameBuilder.deferRenderNode(*syncedNode); 1996 1997 ShadowHwLayerTestRenderer renderer; 1998 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1999 EXPECT_EQ(5, renderer.getIndex()); 2000 2001 // clean up layer pointer, so we can safely destruct RenderNode 2002 *layerHandle = nullptr; 2003} 2004 2005RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) { 2006 class ShadowLayeringTestRenderer : public TestRendererBase { 2007 public: 2008 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 2009 int index = mIndex++; 2010 EXPECT_TRUE(index == 0 || index == 1); 2011 } 2012 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2013 int index = mIndex++; 2014 EXPECT_TRUE(index == 2 || index == 3); 2015 } 2016 }; 2017 auto parent = TestUtils::createNode<RecordingCanvas>( 2018 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { 2019 canvas.insertReorderBarrier(true); 2020 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 2021 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 2022 }); 2023 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, 2024 (FrameBuilder::LightGeometry){{100, 100, 100}, 50}, 2025 Caches::getInstance()); 2026 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 2027 2028 ShadowLayeringTestRenderer renderer; 2029 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2030 EXPECT_EQ(4, renderer.getIndex()); 2031} 2032 2033RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) { 2034 class ShadowClippingTestRenderer : public TestRendererBase { 2035 public: 2036 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 2037 EXPECT_EQ(0, mIndex++); 2038 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect) 2039 << "Shadow must respect pre-barrier canvas clip value."; 2040 } 2041 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2042 EXPECT_EQ(1, mIndex++); 2043 } 2044 }; 2045 auto parent = TestUtils::createNode<RecordingCanvas>( 2046 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2047 // Apply a clip before the reorder barrier/shadow casting child is drawn. 2048 // This clip must be applied to the shadow cast by the child. 2049 canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect); 2050 canvas.insertReorderBarrier(true); 2051 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 2052 }); 2053 2054 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, 2055 (FrameBuilder::LightGeometry){{100, 100, 100}, 50}, 2056 Caches::getInstance()); 2057 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); 2058 2059 ShadowClippingTestRenderer renderer; 2060 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2061 EXPECT_EQ(2, renderer.getIndex()); 2062} 2063 2064static void testProperty( 2065 std::function<void(RenderProperties&)> propSetupCallback, 2066 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 2067 class PropertyTestRenderer : public TestRendererBase { 2068 public: 2069 explicit PropertyTestRenderer( 2070 std::function<void(const RectOp&, const BakedOpState&)> callback) 2071 : mCallback(callback) {} 2072 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2073 EXPECT_EQ(mIndex++, 0); 2074 mCallback(op, state); 2075 } 2076 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 2077 }; 2078 2079 auto node = TestUtils::createNode<RecordingCanvas>( 2080 0, 0, 100, 100, [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 2081 propSetupCallback(props); 2082 SkPaint paint; 2083 paint.setColor(SK_ColorWHITE); 2084 canvas.drawRect(0, 0, 100, 100, paint); 2085 }); 2086 2087 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry, 2088 Caches::getInstance()); 2089 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 2090 2091 PropertyTestRenderer renderer(opValidateCallback); 2092 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2093 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 2094} 2095 2096RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 2097 testProperty( 2098 [](RenderProperties& properties) { 2099 properties.setAlpha(0.5f); 2100 properties.setHasOverlappingRendering(false); 2101 }, 2102 [](const RectOp& op, const BakedOpState& state) { 2103 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 2104 }); 2105} 2106 2107RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) { 2108 testProperty( 2109 [](RenderProperties& properties) { 2110 properties.setClipToBounds(true); 2111 properties.setClipBounds(Rect(10, 20, 300, 400)); 2112 }, 2113 [](const RectOp& op, const BakedOpState& state) { 2114 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 2115 << "Clip rect should be intersection of node bounds and clip bounds"; 2116 }); 2117} 2118 2119RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) { 2120 testProperty( 2121 [](RenderProperties& properties) { 2122 properties.mutableRevealClip().set(true, 50, 50, 25); 2123 }, 2124 [](const RectOp& op, const BakedOpState& state) { 2125 ASSERT_NE(nullptr, state.roundRectClipState); 2126 EXPECT_TRUE(state.roundRectClipState->highPriority); 2127 EXPECT_EQ(25, state.roundRectClipState->radius); 2128 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 2129 }); 2130} 2131 2132RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) { 2133 testProperty( 2134 [](RenderProperties& properties) { 2135 properties.mutableOutline().setShouldClip(true); 2136 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 2137 }, 2138 [](const RectOp& op, const BakedOpState& state) { 2139 ASSERT_NE(nullptr, state.roundRectClipState); 2140 EXPECT_FALSE(state.roundRectClipState->highPriority); 2141 EXPECT_EQ(5, state.roundRectClipState->radius); 2142 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 2143 }); 2144} 2145 2146RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) { 2147 testProperty( 2148 [](RenderProperties& properties) { 2149 properties.setLeftTopRightBottom(10, 10, 110, 110); 2150 2151 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 2152 properties.setStaticMatrix(&staticMatrix); 2153 2154 // ignored, since static overrides animation 2155 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 2156 properties.setAnimationMatrix(&animationMatrix); 2157 2158 properties.setTranslationX(10); 2159 properties.setTranslationY(20); 2160 properties.setScaleX(0.5f); 2161 properties.setScaleY(0.7f); 2162 }, 2163 [](const RectOp& op, const BakedOpState& state) { 2164 Matrix4 matrix; 2165 matrix.loadTranslate(10, 10, 0); // left, top 2166 matrix.scale(1.2f, 1.2f, 1); // static matrix 2167 // ignore animation matrix, since static overrides it 2168 2169 // translation xy 2170 matrix.translate(10, 20); 2171 2172 // scale xy (from default pivot - center) 2173 matrix.translate(50, 50); 2174 matrix.scale(0.5f, 0.7f, 1); 2175 matrix.translate(-50, -50); 2176 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 2177 << "Op draw matrix must match expected combination of transformation " 2178 "properties"; 2179 }); 2180} 2181 2182struct SaveLayerAlphaData { 2183 uint32_t layerWidth = 0; 2184 uint32_t layerHeight = 0; 2185 Rect rectClippedBounds; 2186 Matrix4 rectMatrix; 2187 Matrix4 drawLayerMatrix; 2188}; 2189/** 2190 * Constructs a view to hit the temporary layer alpha property implementation: 2191 * a) 0 < alpha < 1 2192 * b) too big for layer (larger than maxTextureSize) 2193 * c) overlapping rendering content 2194 * returning observed data about layer size and content clip/transform. 2195 * 2196 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 2197 * (for efficiency, and to fit in layer size constraints) based on parent clip. 2198 */ 2199void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 2200 std::function<void(RenderProperties&)> propSetupCallback) { 2201 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 2202 public: 2203 explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) : mOutData(outData) {} 2204 2205 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 2206 EXPECT_EQ(0, mIndex++); 2207 mOutData->layerWidth = width; 2208 mOutData->layerHeight = height; 2209 return nullptr; 2210 } 2211 void onRectOp(const RectOp& op, const BakedOpState& state) override { 2212 EXPECT_EQ(1, mIndex++); 2213 2214 mOutData->rectClippedBounds = state.computedState.clippedBounds; 2215 mOutData->rectMatrix = state.computedState.transform; 2216 } 2217 void endLayer() override { EXPECT_EQ(2, mIndex++); } 2218 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 2219 EXPECT_EQ(3, mIndex++); 2220 mOutData->drawLayerMatrix = state.computedState.transform; 2221 } 2222 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override { 2223 EXPECT_EQ(4, mIndex++); 2224 } 2225 2226 private: 2227 SaveLayerAlphaData* mOutData; 2228 }; 2229 2230 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 2231 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 2232 auto node = TestUtils::createNode<RecordingCanvas>( 2233 0, 0, 10000, 10000, 2234 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 2235 properties.setHasOverlappingRendering(true); 2236 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 2237 // apply other properties 2238 propSetupCallback(properties); 2239 2240 SkPaint paint; 2241 paint.setColor(SK_ColorWHITE); 2242 canvas.drawRect(0, 0, 10000, 10000, paint); 2243 }); 2244 auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height 2245 2246 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, 2247 Caches::getInstance()); 2248 frameBuilder.deferRenderNode(*syncedNode); 2249 2250 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 2251 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2252 2253 // assert, since output won't be valid if we haven't seen a save layer triggered 2254 ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 2255} 2256 2257RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 2258 SaveLayerAlphaData observedData; 2259 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2260 properties.setTranslationX(10); // offset rendering content 2261 properties.setTranslationY(-2000); // offset rendering content 2262 }); 2263 EXPECT_EQ(190u, observedData.layerWidth); 2264 EXPECT_EQ(200u, observedData.layerHeight); 2265 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 2266 << "expect content to be clipped to screen area"; 2267 Matrix4 expected; 2268 expected.loadTranslate(0, -2000, 0); 2269 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 2270 << "expect content to be translated as part of being clipped"; 2271 expected.loadTranslate(10, 0, 0); 2272 EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix) 2273 << "expect drawLayer to be translated as part of being clipped"; 2274} 2275 2276RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 2277 SaveLayerAlphaData observedData; 2278 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2279 // Translate and rotate the view so that the only visible part is the top left corner of 2280 // the view. It will form an isosceles right triangle with a long side length of 200 at the 2281 // bottom of the viewport. 2282 properties.setTranslationX(100); 2283 properties.setTranslationY(100); 2284 properties.setPivotX(0); 2285 properties.setPivotY(0); 2286 properties.setRotation(45); 2287 }); 2288 // ceil(sqrt(2) / 2 * 200) = 142 2289 EXPECT_EQ(142u, observedData.layerWidth); 2290 EXPECT_EQ(142u, observedData.layerHeight); 2291 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 2292 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2293} 2294 2295RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 2296 SaveLayerAlphaData observedData; 2297 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 2298 properties.setPivotX(0); 2299 properties.setPivotY(0); 2300 properties.setScaleX(2); 2301 properties.setScaleY(0.5f); 2302 }); 2303 EXPECT_EQ(100u, observedData.layerWidth); 2304 EXPECT_EQ(400u, observedData.layerHeight); 2305 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 2306 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 2307} 2308 2309RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) { 2310 class ClipReplaceTestRenderer : public TestRendererBase { 2311 public: 2312 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 2313 EXPECT_EQ(0, mIndex++); 2314 EXPECT_TRUE(op.localClip->intersectWithRoot); 2315 EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect) 2316 << "Expect resolved clip to be intersection of viewport clip and clip op"; 2317 } 2318 }; 2319 auto node = TestUtils::createNode<RecordingCanvas>( 2320 20, 20, 30, 30, [](RenderProperties& props, RecordingCanvas& canvas) { 2321 canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated); 2322 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); 2323 }); 2324 2325 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, sLightGeometry, 2326 Caches::getInstance()); 2327 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); 2328 2329 ClipReplaceTestRenderer renderer; 2330 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2331 EXPECT_EQ(1, renderer.getIndex()); 2332} 2333 2334RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) { 2335 /* R is backward projected on B 2336 A 2337 / \ 2338 B C 2339 | 2340 R 2341 */ 2342 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2343 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2344 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2345 props.setProjectionReceiver(true); 2346 }); // nodeB 2347 drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { 2348 drawOrderedNode(&canvas, 1, 2349 [](RenderProperties& props, RecordingCanvas& canvas) { 2350 props.setProjectBackwards(true); 2351 props.setClipToBounds(false); 2352 }); // nodeR 2353 }); // nodeC 2354 }); // nodeA 2355 2356 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2357 Caches::getInstance()); 2358 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2359 2360 ZReorderTestRenderer renderer; 2361 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2362 EXPECT_EQ(3, renderer.getIndex()); 2363} 2364 2365RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) { 2366 /* R is backward projected on E 2367 A 2368 / | \ 2369 / | \ 2370 B C E 2371 | 2372 R 2373 */ 2374 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2375 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2376 drawOrderedNode(&canvas, 0, nullptr); // nodeB 2377 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2378 drawOrderedNode(&canvas, 3, [](RenderProperties& props, 2379 RecordingCanvas& canvas) { // drawn as 2 2380 props.setProjectBackwards(true); 2381 props.setClipToBounds(false); 2382 }); // nodeR 2383 }); // nodeC 2384 drawOrderedNode(&canvas, 2, [](RenderProperties& props, 2385 RecordingCanvas& canvas) { // drawn as 3 2386 props.setProjectionReceiver(true); 2387 }); // nodeE 2388 }); // nodeA 2389 2390 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2391 Caches::getInstance()); 2392 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2393 2394 ZReorderTestRenderer renderer; 2395 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2396 EXPECT_EQ(4, renderer.getIndex()); 2397} 2398 2399RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) { 2400 /* R is backward projected without receiver 2401 A 2402 / \ 2403 B C 2404 | 2405 R 2406 */ 2407 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2408 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2409 drawOrderedNode(&canvas, 0, nullptr); // nodeB 2410 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2411 drawOrderedNode(&canvas, 255, 2412 [](RenderProperties& props, RecordingCanvas& canvas) { 2413 // not having a projection receiver is an undefined behavior 2414 props.setProjectBackwards(true); 2415 props.setClipToBounds(false); 2416 }); // nodeR 2417 }); // nodeC 2418 }); // nodeA 2419 2420 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2421 Caches::getInstance()); 2422 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2423 2424 ZReorderTestRenderer renderer; 2425 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2426 EXPECT_EQ(2, renderer.getIndex()); 2427} 2428 2429RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) { 2430 /* R is backward projected on C 2431 A 2432 / \ 2433 B C 2434 | 2435 R 2436 */ 2437 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2438 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2439 drawOrderedNode(&canvas, 0, nullptr); // nodeB 2440 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2441 props.setProjectionReceiver(true); 2442 drawOrderedNode(&canvas, 2, 2443 [](RenderProperties& props, RecordingCanvas& canvas) { 2444 props.setProjectBackwards(true); 2445 props.setClipToBounds(false); 2446 }); // nodeR 2447 }); // nodeC 2448 }); // nodeA 2449 2450 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2451 Caches::getInstance()); 2452 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2453 2454 ZReorderTestRenderer renderer; 2455 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2456 EXPECT_EQ(3, renderer.getIndex()); 2457} 2458 2459RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) { 2460 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2461 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2462 drawOrderedNode(&canvas, 0, nullptr); // nodeB 2463 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2464 drawOrderedNode(&canvas, 255, 2465 [](RenderProperties& props, RecordingCanvas& canvas) { 2466 // having a node that is projected on itself is an 2467 // undefined/unexpected behavior 2468 props.setProjectionReceiver(true); 2469 props.setProjectBackwards(true); 2470 props.setClipToBounds(false); 2471 }); // nodeR 2472 }); // nodeC 2473 }); // nodeA 2474 2475 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2476 Caches::getInstance()); 2477 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2478 2479 ZReorderTestRenderer renderer; 2480 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2481 EXPECT_EQ(2, renderer.getIndex()); 2482} 2483 2484RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) { 2485 // TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a 2486 // bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical 2487 // tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling 2488 /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below), 2489 but for some reason it is drawn. 2490 A 2491 /|\ 2492 / | \ 2493 B C R 2494 */ 2495 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2496 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2497 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2498 props.setProjectionReceiver(true); 2499 }); // nodeB 2500 drawOrderedNode(&canvas, 2, 2501 [](RenderProperties& props, RecordingCanvas& canvas) {}); // nodeC 2502 drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { 2503 props.setProjectBackwards(true); 2504 props.setClipToBounds(false); 2505 }); // nodeR 2506 }); // nodeA 2507 2508 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2509 Caches::getInstance()); 2510 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2511 2512 ZReorderTestRenderer renderer; 2513 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2514 EXPECT_EQ(3, renderer.getIndex()); 2515} 2516 2517RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) { 2518 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed. 2519 A 2520 | 2521 G 2522 /|\ 2523 / | \ 2524 B C R 2525 */ 2526 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2527 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2528 drawOrderedNode(&canvas, 0, [](RenderProperties& props, 2529 RecordingCanvas& canvas) { // G 2530 drawOrderedNode(&canvas, 1, 2531 [](RenderProperties& props, RecordingCanvas& canvas) { // B 2532 props.setProjectionReceiver(true); 2533 }); // nodeB 2534 drawOrderedNode(&canvas, 2, 2535 [](RenderProperties& props, RecordingCanvas& canvas) { // C 2536 }); // nodeC 2537 drawOrderedNode(&canvas, 255, 2538 [](RenderProperties& props, RecordingCanvas& canvas) { // R 2539 props.setProjectBackwards(true); 2540 props.setClipToBounds(false); 2541 }); // nodeR 2542 }); // nodeG 2543 }); // nodeA 2544 2545 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2546 Caches::getInstance()); 2547 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2548 2549 ZReorderTestRenderer renderer; 2550 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2551 EXPECT_EQ(3, renderer.getIndex()); 2552} 2553 2554RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) { 2555 /* R is backward projected on B 2556 A 2557 | 2558 B 2559 | 2560 C 2561 | 2562 R 2563 */ 2564 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2565 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2566 drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { 2567 props.setProjectionReceiver(true); 2568 drawOrderedNode(&canvas, 1, 2569 [](RenderProperties& props, RecordingCanvas& canvas) { 2570 drawOrderedNode(&canvas, 2, [](RenderProperties& props, 2571 RecordingCanvas& canvas) { 2572 props.setProjectBackwards(true); 2573 props.setClipToBounds(false); 2574 }); // nodeR 2575 }); // nodeC 2576 }); // nodeB 2577 }); // nodeA 2578 2579 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2580 Caches::getInstance()); 2581 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2582 2583 ZReorderTestRenderer renderer; 2584 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2585 EXPECT_EQ(3, renderer.getIndex()); 2586} 2587 2588RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) { 2589 /* B and G are receivables, R is backward projected 2590 A 2591 / \ 2592 B C 2593 / \ 2594 G R 2595 */ 2596 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2597 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2598 drawOrderedNode(&canvas, 0, 2599 [](RenderProperties& props, RecordingCanvas& canvas) { // B 2600 props.setProjectionReceiver(true); 2601 }); // nodeB 2602 drawOrderedNode(&canvas, 2, [](RenderProperties& props, 2603 RecordingCanvas& canvas) { // C 2604 drawOrderedNode(&canvas, 3, 2605 [](RenderProperties& props, RecordingCanvas& canvas) { // G 2606 props.setProjectionReceiver(true); 2607 }); // nodeG 2608 drawOrderedNode(&canvas, 1, 2609 [](RenderProperties& props, RecordingCanvas& canvas) { // R 2610 props.setProjectBackwards(true); 2611 props.setClipToBounds(false); 2612 }); // nodeR 2613 }); // nodeC 2614 }); // nodeA 2615 2616 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2617 Caches::getInstance()); 2618 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2619 2620 ZReorderTestRenderer renderer; 2621 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2622 EXPECT_EQ(4, renderer.getIndex()); 2623} 2624 2625RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) { 2626 /* B and G are receivables, G is backward projected 2627 A 2628 / \ 2629 B C 2630 / \ 2631 G R 2632 */ 2633 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2634 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2635 drawOrderedNode(&canvas, 0, 2636 [](RenderProperties& props, RecordingCanvas& canvas) { // B 2637 props.setProjectionReceiver(true); 2638 }); // nodeB 2639 drawOrderedNode(&canvas, 2, [](RenderProperties& props, 2640 RecordingCanvas& canvas) { // C 2641 drawOrderedNode(&canvas, 1, 2642 [](RenderProperties& props, RecordingCanvas& canvas) { // G 2643 props.setProjectionReceiver(true); 2644 props.setProjectBackwards(true); 2645 props.setClipToBounds(false); 2646 }); // nodeG 2647 drawOrderedNode(&canvas, 3, 2648 [](RenderProperties& props, RecordingCanvas& canvas) { // R 2649 }); // nodeR 2650 }); // nodeC 2651 }); // nodeA 2652 2653 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2654 Caches::getInstance()); 2655 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2656 2657 ZReorderTestRenderer renderer; 2658 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2659 EXPECT_EQ(4, renderer.getIndex()); 2660} 2661 2662RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) { 2663 /* B and G are receivables, R is backward projected 2664 A 2665 / \ 2666 B C 2667 / \ 2668 G D 2669 | 2670 R 2671 */ 2672 auto nodeA = TestUtils::createNode<RecordingCanvas>( 2673 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) { 2674 drawOrderedNode(&canvas, 0, 2675 [](RenderProperties& props, RecordingCanvas& canvas) { // B 2676 props.setProjectionReceiver(true); 2677 }); // nodeB 2678 drawOrderedNode(&canvas, 1, [](RenderProperties& props, 2679 RecordingCanvas& canvas) { // C 2680 drawOrderedNode(&canvas, 2, 2681 [](RenderProperties& props, RecordingCanvas& canvas) { // G 2682 props.setProjectionReceiver(true); 2683 }); // nodeG 2684 drawOrderedNode( 2685 &canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { // D 2686 drawOrderedNode(&canvas, 3, [](RenderProperties& props, 2687 RecordingCanvas& canvas) { // R 2688 props.setProjectBackwards(true); 2689 props.setClipToBounds(false); 2690 }); // nodeR 2691 }); // nodeD 2692 }); // nodeC 2693 }); // nodeA 2694 2695 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry, 2696 Caches::getInstance()); 2697 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA)); 2698 2699 ZReorderTestRenderer renderer; 2700 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 2701 EXPECT_EQ(5, renderer.getIndex()); 2702} 2703 2704} // namespace uirenderer 2705} // namespace android 2706