• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
~TestRendererBase()48     virtual ~TestRendererBase() {}
startTemporaryLayer(uint32_t,uint32_t)49     virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
50         ADD_FAILURE() << "Temporary layers not expected in this test";
51         return nullptr;
52     }
recycleTemporaryLayer(OffscreenBuffer *)53     virtual void recycleTemporaryLayer(OffscreenBuffer*) {
54         ADD_FAILURE() << "Temporary layers not expected in this test";
55     }
startRepaintLayer(OffscreenBuffer *,const Rect & repaintRect)56     virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
57         ADD_FAILURE() << "Layer repaint not expected in this test";
58     }
endLayer()59     virtual void endLayer() { ADD_FAILURE() << "Layer updates not expected in this test"; }
startFrame(uint32_t width,uint32_t height,const Rect & repaintRect)60     virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
endFrame(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     }
MAP_MERGEABLE_OPS(X)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,simple)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,simpleStroke)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,arcStrokeClip)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,simpleRejection)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,simpleBatching)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,deferRenderNode_translateClip)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,deferRenderNodeScene)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,empty_noFbo0)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,empty_withFbo0)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,avoidOverdraw_rects)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,avoidOverdraw_bitmaps)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,clippedMerging)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,regionClipStopsMerge)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,textMerging)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,textStrikethrough)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,textStyle)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,textureLayer_clipLocalMatrix)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,textureLayer_combineMatrices)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,textureLayer_reject)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,functor_reject)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,deferColorOp_unbounded)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderNode)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,clipped)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayer_simple)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayer_nested)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayer_contentRejection)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayerUnclipped_simple)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayerUnclipped_round)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayerUnclipped_mergedClears)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayerUnclipped_clearClip)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayerUnclipped_reject)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  */
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,saveLayerUnclipped_complex)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,hwLayer_simple)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,hwLayer_complex)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,buildLayer)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 
drawOrderedRect(Canvas * canvas,uint8_t expectedDrawOrder)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 }
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,float z)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 
drawOrderedNode(Canvas * canvas,uint8_t expectedDrawOrder,std::function<void (RenderProperties & props,RecordingCanvas & canvas)> setup)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:
onRectOp(const RectOp & op,const BakedOpState & state)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,zReorder)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorder)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionHwLayer)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionChildScroll)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
createWhiteRectShadowCaster(float 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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,shadow)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,shadowSaveLayer)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,shadowHwLayer)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,shadowLayering)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,shadowClipping)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 
testProperty(std::function<void (RenderProperties &)> propSetupCallback,std::function<void (const RectOp &,const BakedOpState &)> opValidateCallback)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropOverlappingRenderingAlpha)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropClipping)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropRevealClip)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropOutlineClip)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropTransform)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  */
testSaveLayerAlphaClip(SaveLayerAlphaData * outObservedData,std::function<void (RenderProperties &)> propSetupCallback)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropSaveLayerAlphaClipBig)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropSaveLayerAlphaRotate)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,renderPropSaveLayerAlphaScale)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,clip_replace)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderProjectedInMiddle)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderProjectLast)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderNoReceivable)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderParentReceivable)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderSameNodeReceivable)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderProjectedSibling)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderProjectedSibling2)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderGrandparentReceivable)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderTwoReceivables)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderTwoReceivablesLikelyScenario)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 
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder,projectionReorderTwoReceivablesDeeper)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