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