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