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