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