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