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