FrameBuilderTests.cpp revision 80d2ade939153da87b3cd3b0a69a713bf68b64ba
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <gtest/gtest.h> 18 19#include <BakedOpState.h> 20#include <DeferredLayerUpdater.h> 21#include <FrameBuilder.h> 22#include <LayerUpdateQueue.h> 23#include <RecordedOp.h> 24#include <RecordingCanvas.h> 25#include <tests/common/TestUtils.h> 26 27#include <unordered_map> 28 29namespace android { 30namespace uirenderer { 31 32const LayerUpdateQueue sEmptyLayerUpdateQueue; 33const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; 34 35 36/** 37 * Virtual class implemented by each test to redirect static operation / state transitions to 38 * virtual methods. 39 * 40 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests), 41 * and allows Renderer vs Dispatching behavior to be merged. 42 * 43 * onXXXOp methods fail by default - tests should override ops they expect 44 * startRepaintLayer fails by default - tests should override if expected 45 * startFrame/endFrame do nothing by default - tests should override to intercept 46 */ 47class TestRendererBase { 48public: 49 virtual ~TestRendererBase() {} 50 virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) { 51 ADD_FAILURE() << "Layer creation not expected in this test"; 52 return nullptr; 53 } 54 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) { 55 ADD_FAILURE() << "Layer repaint not expected in this test"; 56 } 57 virtual void endLayer() { 58 ADD_FAILURE() << "Layer updates not expected in this test"; 59 } 60 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {} 61 virtual void endFrame(const Rect& repaintRect) {} 62 63 // define virtual defaults for single draw methods 64#define X(Type) \ 65 virtual void on##Type(const Type&, const BakedOpState&) { \ 66 ADD_FAILURE() << #Type " not expected in this test"; \ 67 } 68 MAP_RENDERABLE_OPS(X) 69#undef X 70 71 // define virtual defaults for merged draw methods 72#define X(Type) \ 73 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \ 74 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \ 75 } 76 MAP_MERGEABLE_OPS(X) 77#undef X 78 79 int getIndex() { return mIndex; } 80 81protected: 82 int mIndex = 0; 83}; 84 85/** 86 * Dispatches all static methods to similar formed methods on renderer, which fail by default but 87 * are overridden by subclasses per test. 88 */ 89class TestDispatcher { 90public: 91 // define single op methods, which redirect to TestRendererBase 92#define X(Type) \ 93 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \ 94 renderer.on##Type(op, state); \ 95 } 96 MAP_RENDERABLE_OPS(X); 97#undef X 98 99 // define merged op methods, which redirect to TestRendererBase 100#define X(Type) \ 101 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \ 102 renderer.onMerged##Type##s(opList); \ 103 } 104 MAP_MERGEABLE_OPS(X); 105#undef X 106}; 107 108class FailRenderer : public TestRendererBase {}; 109 110RENDERTHREAD_TEST(FrameBuilder, simple) { 111 class SimpleTestRenderer : public TestRendererBase { 112 public: 113 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 114 EXPECT_EQ(0, mIndex++); 115 EXPECT_EQ(100u, width); 116 EXPECT_EQ(200u, height); 117 } 118 void onRectOp(const RectOp& op, const BakedOpState& state) override { 119 EXPECT_EQ(1, mIndex++); 120 } 121 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 122 EXPECT_EQ(2, mIndex++); 123 } 124 void endFrame(const Rect& repaintRect) override { 125 EXPECT_EQ(3, mIndex++); 126 } 127 }; 128 129 auto node = TestUtils::createNode(0, 0, 100, 200, 130 [](RenderProperties& props, RecordingCanvas& canvas) { 131 SkBitmap bitmap = TestUtils::createSkBitmap(25, 25); 132 canvas.drawRect(0, 0, 100, 200, SkPaint()); 133 canvas.drawBitmap(bitmap, 10, 10, nullptr); 134 }); 135 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, 136 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 137 SimpleTestRenderer renderer; 138 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 139 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end 140} 141 142RENDERTHREAD_TEST(FrameBuilder, simpleStroke) { 143 class SimpleStrokeTestRenderer : public TestRendererBase { 144 public: 145 void onPointsOp(const PointsOp& op, const BakedOpState& state) override { 146 EXPECT_EQ(0, mIndex++); 147 // even though initial bounds are empty... 148 EXPECT_TRUE(op.unmappedBounds.isEmpty()) 149 << "initial bounds should be empty, since they're unstroked"; 150 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds) 151 << "final bounds should account for stroke"; 152 } 153 }; 154 155 auto node = TestUtils::createNode(0, 0, 100, 200, 156 [](RenderProperties& props, RecordingCanvas& canvas) { 157 SkPaint strokedPaint; 158 strokedPaint.setStrokeWidth(10); 159 canvas.drawPoint(50, 50, strokedPaint); 160 }); 161 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, 162 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 163 SimpleStrokeTestRenderer renderer; 164 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 165 EXPECT_EQ(1, renderer.getIndex()); 166} 167 168RENDERTHREAD_TEST(FrameBuilder, simpleRejection) { 169 auto node = TestUtils::createNode(0, 0, 200, 200, 170 [](RenderProperties& props, RecordingCanvas& canvas) { 171 canvas.save(SaveFlags::MatrixClip); 172 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty 173 canvas.drawRect(0, 0, 400, 400, SkPaint()); 174 canvas.restore(); 175 }); 176 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 177 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 178 179 FailRenderer renderer; 180 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 181} 182 183RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { 184 const int LOOPS = 5; 185 class SimpleBatchingTestRenderer : public TestRendererBase { 186 public: 187 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 188 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects"; 189 } 190 void onRectOp(const RectOp& op, const BakedOpState& state) override { 191 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps"; 192 } 193 }; 194 195 auto node = TestUtils::createNode(0, 0, 200, 200, 196 [](RenderProperties& props, RecordingCanvas& canvas) { 197 SkBitmap bitmap = TestUtils::createSkBitmap(10, 10, 198 kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap 199 200 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. 201 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. 202 canvas.save(SaveFlags::MatrixClip); 203 for (int i = 0; i < LOOPS; i++) { 204 canvas.translate(0, 10); 205 canvas.drawRect(0, 0, 10, 10, SkPaint()); 206 canvas.drawBitmap(bitmap, 5, 0, nullptr); 207 } 208 canvas.restore(); 209 }); 210 211 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 212 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 213 SimpleBatchingTestRenderer renderer; 214 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 215 EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 216 << "Expect number of ops = 2 * loop count"; 217} 218 219RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { 220 class AvoidOverdrawRectsTestRenderer : public TestRendererBase { 221 public: 222 void onRectOp(const RectOp& op, const BakedOpState& state) override { 223 EXPECT_EQ(mIndex++, 0) << "Should be one rect"; 224 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds) 225 << "Last rect should occlude others."; 226 } 227 }; 228 auto node = TestUtils::createNode(0, 0, 200, 200, 229 [](RenderProperties& props, RecordingCanvas& canvas) { 230 canvas.drawRect(0, 0, 200, 200, SkPaint()); 231 canvas.drawRect(0, 0, 200, 200, SkPaint()); 232 canvas.drawRect(10, 10, 190, 190, SkPaint()); 233 }); 234 235 // Damage (and therefore clip) is same as last draw, subset of renderable area. 236 // This means last op occludes other contents, and they'll be rejected to avoid overdraw. 237 SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190); 238 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200, 239 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 240 241 EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) 242 << "Recording must not have rejected ops, in order for this test to be valid"; 243 244 AvoidOverdrawRectsTestRenderer renderer; 245 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 246 EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op"; 247} 248 249RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { 250 static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50, 251 SkColorType::kRGB_565_SkColorType); 252 static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50, 253 SkColorType::kAlpha_8_SkColorType); 254 class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { 255 public: 256 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 257 EXPECT_LT(mIndex++, 2) << "Should be two bitmaps"; 258 switch(mIndex++) { 259 case 0: 260 EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef()); 261 break; 262 case 1: 263 EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef()); 264 break; 265 default: 266 ADD_FAILURE() << "Only two ops expected."; 267 } 268 } 269 }; 270 271 auto node = TestUtils::createNode(0, 0, 50, 50, 272 [](RenderProperties& props, RecordingCanvas& canvas) { 273 canvas.drawRect(0, 0, 50, 50, SkPaint()); 274 canvas.drawRect(0, 0, 50, 50, SkPaint()); 275 canvas.drawBitmap(transpBitmap, 0, 0, nullptr); 276 277 // only the below draws should remain, since they're 278 canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr); 279 canvas.drawBitmap(transpBitmap, 0, 0, nullptr); 280 }); 281 282 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50, 283 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 284 285 EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) 286 << "Recording must not have rejected ops, in order for this test to be valid"; 287 288 AvoidOverdrawBitmapsTestRenderer renderer; 289 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 290 EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly one op"; 291} 292 293RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { 294 class ClippedMergingTestRenderer : public TestRendererBase { 295 public: 296 void onMergedBitmapOps(const MergedBakedOpList& opList) override { 297 EXPECT_EQ(0, mIndex); 298 mIndex += opList.count; 299 EXPECT_EQ(4u, opList.count); 300 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip); 301 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right, 302 opList.clipSideFlags); 303 } 304 }; 305 auto node = TestUtils::createNode(0, 0, 100, 100, 306 [](RenderProperties& props, TestCanvas& canvas) { 307 SkBitmap bitmap = TestUtils::createSkBitmap(20, 20); 308 309 // left side clipped (to inset left half) 310 canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op); 311 canvas.drawBitmap(bitmap, 0, 40, nullptr); 312 313 // top side clipped (to inset top half) 314 canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op); 315 canvas.drawBitmap(bitmap, 40, 0, nullptr); 316 317 // right side clipped (to inset right half) 318 canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op); 319 canvas.drawBitmap(bitmap, 80, 40, nullptr); 320 321 // bottom not clipped, just abutting (inset bottom half) 322 canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op); 323 canvas.drawBitmap(bitmap, 40, 70, nullptr); 324 }); 325 326 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 327 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 328 ClippedMergingTestRenderer renderer; 329 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 330 EXPECT_EQ(4, renderer.getIndex()); 331} 332 333RENDERTHREAD_TEST(FrameBuilder, textMerging) { 334 class TextMergingTestRenderer : public TestRendererBase { 335 public: 336 void onMergedTextOps(const MergedBakedOpList& opList) override { 337 EXPECT_EQ(0, mIndex); 338 mIndex += opList.count; 339 EXPECT_EQ(2u, opList.count); 340 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags); 341 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags); 342 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags); 343 } 344 }; 345 auto node = TestUtils::createNode(0, 0, 400, 400, 346 [](RenderProperties& props, TestCanvas& canvas) { 347 SkPaint paint; 348 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 349 paint.setAntiAlias(true); 350 paint.setTextSize(50); 351 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped 352 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped 353 }); 354 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 355 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 356 TextMergingTestRenderer renderer; 357 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 358 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; 359} 360 361RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { 362 const int LOOPS = 5; 363 class TextStrikethroughTestRenderer : public TestRendererBase { 364 public: 365 void onRectOp(const RectOp& op, const BakedOpState& state) override { 366 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text"; 367 } 368 void onMergedTextOps(const MergedBakedOpList& opList) override { 369 EXPECT_EQ(0, mIndex); 370 mIndex += opList.count; 371 EXPECT_EQ(5u, opList.count); 372 } 373 }; 374 auto node = TestUtils::createNode(0, 0, 200, 2000, 375 [](RenderProperties& props, RecordingCanvas& canvas) { 376 SkPaint textPaint; 377 textPaint.setAntiAlias(true); 378 textPaint.setTextSize(20); 379 textPaint.setStrikeThruText(true); 380 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 381 for (int i = 0; i < LOOPS; i++) { 382 TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); 383 } 384 }); 385 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000, 386 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 387 TextStrikethroughTestRenderer renderer; 388 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 389 EXPECT_EQ(2 * LOOPS, renderer.getIndex()) 390 << "Expect number of ops = 2 * loop count"; 391} 392 393static auto styles = { 394 SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style }; 395 396RENDERTHREAD_TEST(FrameBuilder, textStyle) { 397 class TextStyleTestRenderer : public TestRendererBase { 398 public: 399 void onMergedTextOps(const MergedBakedOpList& opList) override { 400 ASSERT_EQ(0, mIndex); 401 ASSERT_EQ(3u, opList.count); 402 mIndex += opList.count; 403 404 int index = 0; 405 for (auto style : styles) { 406 auto state = opList.states[index++]; 407 ASSERT_EQ(style, state->op->paint->getStyle()) 408 << "Remainder of validation relies upon stable merged order"; 409 ASSERT_EQ(0, state->computedState.clipSideFlags) 410 << "Clipped bounds validation requires unclipped ops"; 411 } 412 413 Rect fill = opList.states[0]->computedState.clippedBounds; 414 Rect stroke = opList.states[1]->computedState.clippedBounds; 415 EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds) 416 << "Stroke+Fill should be same as stroke"; 417 418 EXPECT_TRUE(stroke.contains(fill)); 419 EXPECT_FALSE(fill.contains(stroke)); 420 421 // outset by half the stroke width 422 Rect outsetFill(fill); 423 outsetFill.outset(5); 424 EXPECT_EQ(stroke, outsetFill); 425 } 426 }; 427 auto node = TestUtils::createNode(0, 0, 400, 400, 428 [](RenderProperties& props, TestCanvas& canvas) { 429 SkPaint paint; 430 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 431 paint.setAntiAlias(true); 432 paint.setTextSize(50); 433 paint.setStrokeWidth(10); 434 435 // draw 3 copies of the same text overlapping, each with a different style. 436 // They'll get merged, but with 437 for (auto style : styles) { 438 paint.setStyle(style); 439 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); 440 } 441 }); 442 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 443 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 444 TextStyleTestRenderer renderer; 445 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 446 EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops"; 447} 448 449RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { 450 class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase { 451 public: 452 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 453 EXPECT_EQ(0, mIndex++); 454 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect()); 455 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds); 456 457 Matrix4 expected; 458 expected.loadTranslate(5, 5, 0); 459 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 460 } 461 }; 462 463 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 464 SkMatrix::MakeTrans(5, 5)); 465 466 auto node = TestUtils::createNode(0, 0, 200, 200, 467 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 468 canvas.save(SaveFlags::MatrixClip); 469 canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op); 470 canvas.drawLayer(layerUpdater.get()); 471 canvas.restore(); 472 }); 473 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 474 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 475 TextureLayerClipLocalMatrixTestRenderer renderer; 476 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 477 EXPECT_EQ(1, renderer.getIndex()); 478} 479 480RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) { 481 class TextureLayerCombineMatricesTestRenderer : public TestRendererBase { 482 public: 483 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { 484 EXPECT_EQ(0, mIndex++); 485 486 Matrix4 expected; 487 expected.loadTranslate(35, 45, 0); 488 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 489 } 490 }; 491 492 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 493 SkMatrix::MakeTrans(5, 5)); 494 495 auto node = TestUtils::createNode(0, 0, 200, 200, 496 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 497 canvas.save(SaveFlags::MatrixClip); 498 canvas.translate(30, 40); 499 canvas.drawLayer(layerUpdater.get()); 500 canvas.restore(); 501 }); 502 503 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 504 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 505 TextureLayerCombineMatricesTestRenderer renderer; 506 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 507 EXPECT_EQ(1, renderer.getIndex()); 508} 509 510RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) { 511 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100, 512 SkMatrix::MakeTrans(5, 5)); 513 layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected 514 515 auto node = TestUtils::createNode(0, 0, 200, 200, 516 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { 517 canvas.drawLayer(layerUpdater.get()); 518 }); 519 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 520 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 521 FailRenderer renderer; 522 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 523} 524 525RENDERTHREAD_TEST(FrameBuilder, functor_reject) { 526 class FunctorTestRenderer : public TestRendererBase { 527 public: 528 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override { 529 EXPECT_EQ(0, mIndex++); 530 } 531 }; 532 Functor noopFunctor; 533 534 // 1 million pixel tall view, scrolled down 80% 535 auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000, 536 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { 537 canvas.translate(0, -800000); 538 canvas.callDrawGLFunction(&noopFunctor); 539 }); 540 541 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 542 TestUtils::createSyncedNodeList(scrolledFunctorView), 543 sLightGeometry, Caches::getInstance()); 544 FunctorTestRenderer renderer; 545 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 546 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected"; 547} 548 549RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) { 550 class ColorTestRenderer : public TestRendererBase { 551 public: 552 void onColorOp(const ColorOp& op, const BakedOpState& state) override { 553 EXPECT_EQ(0, mIndex++); 554 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds) 555 << "Color op should be expanded to bounds of surrounding"; 556 } 557 }; 558 559 auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10, 560 [](RenderProperties& props, RecordingCanvas& canvas) { 561 props.setClipToBounds(false); 562 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); 563 }); 564 565 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 566 TestUtils::createSyncedNodeList(unclippedColorView), 567 sLightGeometry, Caches::getInstance()); 568 ColorTestRenderer renderer; 569 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 570 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected"; 571} 572 573TEST(FrameBuilder, renderNode) { 574 class RenderNodeTestRenderer : public TestRendererBase { 575 public: 576 void onRectOp(const RectOp& op, const BakedOpState& state) override { 577 switch(mIndex++) { 578 case 0: 579 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 580 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 581 break; 582 case 1: 583 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds); 584 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 585 break; 586 default: 587 ADD_FAILURE(); 588 } 589 } 590 }; 591 592 auto child = TestUtils::createNode(10, 10, 110, 110, 593 [](RenderProperties& props, RecordingCanvas& canvas) { 594 SkPaint paint; 595 paint.setColor(SK_ColorWHITE); 596 canvas.drawRect(0, 0, 100, 100, paint); 597 }); 598 599 auto parent = TestUtils::createNode(0, 0, 200, 200, 600 [&child](RenderProperties& props, RecordingCanvas& canvas) { 601 SkPaint paint; 602 paint.setColor(SK_ColorDKGRAY); 603 canvas.drawRect(0, 0, 200, 200, paint); 604 605 canvas.save(SaveFlags::MatrixClip); 606 canvas.translate(40, 40); 607 canvas.drawRenderNode(child.get()); 608 canvas.restore(); 609 }); 610 611 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 612 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 613 RenderNodeTestRenderer renderer; 614 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 615 EXPECT_EQ(2, renderer.getIndex()); 616} 617 618RENDERTHREAD_TEST(FrameBuilder, clipped) { 619 class ClippedTestRenderer : public TestRendererBase { 620 public: 621 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { 622 EXPECT_EQ(0, mIndex++); 623 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds); 624 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect()); 625 EXPECT_TRUE(state.computedState.transform.isIdentity()); 626 } 627 }; 628 629 auto node = TestUtils::createNode(0, 0, 200, 200, 630 [](RenderProperties& props, RecordingCanvas& canvas) { 631 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200); 632 canvas.drawBitmap(bitmap, 0, 0, nullptr); 633 }); 634 635 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, 636 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver 637 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 638 ClippedTestRenderer renderer; 639 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 640} 641 642RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { 643 class SaveLayerSimpleTestRenderer : public TestRendererBase { 644 public: 645 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 646 EXPECT_EQ(0, mIndex++); 647 EXPECT_EQ(180u, width); 648 EXPECT_EQ(180u, height); 649 return nullptr; 650 } 651 void endLayer() override { 652 EXPECT_EQ(2, mIndex++); 653 } 654 void onRectOp(const RectOp& op, const BakedOpState& state) override { 655 EXPECT_EQ(1, mIndex++); 656 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds); 657 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds); 658 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect()); 659 660 Matrix4 expectedTransform; 661 expectedTransform.loadTranslate(-10, -10, 0); 662 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform); 663 } 664 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 665 EXPECT_EQ(3, mIndex++); 666 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 667 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 668 EXPECT_TRUE(state.computedState.transform.isIdentity()); 669 } 670 }; 671 672 auto node = TestUtils::createNode(0, 0, 200, 200, 673 [](RenderProperties& props, RecordingCanvas& canvas) { 674 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer); 675 canvas.drawRect(10, 10, 190, 190, SkPaint()); 676 canvas.restore(); 677 }); 678 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 679 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 680 SaveLayerSimpleTestRenderer renderer; 681 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 682 EXPECT_EQ(4, renderer.getIndex()); 683} 684 685RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { 686 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as: 687 * - startTemporaryLayer2, rect2 endLayer2 688 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1 689 * - startFrame, layerOp1, endFrame 690 */ 691 class SaveLayerNestedTestRenderer : public TestRendererBase { 692 public: 693 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 694 const int index = mIndex++; 695 if (index == 0) { 696 EXPECT_EQ(400u, width); 697 EXPECT_EQ(400u, height); 698 return (OffscreenBuffer*) 0x400; 699 } else if (index == 3) { 700 EXPECT_EQ(800u, width); 701 EXPECT_EQ(800u, height); 702 return (OffscreenBuffer*) 0x800; 703 } else { ADD_FAILURE(); } 704 return (OffscreenBuffer*) nullptr; 705 } 706 void endLayer() override { 707 int index = mIndex++; 708 EXPECT_TRUE(index == 2 || index == 6); 709 } 710 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 711 EXPECT_EQ(7, mIndex++); 712 } 713 void endFrame(const Rect& repaintRect) override { 714 EXPECT_EQ(9, mIndex++); 715 } 716 void onRectOp(const RectOp& op, const BakedOpState& state) override { 717 const int index = mIndex++; 718 if (index == 1) { 719 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect 720 } else if (index == 4) { 721 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect 722 } else { ADD_FAILURE(); } 723 } 724 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 725 const int index = mIndex++; 726 if (index == 5) { 727 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle); 728 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer 729 } else if (index == 8) { 730 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle); 731 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer 732 } else { ADD_FAILURE(); } 733 } 734 }; 735 736 auto node = TestUtils::createNode(0, 0, 800, 800, 737 [](RenderProperties& props, RecordingCanvas& canvas) { 738 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer); 739 { 740 canvas.drawRect(0, 0, 800, 800, SkPaint()); 741 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer); 742 { 743 canvas.drawRect(0, 0, 400, 400, SkPaint()); 744 } 745 canvas.restore(); 746 } 747 canvas.restore(); 748 }); 749 750 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800, 751 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 752 SaveLayerNestedTestRenderer renderer; 753 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 754 EXPECT_EQ(10, renderer.getIndex()); 755} 756 757RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { 758 auto node = TestUtils::createNode(0, 0, 200, 200, 759 [](RenderProperties& props, RecordingCanvas& canvas) { 760 canvas.save(SaveFlags::MatrixClip); 761 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); 762 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer); 763 764 // draw within save layer may still be recorded, but shouldn't be drawn 765 canvas.drawRect(200, 200, 400, 400, SkPaint()); 766 767 canvas.restore(); 768 canvas.restore(); 769 }); 770 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 771 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 772 773 FailRenderer renderer; 774 // should see no ops, even within the layer, since the layer should be rejected 775 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 776} 777 778RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { 779 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase { 780 public: 781 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 782 EXPECT_EQ(0, mIndex++); 783 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 784 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 785 EXPECT_TRUE(state.computedState.transform.isIdentity()); 786 } 787 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 788 EXPECT_EQ(1, mIndex++); 789 ASSERT_NE(nullptr, op.paint); 790 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); 791 } 792 void onRectOp(const RectOp& op, const BakedOpState& state) override { 793 EXPECT_EQ(2, mIndex++); 794 EXPECT_EQ(Rect(200, 200), op.unmappedBounds); 795 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds); 796 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); 797 EXPECT_TRUE(state.computedState.transform.isIdentity()); 798 } 799 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 800 EXPECT_EQ(3, mIndex++); 801 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); 802 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState); 803 EXPECT_TRUE(state.computedState.transform.isIdentity()); 804 } 805 }; 806 807 auto node = TestUtils::createNode(0, 0, 200, 200, 808 [](RenderProperties& props, RecordingCanvas& canvas) { 809 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 810 canvas.drawRect(0, 0, 200, 200, SkPaint()); 811 canvas.restore(); 812 }); 813 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 814 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 815 SaveLayerUnclippedSimpleTestRenderer renderer; 816 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 817 EXPECT_EQ(4, renderer.getIndex()); 818} 819 820RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { 821 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase { 822 public: 823 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 824 int index = mIndex++; 825 EXPECT_GT(4, index); 826 EXPECT_EQ(5, op.unmappedBounds.getWidth()); 827 EXPECT_EQ(5, op.unmappedBounds.getHeight()); 828 if (index == 0) { 829 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds); 830 } else if (index == 1) { 831 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds); 832 } else if (index == 2) { 833 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds); 834 } else if (index == 3) { 835 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds); 836 } 837 } 838 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 839 EXPECT_EQ(4, mIndex++); 840 ASSERT_EQ(op.vertexCount, 16u); 841 for (size_t i = 0; i < op.vertexCount; i++) { 842 auto v = op.vertices[i]; 843 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200); 844 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200); 845 } 846 } 847 void onRectOp(const RectOp& op, const BakedOpState& state) override { 848 EXPECT_EQ(5, mIndex++); 849 } 850 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 851 EXPECT_LT(5, mIndex++); 852 } 853 }; 854 855 auto node = TestUtils::createNode(0, 0, 200, 200, 856 [](RenderProperties& props, RecordingCanvas& canvas) { 857 858 int restoreTo = canvas.save(SaveFlags::MatrixClip); 859 canvas.scale(2, 2); 860 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip); 861 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip); 862 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip); 863 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip); 864 canvas.drawRect(0, 0, 100, 100, SkPaint()); 865 canvas.restoreToCount(restoreTo); 866 }); 867 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 868 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 869 SaveLayerUnclippedMergedClearsTestRenderer renderer; 870 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 871 EXPECT_EQ(10, renderer.getIndex()) 872 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect."; 873} 874 875RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { 876 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase { 877 public: 878 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 879 EXPECT_EQ(0, mIndex++); 880 } 881 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 882 EXPECT_EQ(1, mIndex++); 883 ASSERT_NE(nullptr, op.paint); 884 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint)); 885 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds) 886 << "Expect dirty rect as clip"; 887 ASSERT_NE(nullptr, state.computedState.clipState); 888 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect); 889 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 890 } 891 void onRectOp(const RectOp& op, const BakedOpState& state) override { 892 EXPECT_EQ(2, mIndex++); 893 } 894 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 895 EXPECT_EQ(3, mIndex++); 896 } 897 }; 898 899 auto node = TestUtils::createNode(0, 0, 200, 200, 900 [](RenderProperties& props, RecordingCanvas& canvas) { 901 // save smaller than clip, so we get unclipped behavior 902 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0)); 903 canvas.drawRect(0, 0, 200, 200, SkPaint()); 904 canvas.restore(); 905 }); 906 907 // draw with partial screen dirty, and assert we see that rect later 908 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, 909 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 910 SaveLayerUnclippedClearClipTestRenderer renderer; 911 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 912 EXPECT_EQ(4, renderer.getIndex()); 913} 914 915RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { 916 auto node = TestUtils::createNode(0, 0, 200, 200, 917 [](RenderProperties& props, RecordingCanvas& canvas) { 918 // unclipped savelayer + rect both in area that won't intersect with dirty 919 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0)); 920 canvas.drawRect(100, 100, 200, 200, SkPaint()); 921 canvas.restore(); 922 }); 923 924 // draw with partial screen dirty that doesn't intersect with savelayer 925 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, 926 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 927 FailRenderer renderer; 928 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 929} 930 931/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as: 932 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer 933 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe 934 */ 935RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { 936 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase { 937 public: 938 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 939 EXPECT_EQ(0, mIndex++); // savelayer first 940 return (OffscreenBuffer*)0xabcd; 941 } 942 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override { 943 int index = mIndex++; 944 EXPECT_TRUE(index == 1 || index == 7); 945 } 946 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override { 947 int index = mIndex++; 948 EXPECT_TRUE(index == 2 || index == 8); 949 } 950 void onRectOp(const RectOp& op, const BakedOpState& state) override { 951 EXPECT_EQ(3, mIndex++); 952 Matrix4 expected; 953 expected.loadTranslate(-100, -100, 0); 954 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds); 955 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform); 956 } 957 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override { 958 int index = mIndex++; 959 EXPECT_TRUE(index == 4 || index == 10); 960 } 961 void endLayer() override { 962 EXPECT_EQ(5, mIndex++); 963 } 964 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 965 EXPECT_EQ(6, mIndex++); 966 } 967 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 968 EXPECT_EQ(9, mIndex++); 969 } 970 void endFrame(const Rect& repaintRect) override { 971 EXPECT_EQ(11, mIndex++); 972 } 973 }; 974 975 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping 976 [](RenderProperties& props, RecordingCanvas& canvas) { 977 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped 978 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped 979 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped 980 canvas.drawRect(200, 200, 300, 300, SkPaint()); 981 canvas.restore(); 982 canvas.restore(); 983 canvas.restore(); 984 }); 985 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600, 986 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 987 SaveLayerUnclippedComplexTestRenderer renderer; 988 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 989 EXPECT_EQ(12, renderer.getIndex()); 990} 991 992RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { 993 class HwLayerSimpleTestRenderer : public TestRendererBase { 994 public: 995 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 996 EXPECT_EQ(0, mIndex++); 997 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 998 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 999 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); 1000 } 1001 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1002 EXPECT_EQ(1, mIndex++); 1003 1004 EXPECT_TRUE(state.computedState.transform.isIdentity()) 1005 << "Transform should be reset within layer"; 1006 1007 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) 1008 << "Damage rect should be used to clip layer content"; 1009 } 1010 void endLayer() override { 1011 EXPECT_EQ(2, mIndex++); 1012 } 1013 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1014 EXPECT_EQ(3, mIndex++); 1015 } 1016 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1017 EXPECT_EQ(4, mIndex++); 1018 } 1019 void endFrame(const Rect& repaintRect) override { 1020 EXPECT_EQ(5, mIndex++); 1021 } 1022 }; 1023 1024 auto node = TestUtils::createNode(10, 10, 110, 110, 1025 [](RenderProperties& props, RecordingCanvas& canvas) { 1026 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1027 SkPaint paint; 1028 paint.setColor(SK_ColorWHITE); 1029 canvas.drawRect(0, 0, 100, 100, paint); 1030 }); 1031 OffscreenBuffer** layerHandle = node->getLayerHandle(); 1032 1033 // create RenderNode's layer here in same way prepareTree would 1034 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1035 *layerHandle = &layer; 1036 1037 auto syncedNodeList = TestUtils::createSyncedNodeList(node); 1038 1039 // only enqueue partial damage 1040 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1041 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); 1042 1043 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1044 syncedNodeList, sLightGeometry, Caches::getInstance()); 1045 HwLayerSimpleTestRenderer renderer; 1046 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1047 EXPECT_EQ(6, renderer.getIndex()); 1048 1049 // clean up layer pointer, so we can safely destruct RenderNode 1050 *layerHandle = nullptr; 1051} 1052 1053RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { 1054 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as: 1055 * - startRepaintLayer(child), rect(grey), endLayer 1056 * - startTemporaryLayer, drawLayer(child), endLayer 1057 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer 1058 * - startFrame, drawLayer(parent), endLayerb 1059 */ 1060 class HwLayerComplexTestRenderer : public TestRendererBase { 1061 public: 1062 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) { 1063 EXPECT_EQ(3, mIndex++); // savelayer first 1064 return (OffscreenBuffer*)0xabcd; 1065 } 1066 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1067 int index = mIndex++; 1068 if (index == 0) { 1069 // starting inner layer 1070 EXPECT_EQ(100u, offscreenBuffer->viewportWidth); 1071 EXPECT_EQ(100u, offscreenBuffer->viewportHeight); 1072 } else if (index == 6) { 1073 // starting outer layer 1074 EXPECT_EQ(200u, offscreenBuffer->viewportWidth); 1075 EXPECT_EQ(200u, offscreenBuffer->viewportHeight); 1076 } else { ADD_FAILURE(); } 1077 } 1078 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1079 int index = mIndex++; 1080 if (index == 1) { 1081 // inner layer's rect (white) 1082 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1083 } else if (index == 7) { 1084 // outer layer's rect (grey) 1085 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1086 } else { ADD_FAILURE(); } 1087 } 1088 void endLayer() override { 1089 int index = mIndex++; 1090 EXPECT_TRUE(index == 2 || index == 5 || index == 9); 1091 } 1092 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { 1093 EXPECT_EQ(10, mIndex++); 1094 } 1095 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1096 OffscreenBuffer* layer = *op.layerHandle; 1097 int index = mIndex++; 1098 if (index == 4) { 1099 EXPECT_EQ(100u, layer->viewportWidth); 1100 EXPECT_EQ(100u, layer->viewportHeight); 1101 } else if (index == 8) { 1102 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle); 1103 } else if (index == 11) { 1104 EXPECT_EQ(200u, layer->viewportWidth); 1105 EXPECT_EQ(200u, layer->viewportHeight); 1106 } else { ADD_FAILURE(); } 1107 } 1108 void endFrame(const Rect& repaintRect) override { 1109 EXPECT_EQ(12, mIndex++); 1110 } 1111 }; 1112 1113 auto child = TestUtils::createNode(50, 50, 150, 150, 1114 [](RenderProperties& props, RecordingCanvas& canvas) { 1115 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1116 SkPaint paint; 1117 paint.setColor(SK_ColorWHITE); 1118 canvas.drawRect(0, 0, 100, 100, paint); 1119 }); 1120 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1121 *(child->getLayerHandle()) = &childLayer; 1122 1123 RenderNode* childPtr = child.get(); 1124 auto parent = TestUtils::createNode(0, 0, 200, 200, 1125 [childPtr](RenderProperties& props, RecordingCanvas& canvas) { 1126 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1127 SkPaint paint; 1128 paint.setColor(SK_ColorDKGRAY); 1129 canvas.drawRect(0, 0, 200, 200, paint); 1130 1131 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer); 1132 canvas.drawRenderNode(childPtr); 1133 canvas.restore(); 1134 }); 1135 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1136 *(parent->getLayerHandle()) = &parentLayer; 1137 1138 auto syncedList = TestUtils::createSyncedNodeList(parent); 1139 1140 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1141 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); 1142 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); 1143 1144 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1145 syncedList, sLightGeometry, Caches::getInstance()); 1146 HwLayerComplexTestRenderer renderer; 1147 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1148 EXPECT_EQ(13, renderer.getIndex()); 1149 1150 // clean up layer pointers, so we can safely destruct RenderNodes 1151 *(child->getLayerHandle()) = nullptr; 1152 *(parent->getLayerHandle()) = nullptr; 1153} 1154 1155static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { 1156 SkPaint paint; 1157 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel 1158 canvas->drawRect(0, 0, 100, 100, paint); 1159} 1160static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { 1161 auto node = TestUtils::createNode(0, 0, 100, 100, 1162 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { 1163 drawOrderedRect(&canvas, expectedDrawOrder); 1164 }); 1165 node->mutateStagingProperties().setTranslationZ(z); 1166 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); 1167 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership 1168} 1169RENDERTHREAD_TEST(FrameBuilder, zReorder) { 1170 class ZReorderTestRenderer : public TestRendererBase { 1171 public: 1172 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1173 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel 1174 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; 1175 } 1176 }; 1177 1178 auto parent = TestUtils::createNode(0, 0, 100, 100, 1179 [](RenderProperties& props, RecordingCanvas& canvas) { 1180 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder 1181 drawOrderedRect(&canvas, 1); 1182 canvas.insertReorderBarrier(true); 1183 drawOrderedNode(&canvas, 6, 2.0f); 1184 drawOrderedRect(&canvas, 3); 1185 drawOrderedNode(&canvas, 4, 0.0f); 1186 drawOrderedRect(&canvas, 5); 1187 drawOrderedNode(&canvas, 2, -2.0f); 1188 drawOrderedNode(&canvas, 7, 2.0f); 1189 canvas.insertReorderBarrier(false); 1190 drawOrderedRect(&canvas, 8); 1191 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder 1192 }); 1193 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 1194 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1195 ZReorderTestRenderer renderer; 1196 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1197 EXPECT_EQ(10, renderer.getIndex()); 1198}; 1199 1200RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { 1201 static const int scrollX = 5; 1202 static const int scrollY = 10; 1203 class ProjectionReorderTestRenderer : public TestRendererBase { 1204 public: 1205 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1206 const int index = mIndex++; 1207 1208 Matrix4 expectedMatrix; 1209 switch (index) { 1210 case 0: 1211 EXPECT_EQ(Rect(100, 100), op.unmappedBounds); 1212 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); 1213 expectedMatrix.loadIdentity(); 1214 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1215 break; 1216 case 1: 1217 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); 1218 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); 1219 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0); 1220 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1221 EXPECT_EQ(Rect(-35, -30, 45, 50), 1222 Rect(state.computedState.localProjectionPathMask->getBounds())); 1223 break; 1224 case 2: 1225 EXPECT_EQ(Rect(100, 50), op.unmappedBounds); 1226 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); 1227 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); 1228 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask); 1229 break; 1230 default: 1231 ADD_FAILURE(); 1232 } 1233 EXPECT_EQ(expectedMatrix, state.computedState.transform); 1234 } 1235 }; 1236 1237 /** 1238 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) 1239 * with a projecting child (P) of its own. P would normally draw between B and C's "background" 1240 * draw, but because it is projected backwards, it's drawn in between B and C. 1241 * 1242 * The parent is scrolled by scrollX/scrollY, but this does not affect the background 1243 * (which isn't affected by scroll). 1244 */ 1245 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100, 1246 [](RenderProperties& properties, RecordingCanvas& canvas) { 1247 properties.setProjectionReceiver(true); 1248 // scroll doesn't apply to background, so undone via translationX/Y 1249 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1250 properties.setTranslationX(scrollX); 1251 properties.setTranslationY(scrollY); 1252 1253 SkPaint paint; 1254 paint.setColor(SK_ColorWHITE); 1255 canvas.drawRect(0, 0, 100, 100, paint); 1256 }); 1257 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50, 1258 [](RenderProperties& properties, RecordingCanvas& canvas) { 1259 properties.setProjectBackwards(true); 1260 properties.setClipToBounds(false); 1261 SkPaint paint; 1262 paint.setColor(SK_ColorDKGRAY); 1263 canvas.drawRect(-10, -10, 60, 60, paint); 1264 }); 1265 auto child = TestUtils::createNode(0, 50, 100, 100, 1266 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1267 SkPaint paint; 1268 paint.setColor(SK_ColorBLUE); 1269 canvas.drawRect(0, 0, 100, 50, paint); 1270 canvas.drawRenderNode(projectingRipple.get()); 1271 }); 1272 auto parent = TestUtils::createNode(0, 0, 100, 100, 1273 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1274 // Set a rect outline for the projecting ripple to be masked against. 1275 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f); 1276 1277 canvas.save(SaveFlags::MatrixClip); 1278 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1279 canvas.drawRenderNode(receiverBackground.get()); 1280 canvas.drawRenderNode(child.get()); 1281 canvas.restore(); 1282 }); 1283 1284 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, 1285 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1286 ProjectionReorderTestRenderer renderer; 1287 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1288 EXPECT_EQ(3, renderer.getIndex()); 1289} 1290 1291RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { 1292 static const int scrollX = 5; 1293 static const int scrollY = 10; 1294 class ProjectionHwLayerTestRenderer : public TestRendererBase { 1295 public: 1296 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1297 EXPECT_EQ(0, mIndex++); 1298 } 1299 void onArcOp(const ArcOp& op, const BakedOpState& state) override { 1300 EXPECT_EQ(1, mIndex++); 1301 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1302 } 1303 void endLayer() override { 1304 EXPECT_EQ(2, mIndex++); 1305 } 1306 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1307 EXPECT_EQ(3, mIndex++); 1308 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1309 } 1310 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1311 EXPECT_EQ(4, mIndex++); 1312 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask); 1313 Matrix4 expected; 1314 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0); 1315 EXPECT_EQ(expected, state.computedState.transform); 1316 EXPECT_EQ(Rect(-85, -80, 295, 300), 1317 Rect(state.computedState.localProjectionPathMask->getBounds())); 1318 } 1319 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1320 EXPECT_EQ(5, mIndex++); 1321 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask); 1322 } 1323 }; 1324 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1325 [](RenderProperties& properties, RecordingCanvas& canvas) { 1326 properties.setProjectionReceiver(true); 1327 // scroll doesn't apply to background, so undone via translationX/Y 1328 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1329 properties.setTranslationX(scrollX); 1330 properties.setTranslationY(scrollY); 1331 1332 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1333 }); 1334 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1335 [](RenderProperties& properties, RecordingCanvas& canvas) { 1336 properties.setProjectBackwards(true); 1337 properties.setClipToBounds(false); 1338 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds 1339 }); 1340 auto child = TestUtils::createNode(100, 100, 300, 300, 1341 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1342 properties.mutateLayerProperties().setType(LayerType::RenderLayer); 1343 canvas.drawRenderNode(projectingRipple.get()); 1344 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint()); 1345 }); 1346 auto parent = TestUtils::createNode(0, 0, 400, 400, 1347 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1348 // Set a rect outline for the projecting ripple to be masked against. 1349 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f); 1350 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1351 canvas.drawRenderNode(receiverBackground.get()); 1352 canvas.drawRenderNode(child.get()); 1353 }); 1354 1355 OffscreenBuffer** layerHandle = child->getLayerHandle(); 1356 1357 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1358 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200); 1359 Matrix4 windowTransform; 1360 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin 1361 layer.setWindowTransform(windowTransform); 1362 *layerHandle = &layer; 1363 1364 auto syncedList = TestUtils::createSyncedNodeList(parent); 1365 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1366 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); 1367 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 1368 syncedList, sLightGeometry, Caches::getInstance()); 1369 ProjectionHwLayerTestRenderer renderer; 1370 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1371 EXPECT_EQ(6, renderer.getIndex()); 1372 1373 // clean up layer pointer, so we can safely destruct RenderNode 1374 *layerHandle = nullptr; 1375} 1376 1377RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { 1378 static const int scrollX = 500000; 1379 static const int scrollY = 0; 1380 class ProjectionChildScrollTestRenderer : public TestRendererBase { 1381 public: 1382 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1383 EXPECT_EQ(0, mIndex++); 1384 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1385 } 1386 void onOvalOp(const OvalOp& op, const BakedOpState& state) override { 1387 EXPECT_EQ(1, mIndex++); 1388 ASSERT_NE(nullptr, state.computedState.clipState); 1389 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode); 1390 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect); 1391 EXPECT_TRUE(state.computedState.transform.isIdentity()); 1392 } 1393 }; 1394 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400, 1395 [](RenderProperties& properties, RecordingCanvas& canvas) { 1396 properties.setProjectionReceiver(true); 1397 canvas.drawRect(0, 0, 400, 400, SkPaint()); 1398 }); 1399 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200, 1400 [](RenderProperties& properties, RecordingCanvas& canvas) { 1401 // scroll doesn't apply to background, so undone via translationX/Y 1402 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! 1403 properties.setTranslationX(scrollX); 1404 properties.setTranslationY(scrollY); 1405 properties.setProjectBackwards(true); 1406 properties.setClipToBounds(false); 1407 canvas.drawOval(0, 0, 200, 200, SkPaint()); 1408 }); 1409 auto child = TestUtils::createNode(0, 0, 400, 400, 1410 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { 1411 // Record time clip will be ignored by projectee 1412 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op); 1413 1414 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) 1415 canvas.drawRenderNode(projectingRipple.get()); 1416 }); 1417 auto parent = TestUtils::createNode(0, 0, 400, 400, 1418 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { 1419 canvas.drawRenderNode(receiverBackground.get()); 1420 canvas.drawRenderNode(child.get()); 1421 }); 1422 1423 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, 1424 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1425 ProjectionChildScrollTestRenderer renderer; 1426 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1427 EXPECT_EQ(2, renderer.getIndex()); 1428} 1429 1430// creates a 100x100 shadow casting node with provided translationZ 1431static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) { 1432 return TestUtils::createNode(0, 0, 100, 100, 1433 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { 1434 properties.setTranslationZ(translationZ); 1435 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); 1436 SkPaint paint; 1437 paint.setColor(SK_ColorWHITE); 1438 canvas.drawRect(0, 0, 100, 100, paint); 1439 }); 1440} 1441 1442RENDERTHREAD_TEST(FrameBuilder, shadow) { 1443 class ShadowTestRenderer : public TestRendererBase { 1444 public: 1445 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1446 EXPECT_EQ(0, mIndex++); 1447 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha); 1448 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr)); 1449 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY); 1450 1451 Matrix4 expectedZ; 1452 expectedZ.loadTranslate(0, 0, 5); 1453 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ); 1454 } 1455 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1456 EXPECT_EQ(1, mIndex++); 1457 } 1458 }; 1459 1460 auto parent = TestUtils::createNode(0, 0, 200, 200, 1461 [](RenderProperties& props, RecordingCanvas& canvas) { 1462 canvas.insertReorderBarrier(true); 1463 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1464 }); 1465 1466 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1467 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); 1468 ShadowTestRenderer renderer; 1469 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1470 EXPECT_EQ(2, renderer.getIndex()); 1471} 1472 1473RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { 1474 class ShadowSaveLayerTestRenderer : public TestRendererBase { 1475 public: 1476 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1477 EXPECT_EQ(0, mIndex++); 1478 return nullptr; 1479 } 1480 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1481 EXPECT_EQ(1, mIndex++); 1482 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1483 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1484 } 1485 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1486 EXPECT_EQ(2, mIndex++); 1487 } 1488 void endLayer() override { 1489 EXPECT_EQ(3, mIndex++); 1490 } 1491 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1492 EXPECT_EQ(4, mIndex++); 1493 } 1494 }; 1495 1496 auto parent = TestUtils::createNode(0, 0, 200, 200, 1497 [](RenderProperties& props, RecordingCanvas& canvas) { 1498 // save/restore outside of reorderBarrier, so they don't get moved out of place 1499 canvas.translate(20, 10); 1500 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer); 1501 canvas.insertReorderBarrier(true); 1502 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1503 canvas.insertReorderBarrier(false); 1504 canvas.restoreToCount(count); 1505 }); 1506 1507 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1508 TestUtils::createSyncedNodeList(parent), 1509 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1510 ShadowSaveLayerTestRenderer renderer; 1511 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1512 EXPECT_EQ(5, renderer.getIndex()); 1513} 1514 1515RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { 1516 class ShadowHwLayerTestRenderer : public TestRendererBase { 1517 public: 1518 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { 1519 EXPECT_EQ(0, mIndex++); 1520 } 1521 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1522 EXPECT_EQ(1, mIndex++); 1523 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x); 1524 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y); 1525 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius); 1526 } 1527 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1528 EXPECT_EQ(2, mIndex++); 1529 } 1530 void endLayer() override { 1531 EXPECT_EQ(3, mIndex++); 1532 } 1533 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1534 EXPECT_EQ(4, mIndex++); 1535 } 1536 }; 1537 1538 auto parent = TestUtils::createNode(50, 60, 150, 160, 1539 [](RenderProperties& props, RecordingCanvas& canvas) { 1540 props.mutateLayerProperties().setType(LayerType::RenderLayer); 1541 canvas.insertReorderBarrier(true); 1542 canvas.save(SaveFlags::MatrixClip); 1543 canvas.translate(20, 10); 1544 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1545 canvas.restore(); 1546 }); 1547 OffscreenBuffer** layerHandle = parent->getLayerHandle(); 1548 1549 // create RenderNode's layer here in same way prepareTree would, setting windowTransform 1550 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); 1551 Matrix4 windowTransform; 1552 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin 1553 layer.setWindowTransform(windowTransform); 1554 *layerHandle = &layer; 1555 1556 auto syncedList = TestUtils::createSyncedNodeList(parent); 1557 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid 1558 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); 1559 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1560 syncedList, 1561 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance()); 1562 ShadowHwLayerTestRenderer renderer; 1563 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1564 EXPECT_EQ(5, renderer.getIndex()); 1565 1566 // clean up layer pointer, so we can safely destruct RenderNode 1567 *layerHandle = nullptr; 1568} 1569 1570RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { 1571 class ShadowLayeringTestRenderer : public TestRendererBase { 1572 public: 1573 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override { 1574 int index = mIndex++; 1575 EXPECT_TRUE(index == 0 || index == 1); 1576 } 1577 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1578 int index = mIndex++; 1579 EXPECT_TRUE(index == 2 || index == 3); 1580 } 1581 }; 1582 auto parent = TestUtils::createNode(0, 0, 200, 200, 1583 [](RenderProperties& props, RecordingCanvas& canvas) { 1584 canvas.insertReorderBarrier(true); 1585 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); 1586 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); 1587 }); 1588 1589 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1590 TestUtils::createSyncedNodeList(parent), 1591 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); 1592 ShadowLayeringTestRenderer renderer; 1593 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1594 EXPECT_EQ(4, renderer.getIndex()); 1595} 1596 1597static void testProperty(std::function<void(RenderProperties&)> propSetupCallback, 1598 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { 1599 class PropertyTestRenderer : public TestRendererBase { 1600 public: 1601 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) 1602 : mCallback(callback) {} 1603 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1604 EXPECT_EQ(mIndex++, 0); 1605 mCallback(op, state); 1606 } 1607 std::function<void(const RectOp&, const BakedOpState&)> mCallback; 1608 }; 1609 1610 auto node = TestUtils::createNode(0, 0, 100, 100, 1611 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) { 1612 propSetupCallback(props); 1613 SkPaint paint; 1614 paint.setColor(SK_ColorWHITE); 1615 canvas.drawRect(0, 0, 100, 100, paint); 1616 }); 1617 1618 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, 1619 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); 1620 PropertyTestRenderer renderer(opValidateCallback); 1621 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1622 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; 1623} 1624 1625RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) { 1626 testProperty([](RenderProperties& properties) { 1627 properties.setAlpha(0.5f); 1628 properties.setHasOverlappingRendering(false); 1629 }, [](const RectOp& op, const BakedOpState& state) { 1630 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; 1631 }); 1632} 1633 1634RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) { 1635 testProperty([](RenderProperties& properties) { 1636 properties.setClipToBounds(true); 1637 properties.setClipBounds(Rect(10, 20, 300, 400)); 1638 }, [](const RectOp& op, const BakedOpState& state) { 1639 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) 1640 << "Clip rect should be intersection of node bounds and clip bounds"; 1641 }); 1642} 1643 1644RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) { 1645 testProperty([](RenderProperties& properties) { 1646 properties.mutableRevealClip().set(true, 50, 50, 25); 1647 }, [](const RectOp& op, const BakedOpState& state) { 1648 ASSERT_NE(nullptr, state.roundRectClipState); 1649 EXPECT_TRUE(state.roundRectClipState->highPriority); 1650 EXPECT_EQ(25, state.roundRectClipState->radius); 1651 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); 1652 }); 1653} 1654 1655RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) { 1656 testProperty([](RenderProperties& properties) { 1657 properties.mutableOutline().setShouldClip(true); 1658 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); 1659 }, [](const RectOp& op, const BakedOpState& state) { 1660 ASSERT_NE(nullptr, state.roundRectClipState); 1661 EXPECT_FALSE(state.roundRectClipState->highPriority); 1662 EXPECT_EQ(5, state.roundRectClipState->radius); 1663 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); 1664 }); 1665} 1666 1667RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) { 1668 testProperty([](RenderProperties& properties) { 1669 properties.setLeftTopRightBottom(10, 10, 110, 110); 1670 1671 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); 1672 properties.setStaticMatrix(&staticMatrix); 1673 1674 // ignored, since static overrides animation 1675 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); 1676 properties.setAnimationMatrix(&animationMatrix); 1677 1678 properties.setTranslationX(10); 1679 properties.setTranslationY(20); 1680 properties.setScaleX(0.5f); 1681 properties.setScaleY(0.7f); 1682 }, [](const RectOp& op, const BakedOpState& state) { 1683 Matrix4 matrix; 1684 matrix.loadTranslate(10, 10, 0); // left, top 1685 matrix.scale(1.2f, 1.2f, 1); // static matrix 1686 // ignore animation matrix, since static overrides it 1687 1688 // translation xy 1689 matrix.translate(10, 20); 1690 1691 // scale xy (from default pivot - center) 1692 matrix.translate(50, 50); 1693 matrix.scale(0.5f, 0.7f, 1); 1694 matrix.translate(-50, -50); 1695 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) 1696 << "Op draw matrix must match expected combination of transformation properties"; 1697 }); 1698} 1699 1700struct SaveLayerAlphaData { 1701 uint32_t layerWidth = 0; 1702 uint32_t layerHeight = 0; 1703 Rect rectClippedBounds; 1704 Matrix4 rectMatrix; 1705}; 1706/** 1707 * Constructs a view to hit the temporary layer alpha property implementation: 1708 * a) 0 < alpha < 1 1709 * b) too big for layer (larger than maxTextureSize) 1710 * c) overlapping rendering content 1711 * returning observed data about layer size and content clip/transform. 1712 * 1713 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced 1714 * (for efficiency, and to fit in layer size constraints) based on parent clip. 1715 */ 1716void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, 1717 std::function<void(RenderProperties&)> propSetupCallback) { 1718 class SaveLayerAlphaClipTestRenderer : public TestRendererBase { 1719 public: 1720 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) 1721 : mOutData(outData) {} 1722 1723 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { 1724 EXPECT_EQ(0, mIndex++); 1725 mOutData->layerWidth = width; 1726 mOutData->layerHeight = height; 1727 return nullptr; 1728 } 1729 void onRectOp(const RectOp& op, const BakedOpState& state) override { 1730 EXPECT_EQ(1, mIndex++); 1731 1732 mOutData->rectClippedBounds = state.computedState.clippedBounds; 1733 mOutData->rectMatrix = state.computedState.transform; 1734 } 1735 void endLayer() override { 1736 EXPECT_EQ(2, mIndex++); 1737 } 1738 void onLayerOp(const LayerOp& op, const BakedOpState& state) override { 1739 EXPECT_EQ(3, mIndex++); 1740 } 1741 private: 1742 SaveLayerAlphaData* mOutData; 1743 }; 1744 1745 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize()) 1746 << "Node must be bigger than max texture size to exercise saveLayer codepath"; 1747 auto node = TestUtils::createNode(0, 0, 10000, 10000, 1748 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) { 1749 properties.setHasOverlappingRendering(true); 1750 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer 1751 // apply other properties 1752 propSetupCallback(properties); 1753 1754 SkPaint paint; 1755 paint.setColor(SK_ColorWHITE); 1756 canvas.drawRect(0, 0, 10000, 10000, paint); 1757 }); 1758 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height 1759 1760 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, 1761 nodes, sLightGeometry, Caches::getInstance()); 1762 SaveLayerAlphaClipTestRenderer renderer(outObservedData); 1763 frameBuilder.replayBakedOps<TestDispatcher>(renderer); 1764 1765 // assert, since output won't be valid if we haven't seen a save layer triggered 1766 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior."; 1767} 1768 1769RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) { 1770 SaveLayerAlphaData observedData; 1771 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1772 properties.setTranslationX(10); // offset rendering content 1773 properties.setTranslationY(-2000); // offset rendering content 1774 }); 1775 EXPECT_EQ(190u, observedData.layerWidth); 1776 EXPECT_EQ(200u, observedData.layerHeight); 1777 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds) 1778 << "expect content to be clipped to screen area"; 1779 Matrix4 expected; 1780 expected.loadTranslate(0, -2000, 0); 1781 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix) 1782 << "expect content to be translated as part of being clipped"; 1783} 1784 1785RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) { 1786 SaveLayerAlphaData observedData; 1787 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1788 // Translate and rotate the view so that the only visible part is the top left corner of 1789 // the view. It will form an isosceles right triangle with a long side length of 200 at the 1790 // bottom of the viewport. 1791 properties.setTranslationX(100); 1792 properties.setTranslationY(100); 1793 properties.setPivotX(0); 1794 properties.setPivotY(0); 1795 properties.setRotation(45); 1796 }); 1797 // ceil(sqrt(2) / 2 * 200) = 142 1798 EXPECT_EQ(142u, observedData.layerWidth); 1799 EXPECT_EQ(142u, observedData.layerHeight); 1800 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds); 1801 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1802} 1803 1804RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) { 1805 SaveLayerAlphaData observedData; 1806 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { 1807 properties.setPivotX(0); 1808 properties.setPivotY(0); 1809 properties.setScaleX(2); 1810 properties.setScaleY(0.5f); 1811 }); 1812 EXPECT_EQ(100u, observedData.layerWidth); 1813 EXPECT_EQ(400u, observedData.layerHeight); 1814 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds); 1815 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix); 1816} 1817 1818} // namespace uirenderer 1819} // namespace android 1820