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