blob: 347d4a5ce7bacd0c74fd9cd8817882299f0b42bf [file] [log] [blame]
Chris Craikb565df12015-10-05 13:00:52 -07001/*
Chris Craik5ea17242016-01-11 14:07:59 -08002 * Copyright (C) 2016 The Android Open Source Project
Chris Craikb565df12015-10-05 13:00:52 -07003 *
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>
Chris Craikd2dfd8f2015-12-16 14:27:20 -080020#include <DeferredLayerUpdater.h>
Chris Craikf158b492016-01-12 14:45:08 -080021#include <FrameBuilder.h>
Chris Craik8ecf41c2015-11-16 10:27:59 -080022#include <LayerUpdateQueue.h>
Chris Craikb565df12015-10-05 13:00:52 -070023#include <RecordedOp.h>
24#include <RecordingCanvas.h>
Chris Craik8160f202015-12-02 14:50:25 -080025#include <tests/common/TestUtils.h>
Chris Craikb565df12015-10-05 13:00:52 -070026
27#include <unordered_map>
28
29namespace android {
30namespace uirenderer {
31
Chris Craik6e068c012016-01-15 16:15:30 -080032const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
33
Chris Craik6fe991e52015-10-20 09:39:42 -070034/**
Chris Craik5854b342015-10-26 15:49:56 -070035 * Virtual class implemented by each test to redirect static operation / state transitions to
36 * virtual methods.
Chris Craik6fe991e52015-10-20 09:39:42 -070037 *
Chris Craik5854b342015-10-26 15:49:56 -070038 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
39 * and allows Renderer vs Dispatching behavior to be merged.
Chris Craik6fe991e52015-10-20 09:39:42 -070040 *
41 * onXXXOp methods fail by default - tests should override ops they expect
Chris Craikd3daa312015-11-06 10:59:56 -080042 * startRepaintLayer fails by default - tests should override if expected
Chris Craik6fe991e52015-10-20 09:39:42 -070043 * startFrame/endFrame do nothing by default - tests should override to intercept
44 */
Chris Craik5854b342015-10-26 15:49:56 -070045class TestRendererBase {
Chris Craik6fe991e52015-10-20 09:39:42 -070046public:
Chris Craik5854b342015-10-26 15:49:56 -070047 virtual ~TestRendererBase() {}
Chris Craikd3daa312015-11-06 10:59:56 -080048 virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
Chris Craik74af6e22016-04-05 13:18:56 -070049 ADD_FAILURE() << "Temporary layers not expected in this test";
Chris Craika6ac95e2015-11-02 18:06:59 -080050 return nullptr;
51 }
Chris Craik74af6e22016-04-05 13:18:56 -070052 virtual void recycleTemporaryLayer(OffscreenBuffer*) {
53 ADD_FAILURE() << "Temporary layers not expected in this test";
54 }
Chris Craik98787e62015-11-13 10:55:30 -080055 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
Chris Craika6ac95e2015-11-02 18:06:59 -080056 ADD_FAILURE() << "Layer repaint not expected in this test";
57 }
58 virtual void endLayer() {
59 ADD_FAILURE() << "Layer updates not expected in this test";
60 }
Chris Craik98787e62015-11-13 10:55:30 -080061 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
Chris Craike4db79d2015-12-22 16:32:23 -080062 virtual void endFrame(const Rect& repaintRect) {}
Chris Craik6fe991e52015-10-20 09:39:42 -070063
Chris Craik15c3f192015-12-03 12:16:56 -080064 // define virtual defaults for single draw methods
65#define X(Type) \
Chris Craika6ac95e2015-11-02 18:06:59 -080066 virtual void on##Type(const Type&, const BakedOpState&) { \
67 ADD_FAILURE() << #Type " not expected in this test"; \
68 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080069 MAP_RENDERABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080070#undef X
71
72 // define virtual defaults for merged draw methods
73#define X(Type) \
74 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
75 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
76 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080077 MAP_MERGEABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080078#undef X
79
Chris Craik5854b342015-10-26 15:49:56 -070080 int getIndex() { return mIndex; }
Chris Craik6fe991e52015-10-20 09:39:42 -070081
Chris Craik5854b342015-10-26 15:49:56 -070082protected:
83 int mIndex = 0;
84};
85
86/**
87 * Dispatches all static methods to similar formed methods on renderer, which fail by default but
Chris Craik8ecf41c2015-11-16 10:27:59 -080088 * are overridden by subclasses per test.
Chris Craik5854b342015-10-26 15:49:56 -070089 */
90class TestDispatcher {
91public:
Chris Craik15c3f192015-12-03 12:16:56 -080092 // define single op methods, which redirect to TestRendererBase
93#define X(Type) \
Chris Craik5854b342015-10-26 15:49:56 -070094 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
95 renderer.on##Type(op, state); \
Chris Craik6fe991e52015-10-20 09:39:42 -070096 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080097 MAP_RENDERABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -080098#undef X
99
100 // define merged op methods, which redirect to TestRendererBase
101#define X(Type) \
102 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
103 renderer.onMerged##Type##s(opList); \
104 }
Chris Craik7cbf63d2016-01-06 13:46:52 -0800105 MAP_MERGEABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -0800106#undef X
Chris Craik6fe991e52015-10-20 09:39:42 -0700107};
Chris Craikb565df12015-10-05 13:00:52 -0700108
Chris Craik5854b342015-10-26 15:49:56 -0700109class FailRenderer : public TestRendererBase {};
Chris Craik6fe991e52015-10-20 09:39:42 -0700110
Chris Craik3a5811b2016-03-22 15:03:08 -0700111RENDERTHREAD_TEST(FrameBuilder, simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800112 class SimpleTestRenderer : public TestRendererBase {
113 public:
Chris Craik98787e62015-11-13 10:55:30 -0800114 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800115 EXPECT_EQ(0, mIndex++);
116 EXPECT_EQ(100u, width);
117 EXPECT_EQ(200u, height);
118 }
119 void onRectOp(const RectOp& op, const BakedOpState& state) override {
120 EXPECT_EQ(1, mIndex++);
121 }
122 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
123 EXPECT_EQ(2, mIndex++);
124 }
Chris Craike4db79d2015-12-22 16:32:23 -0800125 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800126 EXPECT_EQ(3, mIndex++);
127 }
128 };
129
Stan Iliev06152cd2016-07-27 17:55:43 -0400130 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -0800131 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700132 SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
Chris Craikb565df12015-10-05 13:00:52 -0700133 canvas.drawRect(0, 0, 100, 200, SkPaint());
134 canvas.drawBitmap(bitmap, 10, 10, nullptr);
135 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700136 FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
137 sLightGeometry, Caches::getInstance());
138 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
139
Chris Craik5854b342015-10-26 15:49:56 -0700140 SimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800141 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700142 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
Chris Craik6fe991e52015-10-20 09:39:42 -0700143}
144
Chris Craik3a5811b2016-03-22 15:03:08 -0700145RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
Chris Craik386aa032015-12-07 17:08:25 -0800146 class SimpleStrokeTestRenderer : public TestRendererBase {
147 public:
148 void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
149 EXPECT_EQ(0, mIndex++);
150 // even though initial bounds are empty...
151 EXPECT_TRUE(op.unmappedBounds.isEmpty())
152 << "initial bounds should be empty, since they're unstroked";
153 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
154 << "final bounds should account for stroke";
155 }
156 };
157
Stan Iliev06152cd2016-07-27 17:55:43 -0400158 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200,
Chris Craik386aa032015-12-07 17:08:25 -0800159 [](RenderProperties& props, RecordingCanvas& canvas) {
160 SkPaint strokedPaint;
161 strokedPaint.setStrokeWidth(10);
162 canvas.drawPoint(50, 50, strokedPaint);
163 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700164 FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
165 sLightGeometry, Caches::getInstance());
166 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
167
Chris Craik386aa032015-12-07 17:08:25 -0800168 SimpleStrokeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800169 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik386aa032015-12-07 17:08:25 -0800170 EXPECT_EQ(1, renderer.getIndex());
171}
172
Chris Craik3a5811b2016-03-22 15:03:08 -0700173RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
Stan Iliev06152cd2016-07-27 17:55:43 -0400174 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -0800175 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500176 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700177 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
178 canvas.drawRect(0, 0, 400, 400, SkPaint());
179 canvas.restore();
180 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700181 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
182 sLightGeometry, Caches::getInstance());
183 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
Chris Craik6fe991e52015-10-20 09:39:42 -0700184
Chris Craik5854b342015-10-26 15:49:56 -0700185 FailRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800186 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb565df12015-10-05 13:00:52 -0700187}
188
Chris Craik3a5811b2016-03-22 15:03:08 -0700189RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
Chris Craika1717272015-11-19 13:02:43 -0800190 const int LOOPS = 5;
Chris Craikd3daa312015-11-06 10:59:56 -0800191 class SimpleBatchingTestRenderer : public TestRendererBase {
192 public:
193 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800194 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
Chris Craikd3daa312015-11-06 10:59:56 -0800195 }
196 void onRectOp(const RectOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800197 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
Chris Craikd3daa312015-11-06 10:59:56 -0800198 }
199 };
200
Stan Iliev06152cd2016-07-27 17:55:43 -0400201 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -0800202 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik15c3f192015-12-03 12:16:56 -0800203 SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
204 kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
Chris Craikb565df12015-10-05 13:00:52 -0700205
206 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
207 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
Florin Malitaeecff562015-12-21 10:43:01 -0500208 canvas.save(SaveFlags::MatrixClip);
Chris Craika1717272015-11-19 13:02:43 -0800209 for (int i = 0; i < LOOPS; i++) {
Chris Craikb565df12015-10-05 13:00:52 -0700210 canvas.translate(0, 10);
211 canvas.drawRect(0, 0, 10, 10, SkPaint());
212 canvas.drawBitmap(bitmap, 5, 0, nullptr);
213 }
214 canvas.restore();
215 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700216 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
217 sLightGeometry, Caches::getInstance());
218 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
Chris Craikb565df12015-10-05 13:00:52 -0700219
Chris Craik5854b342015-10-26 15:49:56 -0700220 SimpleBatchingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800221 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800222 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craik15c3f192015-12-03 12:16:56 -0800223 << "Expect number of ops = 2 * loop count";
Chris Craika1717272015-11-19 13:02:43 -0800224}
225
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700226RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
227 class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
228 public:
229 void onRectOp(const RectOp& op, const BakedOpState& state) override {
230 EXPECT_EQ(0, mIndex++);
231 EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
232 EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
233 state.computedState.clipSideFlags);
234 }
235 };
236
Stan Iliev06152cd2016-07-27 17:55:43 -0400237 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700238 [](RenderProperties& props, RecordingCanvas& canvas) {
239 canvas.drawRect(0, 0, 100, 100, SkPaint());
240 });
241
242 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
243 sLightGeometry, Caches::getInstance());
244 frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
245 *TestUtils::getSyncedNode(node));
246
247 DeferRenderNodeTranslateClipTestRenderer renderer;
248 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
249 EXPECT_EQ(1, renderer.getIndex());
250}
251
252RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
253 class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
254 public:
255 void onRectOp(const RectOp& op, const BakedOpState& state) override {
256 const Rect& clippedBounds = state.computedState.clippedBounds;
257 Matrix4 expected;
258 switch (mIndex++) {
259 case 0:
260 // background - left side
261 EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
262 expected.loadTranslate(100, 100, 0);
263 break;
264 case 1:
265 // background - top side
266 EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
267 expected.loadTranslate(100, 100, 0);
268 break;
269 case 2:
270 // content
271 EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
272 expected.loadTranslate(-50, -50, 0);
273 break;
274 case 3:
275 // overlay
276 EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
277 break;
278 default:
279 ADD_FAILURE() << "Too many rects observed";
280 }
281 EXPECT_EQ(expected, state.computedState.transform);
282 }
283 };
284
285 std::vector<sp<RenderNode>> nodes;
286 SkPaint transparentPaint;
287 transparentPaint.setAlpha(128);
288
289 // backdrop
Stan Iliev06152cd2016-07-27 17:55:43 -0400290 nodes.push_back(TestUtils::createNode<RecordingCanvas>(100, 100, 700, 500, // 600x400
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700291 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
292 canvas.drawRect(0, 0, 600, 400, transparentPaint);
293 }));
294
295 // content
296 Rect contentDrawBounds(150, 150, 650, 450); // 500x300
Stan Iliev06152cd2016-07-27 17:55:43 -0400297 nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600,
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700298 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
299 canvas.drawRect(0, 0, 800, 600, transparentPaint);
300 }));
301
302 // overlay
Stan Iliev06152cd2016-07-27 17:55:43 -0400303 nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600,
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700304 [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
305 canvas.drawRect(0, 0, 800, 200, transparentPaint);
306 }));
307
308 for (auto& node : nodes) {
309 TestUtils::syncHierarchyPropertiesAndDisplayList(node);
310 }
311
312 FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
313 sLightGeometry, Caches::getInstance());
314 frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
315
316 DeferRenderNodeSceneTestRenderer renderer;
317 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
318 EXPECT_EQ(4, renderer.getIndex());
319}
320
Chris Craik6246d2782016-03-29 15:01:41 -0700321RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
322 class EmptyNoFbo0TestRenderer : public TestRendererBase {
323 public:
324 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
325 ADD_FAILURE() << "Primary frame draw not expected in this test";
326 }
327 void endFrame(const Rect& repaintRect) override {
328 ADD_FAILURE() << "Primary frame draw not expected in this test";
329 }
330 };
331
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700332 // Use layer update constructor, so no work is enqueued for Fbo0
333 LayerUpdateQueue emptyLayerUpdateQueue;
334 FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
Chris Craik6246d2782016-03-29 15:01:41 -0700335 EmptyNoFbo0TestRenderer renderer;
336 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
337}
338
339RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
340 class EmptyWithFbo0TestRenderer : public TestRendererBase {
341 public:
342 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
343 EXPECT_EQ(0, mIndex++);
344 }
345 void endFrame(const Rect& repaintRect) override {
346 EXPECT_EQ(1, mIndex++);
347 }
348 };
Stan Iliev06152cd2016-07-27 17:55:43 -0400349 auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
Chris Craik6246d2782016-03-29 15:01:41 -0700350 [](RenderProperties& props, RecordingCanvas& canvas) {
351 // no drawn content
352 });
Chris Craik6246d2782016-03-29 15:01:41 -0700353
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700354 // Draw, but pass node without draw content, so no work is done for primary frame
355 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
356 sLightGeometry, Caches::getInstance());
357 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
358
Chris Craik6246d2782016-03-29 15:01:41 -0700359 EmptyWithFbo0TestRenderer renderer;
360 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
361 EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
362 " but fbo0 update lifecycle should still be observed";
363}
364
Chris Craik80d2ade2016-03-28 12:54:07 -0700365RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
366 class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
367 public:
368 void onRectOp(const RectOp& op, const BakedOpState& state) override {
369 EXPECT_EQ(mIndex++, 0) << "Should be one rect";
370 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
371 << "Last rect should occlude others.";
372 }
373 };
Stan Iliev06152cd2016-07-27 17:55:43 -0400374 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik80d2ade2016-03-28 12:54:07 -0700375 [](RenderProperties& props, RecordingCanvas& canvas) {
376 canvas.drawRect(0, 0, 200, 200, SkPaint());
377 canvas.drawRect(0, 0, 200, 200, SkPaint());
378 canvas.drawRect(10, 10, 190, 190, SkPaint());
379 });
380
381 // Damage (and therefore clip) is same as last draw, subset of renderable area.
382 // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700383 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200,
384 sLightGeometry, Caches::getInstance());
385 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
Chris Craik80d2ade2016-03-28 12:54:07 -0700386
387 EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
388 << "Recording must not have rejected ops, in order for this test to be valid";
389
390 AvoidOverdrawRectsTestRenderer renderer;
391 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
392 EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
393}
394
395RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
396 static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
397 SkColorType::kRGB_565_SkColorType);
398 static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
399 SkColorType::kAlpha_8_SkColorType);
400 class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
401 public:
402 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craik80d2ade2016-03-28 12:54:07 -0700403 switch(mIndex++) {
404 case 0:
405 EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
406 break;
407 case 1:
408 EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
409 break;
410 default:
411 ADD_FAILURE() << "Only two ops expected.";
412 }
413 }
414 };
415
Stan Iliev06152cd2016-07-27 17:55:43 -0400416 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 50, 50,
Chris Craik80d2ade2016-03-28 12:54:07 -0700417 [](RenderProperties& props, RecordingCanvas& canvas) {
418 canvas.drawRect(0, 0, 50, 50, SkPaint());
419 canvas.drawRect(0, 0, 50, 50, SkPaint());
420 canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
421
422 // only the below draws should remain, since they're
423 canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
424 canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
425 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700426 FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
427 sLightGeometry, Caches::getInstance());
428 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
Chris Craik80d2ade2016-03-28 12:54:07 -0700429
430 EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
431 << "Recording must not have rejected ops, in order for this test to be valid";
432
433 AvoidOverdrawBitmapsTestRenderer renderer;
434 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
sergeyva82ffc52016-04-04 17:12:04 -0700435 EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
Chris Craik80d2ade2016-03-28 12:54:07 -0700436}
437
Chris Craik3a5811b2016-03-22 15:03:08 -0700438RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
Chris Craik93e53e02015-12-17 18:42:44 -0800439 class ClippedMergingTestRenderer : public TestRendererBase {
440 public:
441 void onMergedBitmapOps(const MergedBakedOpList& opList) override {
442 EXPECT_EQ(0, mIndex);
443 mIndex += opList.count;
444 EXPECT_EQ(4u, opList.count);
445 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
446 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
447 opList.clipSideFlags);
448 }
449 };
Stan Iliev06152cd2016-07-27 17:55:43 -0400450 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
451 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik93e53e02015-12-17 18:42:44 -0800452 SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
453
454 // left side clipped (to inset left half)
455 canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
456 canvas.drawBitmap(bitmap, 0, 40, nullptr);
457
458 // top side clipped (to inset top half)
459 canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
460 canvas.drawBitmap(bitmap, 40, 0, nullptr);
461
462 // right side clipped (to inset right half)
463 canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
464 canvas.drawBitmap(bitmap, 80, 40, nullptr);
465
466 // bottom not clipped, just abutting (inset bottom half)
467 canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
468 canvas.drawBitmap(bitmap, 40, 70, nullptr);
469 });
470
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700471 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
472 sLightGeometry, Caches::getInstance());
473 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
474
Chris Craik93e53e02015-12-17 18:42:44 -0800475 ClippedMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800476 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik93e53e02015-12-17 18:42:44 -0800477 EXPECT_EQ(4, renderer.getIndex());
478}
479
Chris Craikf8f56cb2016-10-14 15:08:21 -0700480RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
481 class RegionClipStopsMergeTestRenderer : public TestRendererBase {
482 public:
483 void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
484 };
485 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
486 [](RenderProperties& props, RecordingCanvas& canvas) {
487 SkPath path;
488 path.addCircle(200, 200, 200, SkPath::kCW_Direction);
489 canvas.save(SaveFlags::MatrixClip);
490 canvas.clipPath(&path, SkRegion::kIntersect_Op);
491 SkPaint paint;
492 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
493 paint.setAntiAlias(true);
494 paint.setTextSize(50);
495 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
496 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200);
497 canvas.restore();
498 });
499
500 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
501 sLightGeometry, Caches::getInstance());
502 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
503
504 RegionClipStopsMergeTestRenderer renderer;
505 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
506 EXPECT_EQ(2, renderer.getIndex());
507}
508
Chris Craik3a5811b2016-03-22 15:03:08 -0700509RENDERTHREAD_TEST(FrameBuilder, textMerging) {
Chris Craikd7448e62015-12-15 10:34:36 -0800510 class TextMergingTestRenderer : public TestRendererBase {
511 public:
512 void onMergedTextOps(const MergedBakedOpList& opList) override {
513 EXPECT_EQ(0, mIndex);
514 mIndex += opList.count;
515 EXPECT_EQ(2u, opList.count);
516 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
517 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
518 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
519 }
520 };
Stan Iliev06152cd2016-07-27 17:55:43 -0400521 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
522 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikd7448e62015-12-15 10:34:36 -0800523 SkPaint paint;
524 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
525 paint.setAntiAlias(true);
526 paint.setTextSize(50);
sergeyvdccca442016-03-21 15:38:21 -0700527 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
528 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
Chris Craikd7448e62015-12-15 10:34:36 -0800529 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700530 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
531 sLightGeometry, Caches::getInstance());
532 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
533
Chris Craikd7448e62015-12-15 10:34:36 -0800534 TextMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800535 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd7448e62015-12-15 10:34:36 -0800536 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
537}
538
Chris Craik3a5811b2016-03-22 15:03:08 -0700539RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
Chris Craika1717272015-11-19 13:02:43 -0800540 const int LOOPS = 5;
541 class TextStrikethroughTestRenderer : public TestRendererBase {
542 public:
543 void onRectOp(const RectOp& op, const BakedOpState& state) override {
544 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
545 }
Chris Craik15c3f192015-12-03 12:16:56 -0800546 void onMergedTextOps(const MergedBakedOpList& opList) override {
547 EXPECT_EQ(0, mIndex);
548 mIndex += opList.count;
549 EXPECT_EQ(5u, opList.count);
Chris Craika1717272015-11-19 13:02:43 -0800550 }
551 };
Stan Iliev06152cd2016-07-27 17:55:43 -0400552 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 2000,
Chris Craik8d1f2122015-11-24 16:40:09 -0800553 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craika1717272015-11-19 13:02:43 -0800554 SkPaint textPaint;
555 textPaint.setAntiAlias(true);
556 textPaint.setTextSize(20);
557 textPaint.setStrikeThruText(true);
Chris Craik42a54072015-11-24 11:41:54 -0800558 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Chris Craika1717272015-11-19 13:02:43 -0800559 for (int i = 0; i < LOOPS; i++) {
sergeyvdccca442016-03-21 15:38:21 -0700560 TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
Chris Craika1717272015-11-19 13:02:43 -0800561 }
562 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700563
564 FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000,
565 sLightGeometry, Caches::getInstance());
566 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
567
Chris Craika1717272015-11-19 13:02:43 -0800568 TextStrikethroughTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800569 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800570 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craikd7448e62015-12-15 10:34:36 -0800571 << "Expect number of ops = 2 * loop count";
Chris Craikb565df12015-10-05 13:00:52 -0700572}
573
Chris Craik7c02cab2016-03-16 17:15:12 -0700574static auto styles = {
575 SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
576
Chris Craik3a5811b2016-03-22 15:03:08 -0700577RENDERTHREAD_TEST(FrameBuilder, textStyle) {
Chris Craik7c02cab2016-03-16 17:15:12 -0700578 class TextStyleTestRenderer : public TestRendererBase {
579 public:
580 void onMergedTextOps(const MergedBakedOpList& opList) override {
581 ASSERT_EQ(0, mIndex);
582 ASSERT_EQ(3u, opList.count);
583 mIndex += opList.count;
584
585 int index = 0;
586 for (auto style : styles) {
587 auto state = opList.states[index++];
588 ASSERT_EQ(style, state->op->paint->getStyle())
589 << "Remainder of validation relies upon stable merged order";
590 ASSERT_EQ(0, state->computedState.clipSideFlags)
591 << "Clipped bounds validation requires unclipped ops";
592 }
593
594 Rect fill = opList.states[0]->computedState.clippedBounds;
595 Rect stroke = opList.states[1]->computedState.clippedBounds;
596 EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
597 << "Stroke+Fill should be same as stroke";
598
599 EXPECT_TRUE(stroke.contains(fill));
600 EXPECT_FALSE(fill.contains(stroke));
601
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400602 // outset by half the stroke width
Chris Craik7c02cab2016-03-16 17:15:12 -0700603 Rect outsetFill(fill);
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400604 outsetFill.outset(5);
Chris Craik7c02cab2016-03-16 17:15:12 -0700605 EXPECT_EQ(stroke, outsetFill);
606 }
607 };
Stan Iliev06152cd2016-07-27 17:55:43 -0400608 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
609 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik7c02cab2016-03-16 17:15:12 -0700610 SkPaint paint;
611 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
612 paint.setAntiAlias(true);
613 paint.setTextSize(50);
614 paint.setStrokeWidth(10);
615
616 // draw 3 copies of the same text overlapping, each with a different style.
617 // They'll get merged, but with
618 for (auto style : styles) {
619 paint.setStyle(style);
sergeyvdccca442016-03-21 15:38:21 -0700620 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
Chris Craik7c02cab2016-03-16 17:15:12 -0700621 }
622 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700623 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
624 sLightGeometry, Caches::getInstance());
625 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
Chris Craik7c02cab2016-03-16 17:15:12 -0700626 TextStyleTestRenderer renderer;
627 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
628 EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
629}
630
Chris Craikaafb01d2016-03-25 18:34:11 -0700631RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
632 class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800633 public:
634 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
635 EXPECT_EQ(0, mIndex++);
Chris Craike4db79d2015-12-22 16:32:23 -0800636 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800637 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
638
639 Matrix4 expected;
640 expected.loadTranslate(5, 5, 0);
641 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
642 }
643 };
644
645 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
Chris Craik243e85b2016-03-25 15:26:11 -0700646 SkMatrix::MakeTrans(5, 5));
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800647
Stan Iliev06152cd2016-07-27 17:55:43 -0400648 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800649 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500650 canvas.save(SaveFlags::MatrixClip);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800651 canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
652 canvas.drawLayer(layerUpdater.get());
653 canvas.restore();
654 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700655
656 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
657 sLightGeometry, Caches::getInstance());
658 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
659
Chris Craikaafb01d2016-03-25 18:34:11 -0700660 TextureLayerClipLocalMatrixTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800661 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800662 EXPECT_EQ(1, renderer.getIndex());
663}
664
Chris Craikaafb01d2016-03-25 18:34:11 -0700665RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
666 class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
667 public:
668 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
669 EXPECT_EQ(0, mIndex++);
670
671 Matrix4 expected;
672 expected.loadTranslate(35, 45, 0);
673 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
674 }
675 };
676
677 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
678 SkMatrix::MakeTrans(5, 5));
679
Stan Iliev06152cd2016-07-27 17:55:43 -0400680 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craikaafb01d2016-03-25 18:34:11 -0700681 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
682 canvas.save(SaveFlags::MatrixClip);
683 canvas.translate(30, 40);
684 canvas.drawLayer(layerUpdater.get());
685 canvas.restore();
686 });
687
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700688 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
689 sLightGeometry, Caches::getInstance());
690 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
691
Chris Craikaafb01d2016-03-25 18:34:11 -0700692 TextureLayerCombineMatricesTestRenderer renderer;
693 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
694 EXPECT_EQ(1, renderer.getIndex());
695}
696
697RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
698 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
699 SkMatrix::MakeTrans(5, 5));
700 layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
701
Stan Iliev06152cd2016-07-27 17:55:43 -0400702 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craikaafb01d2016-03-25 18:34:11 -0700703 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
704 canvas.drawLayer(layerUpdater.get());
705 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700706
707 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
708 sLightGeometry, Caches::getInstance());
709 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
710
Chris Craikaafb01d2016-03-25 18:34:11 -0700711 FailRenderer renderer;
712 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
713}
714
Chris Craik3a5811b2016-03-22 15:03:08 -0700715RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
Chris Craik223e3b62016-03-10 10:27:38 -0800716 class FunctorTestRenderer : public TestRendererBase {
717 public:
718 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
719 EXPECT_EQ(0, mIndex++);
720 }
721 };
722 Functor noopFunctor;
723
724 // 1 million pixel tall view, scrolled down 80%
Stan Iliev06152cd2016-07-27 17:55:43 -0400725 auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 1000000,
Chris Craik223e3b62016-03-10 10:27:38 -0800726 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
727 canvas.translate(0, -800000);
John Reckcd1c3eb2016-04-14 10:38:54 -0700728 canvas.callDrawGLFunction(&noopFunctor, nullptr);
Chris Craik223e3b62016-03-10 10:27:38 -0800729 });
730
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700731 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700732 sLightGeometry, Caches::getInstance());
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700733 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
734
Chris Craik223e3b62016-03-10 10:27:38 -0800735 FunctorTestRenderer renderer;
736 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
737 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
738}
739
Chris Craika2048482016-03-25 14:17:49 -0700740RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
741 class ColorTestRenderer : public TestRendererBase {
742 public:
743 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
744 EXPECT_EQ(0, mIndex++);
745 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
746 << "Color op should be expanded to bounds of surrounding";
747 }
748 };
749
Stan Iliev06152cd2016-07-27 17:55:43 -0400750 auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(0, 0, 10, 10,
Chris Craika2048482016-03-25 14:17:49 -0700751 [](RenderProperties& props, RecordingCanvas& canvas) {
752 props.setClipToBounds(false);
753 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
754 });
755
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700756 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
Chris Craika2048482016-03-25 14:17:49 -0700757 sLightGeometry, Caches::getInstance());
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700758 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
759
Chris Craika2048482016-03-25 14:17:49 -0700760 ColorTestRenderer renderer;
761 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
762 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
763}
764
765TEST(FrameBuilder, renderNode) {
Chris Craikd3daa312015-11-06 10:59:56 -0800766 class RenderNodeTestRenderer : public TestRendererBase {
767 public:
768 void onRectOp(const RectOp& op, const BakedOpState& state) override {
769 switch(mIndex++) {
770 case 0:
Chris Craik5430ab22015-12-10 16:28:16 -0800771 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
Chris Craikd3daa312015-11-06 10:59:56 -0800772 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
773 break;
774 case 1:
775 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
776 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
777 break;
778 default:
779 ADD_FAILURE();
780 }
781 }
782 };
783
Stan Iliev06152cd2016-07-27 17:55:43 -0400784 auto child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
Chris Craik8d1f2122015-11-24 16:40:09 -0800785 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700786 SkPaint paint;
787 paint.setColor(SK_ColorWHITE);
788 canvas.drawRect(0, 0, 100, 100, paint);
789 });
790
Stan Iliev06152cd2016-07-27 17:55:43 -0400791 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800792 [&child](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700793 SkPaint paint;
794 paint.setColor(SK_ColorDKGRAY);
795 canvas.drawRect(0, 0, 200, 200, paint);
Chris Craikb565df12015-10-05 13:00:52 -0700796
Florin Malitaeecff562015-12-21 10:43:01 -0500797 canvas.save(SaveFlags::MatrixClip);
Chris Craikddf22152015-10-14 17:42:47 -0700798 canvas.translate(40, 40);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800799 canvas.drawRenderNode(child.get());
Chris Craikddf22152015-10-14 17:42:47 -0700800 canvas.restore();
Chris Craikb565df12015-10-05 13:00:52 -0700801 });
802
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700803 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
804 sLightGeometry, Caches::getInstance());
805 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
806
Chris Craik5854b342015-10-26 15:49:56 -0700807 RenderNodeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800808 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik223e3b62016-03-10 10:27:38 -0800809 EXPECT_EQ(2, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700810}
811
Chris Craik3a5811b2016-03-22 15:03:08 -0700812RENDERTHREAD_TEST(FrameBuilder, clipped) {
Chris Craikd3daa312015-11-06 10:59:56 -0800813 class ClippedTestRenderer : public TestRendererBase {
814 public:
815 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
816 EXPECT_EQ(0, mIndex++);
817 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800818 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800819 EXPECT_TRUE(state.computedState.transform.isIdentity());
820 }
821 };
822
Stan Iliev06152cd2016-07-27 17:55:43 -0400823 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -0800824 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700825 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
826 canvas.drawBitmap(bitmap, 0, 0, nullptr);
827 });
Chris Craikddf22152015-10-14 17:42:47 -0700828
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700829 // clip to small area, should see in receiver
830 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200,
831 sLightGeometry, Caches::getInstance());
832 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
833
Chris Craik5854b342015-10-26 15:49:56 -0700834 ClippedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800835 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikddf22152015-10-14 17:42:47 -0700836}
837
Chris Craik3a5811b2016-03-22 15:03:08 -0700838RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800839 class SaveLayerSimpleTestRenderer : public TestRendererBase {
840 public:
841 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
842 EXPECT_EQ(0, mIndex++);
843 EXPECT_EQ(180u, width);
844 EXPECT_EQ(180u, height);
845 return nullptr;
846 }
847 void endLayer() override {
848 EXPECT_EQ(2, mIndex++);
849 }
850 void onRectOp(const RectOp& op, const BakedOpState& state) override {
851 EXPECT_EQ(1, mIndex++);
852 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
Chris Craik5430ab22015-12-10 16:28:16 -0800853 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800854 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800855
856 Matrix4 expectedTransform;
857 expectedTransform.loadTranslate(-10, -10, 0);
858 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
859 }
860 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
861 EXPECT_EQ(3, mIndex++);
862 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800863 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800864 EXPECT_TRUE(state.computedState.transform.isIdentity());
865 }
Chris Craik74af6e22016-04-05 13:18:56 -0700866 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
867 EXPECT_EQ(4, mIndex++);
868 EXPECT_EQ(nullptr, offscreenBuffer);
869 }
Chris Craikd3daa312015-11-06 10:59:56 -0800870 };
871
Stan Iliev06152cd2016-07-27 17:55:43 -0400872 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -0800873 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500874 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700875 canvas.drawRect(10, 10, 190, 190, SkPaint());
876 canvas.restore();
877 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700878
879 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
880 sLightGeometry, Caches::getInstance());
881 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
882
Chris Craik5854b342015-10-26 15:49:56 -0700883 SaveLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800884 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -0700885 EXPECT_EQ(5, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700886}
Chris Craik6fe991e52015-10-20 09:39:42 -0700887
Chris Craik3a5811b2016-03-22 15:03:08 -0700888RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
Chris Craikd3daa312015-11-06 10:59:56 -0800889 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
890 * - startTemporaryLayer2, rect2 endLayer2
891 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
892 * - startFrame, layerOp1, endFrame
893 */
894 class SaveLayerNestedTestRenderer : public TestRendererBase {
895 public:
896 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
897 const int index = mIndex++;
898 if (index == 0) {
899 EXPECT_EQ(400u, width);
900 EXPECT_EQ(400u, height);
901 return (OffscreenBuffer*) 0x400;
902 } else if (index == 3) {
903 EXPECT_EQ(800u, width);
904 EXPECT_EQ(800u, height);
905 return (OffscreenBuffer*) 0x800;
906 } else { ADD_FAILURE(); }
907 return (OffscreenBuffer*) nullptr;
908 }
909 void endLayer() override {
910 int index = mIndex++;
911 EXPECT_TRUE(index == 2 || index == 6);
912 }
Chris Craik98787e62015-11-13 10:55:30 -0800913 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800914 EXPECT_EQ(7, mIndex++);
915 }
Chris Craike4db79d2015-12-22 16:32:23 -0800916 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800917 EXPECT_EQ(9, mIndex++);
918 }
919 void onRectOp(const RectOp& op, const BakedOpState& state) override {
920 const int index = mIndex++;
921 if (index == 1) {
Chris Craik5430ab22015-12-10 16:28:16 -0800922 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
Chris Craikd3daa312015-11-06 10:59:56 -0800923 } else if (index == 4) {
Chris Craik5430ab22015-12-10 16:28:16 -0800924 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
Chris Craikd3daa312015-11-06 10:59:56 -0800925 } else { ADD_FAILURE(); }
926 }
927 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
928 const int index = mIndex++;
929 if (index == 5) {
930 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800931 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
Chris Craikd3daa312015-11-06 10:59:56 -0800932 } else if (index == 8) {
933 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800934 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
Chris Craikd3daa312015-11-06 10:59:56 -0800935 } else { ADD_FAILURE(); }
936 }
Chris Craik74af6e22016-04-05 13:18:56 -0700937 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
938 const int index = mIndex++;
939 // order isn't important, but we need to see both
940 if (index == 10) {
941 EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
942 } else if (index == 11) {
943 EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
944 } else { ADD_FAILURE(); }
945 }
Chris Craikd3daa312015-11-06 10:59:56 -0800946 };
947
Stan Iliev06152cd2016-07-27 17:55:43 -0400948 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 800, 800,
Chris Craik8d1f2122015-11-24 16:40:09 -0800949 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500950 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700951 {
952 canvas.drawRect(0, 0, 800, 800, SkPaint());
Florin Malitaeecff562015-12-21 10:43:01 -0500953 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700954 {
955 canvas.drawRect(0, 0, 400, 400, SkPaint());
956 }
957 canvas.restore();
958 }
959 canvas.restore();
960 });
961
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700962 FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800,
963 sLightGeometry, Caches::getInstance());
964 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
965
Chris Craik5854b342015-10-26 15:49:56 -0700966 SaveLayerNestedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800967 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -0700968 EXPECT_EQ(12, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700969}
Chris Craik6fe991e52015-10-20 09:39:42 -0700970
Chris Craik3a5811b2016-03-22 15:03:08 -0700971RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
Stan Iliev06152cd2016-07-27 17:55:43 -0400972 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -0800973 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500974 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700975 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
Florin Malitaeecff562015-12-21 10:43:01 -0500976 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700977
978 // draw within save layer may still be recorded, but shouldn't be drawn
979 canvas.drawRect(200, 200, 400, 400, SkPaint());
980
981 canvas.restore();
982 canvas.restore();
983 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -0700984
985 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
986 sLightGeometry, Caches::getInstance());
987 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
Chris Craik6fe991e52015-10-20 09:39:42 -0700988
Chris Craik5854b342015-10-26 15:49:56 -0700989 FailRenderer renderer;
Chris Craik6fe991e52015-10-20 09:39:42 -0700990 // should see no ops, even within the layer, since the layer should be rejected
Chris Craikf158b492016-01-12 14:45:08 -0800991 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700992}
993
Chris Craik3a5811b2016-03-22 15:03:08 -0700994RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800995 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
996 public:
997 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
998 EXPECT_EQ(0, mIndex++);
999 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -08001000 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -08001001 EXPECT_TRUE(state.computedState.transform.isIdentity());
1002 }
1003 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1004 EXPECT_EQ(1, mIndex++);
1005 ASSERT_NE(nullptr, op.paint);
1006 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
1007 }
1008 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1009 EXPECT_EQ(2, mIndex++);
1010 EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
1011 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
1012 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
1013 EXPECT_TRUE(state.computedState.transform.isIdentity());
1014 }
1015 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1016 EXPECT_EQ(3, mIndex++);
1017 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -08001018 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -08001019 EXPECT_TRUE(state.computedState.transform.isIdentity());
1020 }
1021 };
1022
Stan Iliev06152cd2016-07-27 17:55:43 -04001023 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craikb87eadd2016-01-06 09:16:05 -08001024 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -05001025 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
Chris Craikb87eadd2016-01-06 09:16:05 -08001026 canvas.drawRect(0, 0, 200, 200, SkPaint());
1027 canvas.restore();
1028 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001029
1030 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1031 sLightGeometry, Caches::getInstance());
1032 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1033
Chris Craikb87eadd2016-01-06 09:16:05 -08001034 SaveLayerUnclippedSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001035 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -08001036 EXPECT_EQ(4, renderer.getIndex());
1037}
1038
Chris Craikd5a90112016-06-24 13:53:37 -07001039RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
1040 class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
1041 public:
1042 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1043 EXPECT_EQ(0, mIndex++);
1044 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1045 << "Bounds rect should round out";
1046 }
1047 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {}
1048 void onRectOp(const RectOp& op, const BakedOpState& state) override {}
1049 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1050 EXPECT_EQ(1, mIndex++);
1051 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
1052 << "Bounds rect should round out";
1053 }
1054 };
1055
Stan Iliev06152cd2016-07-27 17:55:43 -04001056 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craikd5a90112016-06-24 13:53:37 -07001057 [](RenderProperties& props, RecordingCanvas& canvas) {
1058 canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
1059 128, (SaveFlags::Flags)(0));
1060 canvas.drawRect(0, 0, 200, 200, SkPaint());
1061 canvas.restore();
1062 });
1063
1064 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1065 sLightGeometry, Caches::getInstance());
1066 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1067
1068 SaveLayerUnclippedRoundTestRenderer renderer;
1069 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1070 EXPECT_EQ(2, renderer.getIndex());
1071}
1072
Chris Craik3a5811b2016-03-22 15:03:08 -07001073RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
Chris Craikb87eadd2016-01-06 09:16:05 -08001074 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
1075 public:
1076 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1077 int index = mIndex++;
1078 EXPECT_GT(4, index);
1079 EXPECT_EQ(5, op.unmappedBounds.getWidth());
1080 EXPECT_EQ(5, op.unmappedBounds.getHeight());
1081 if (index == 0) {
1082 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
1083 } else if (index == 1) {
1084 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
1085 } else if (index == 2) {
1086 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
1087 } else if (index == 3) {
1088 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
1089 }
1090 }
1091 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1092 EXPECT_EQ(4, mIndex++);
1093 ASSERT_EQ(op.vertexCount, 16u);
1094 for (size_t i = 0; i < op.vertexCount; i++) {
1095 auto v = op.vertices[i];
1096 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
1097 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
1098 }
1099 }
1100 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1101 EXPECT_EQ(5, mIndex++);
1102 }
1103 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1104 EXPECT_LT(5, mIndex++);
1105 }
1106 };
1107
Stan Iliev06152cd2016-07-27 17:55:43 -04001108 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craikb87eadd2016-01-06 09:16:05 -08001109 [](RenderProperties& props, RecordingCanvas& canvas) {
1110
Florin Malitaeecff562015-12-21 10:43:01 -05001111 int restoreTo = canvas.save(SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -08001112 canvas.scale(2, 2);
Florin Malitaeecff562015-12-21 10:43:01 -05001113 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
1114 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
1115 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
1116 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -08001117 canvas.drawRect(0, 0, 100, 100, SkPaint());
1118 canvas.restoreToCount(restoreTo);
1119 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001120
1121 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1122 sLightGeometry, Caches::getInstance());
1123 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1124
Chris Craikb87eadd2016-01-06 09:16:05 -08001125 SaveLayerUnclippedMergedClearsTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001126 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -08001127 EXPECT_EQ(10, renderer.getIndex())
1128 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
1129}
1130
Chris Craik3a5811b2016-03-22 15:03:08 -07001131RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
Chris Craik4876de12016-02-25 16:54:08 -08001132 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
1133 public:
1134 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1135 EXPECT_EQ(0, mIndex++);
1136 }
1137 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1138 EXPECT_EQ(1, mIndex++);
1139 ASSERT_NE(nullptr, op.paint);
1140 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
1141 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
1142 << "Expect dirty rect as clip";
1143 ASSERT_NE(nullptr, state.computedState.clipState);
1144 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
1145 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1146 }
1147 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1148 EXPECT_EQ(2, mIndex++);
1149 }
1150 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1151 EXPECT_EQ(3, mIndex++);
1152 }
1153 };
1154
Stan Iliev06152cd2016-07-27 17:55:43 -04001155 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik4876de12016-02-25 16:54:08 -08001156 [](RenderProperties& props, RecordingCanvas& canvas) {
1157 // save smaller than clip, so we get unclipped behavior
1158 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
1159 canvas.drawRect(0, 0, 200, 200, SkPaint());
1160 canvas.restore();
1161 });
1162
1163 // draw with partial screen dirty, and assert we see that rect later
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001164 FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
1165 sLightGeometry, Caches::getInstance());
1166 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1167
Chris Craik4876de12016-02-25 16:54:08 -08001168 SaveLayerUnclippedClearClipTestRenderer renderer;
1169 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1170 EXPECT_EQ(4, renderer.getIndex());
1171}
1172
Chris Craik3a5811b2016-03-22 15:03:08 -07001173RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
Stan Iliev06152cd2016-07-27 17:55:43 -04001174 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik4876de12016-02-25 16:54:08 -08001175 [](RenderProperties& props, RecordingCanvas& canvas) {
1176 // unclipped savelayer + rect both in area that won't intersect with dirty
1177 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
1178 canvas.drawRect(100, 100, 200, 200, SkPaint());
1179 canvas.restore();
1180 });
1181
1182 // draw with partial screen dirty that doesn't intersect with savelayer
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001183 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
1184 sLightGeometry, Caches::getInstance());
1185 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1186
Chris Craik4876de12016-02-25 16:54:08 -08001187 FailRenderer renderer;
1188 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1189}
1190
Chris Craikb87eadd2016-01-06 09:16:05 -08001191/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
1192 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
1193 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
1194 */
Chris Craik3a5811b2016-03-22 15:03:08 -07001195RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
Chris Craikb87eadd2016-01-06 09:16:05 -08001196 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
1197 public:
1198 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1199 EXPECT_EQ(0, mIndex++); // savelayer first
1200 return (OffscreenBuffer*)0xabcd;
1201 }
1202 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1203 int index = mIndex++;
1204 EXPECT_TRUE(index == 1 || index == 7);
1205 }
1206 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1207 int index = mIndex++;
1208 EXPECT_TRUE(index == 2 || index == 8);
1209 }
1210 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1211 EXPECT_EQ(3, mIndex++);
1212 Matrix4 expected;
1213 expected.loadTranslate(-100, -100, 0);
1214 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
1215 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
1216 }
1217 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1218 int index = mIndex++;
1219 EXPECT_TRUE(index == 4 || index == 10);
1220 }
1221 void endLayer() override {
1222 EXPECT_EQ(5, mIndex++);
1223 }
1224 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1225 EXPECT_EQ(6, mIndex++);
1226 }
1227 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1228 EXPECT_EQ(9, mIndex++);
Chris Craik74af6e22016-04-05 13:18:56 -07001229 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
Chris Craikb87eadd2016-01-06 09:16:05 -08001230 }
1231 void endFrame(const Rect& repaintRect) override {
1232 EXPECT_EQ(11, mIndex++);
1233 }
Chris Craik74af6e22016-04-05 13:18:56 -07001234 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1235 EXPECT_EQ(12, mIndex++);
1236 EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
1237 }
Chris Craikb87eadd2016-01-06 09:16:05 -08001238 };
1239
Stan Iliev06152cd2016-07-27 17:55:43 -04001240 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 600, 600, // 500x500 triggers clipping
Chris Craikb87eadd2016-01-06 09:16:05 -08001241 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -05001242 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
1243 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
1244 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
Chris Craikb87eadd2016-01-06 09:16:05 -08001245 canvas.drawRect(200, 200, 300, 300, SkPaint());
1246 canvas.restore();
1247 canvas.restore();
1248 canvas.restore();
1249 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001250
1251 FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600,
1252 sLightGeometry, Caches::getInstance());
1253 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
1254
Chris Craikb87eadd2016-01-06 09:16:05 -08001255 SaveLayerUnclippedComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001256 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -07001257 EXPECT_EQ(13, renderer.getIndex());
Chris Craikb87eadd2016-01-06 09:16:05 -08001258}
1259
Chris Craikf158b492016-01-12 14:45:08 -08001260RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -08001261 class HwLayerSimpleTestRenderer : public TestRendererBase {
1262 public:
Chris Craik98787e62015-11-13 10:55:30 -08001263 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001264 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001265 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1266 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1267 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
Chris Craikd3daa312015-11-06 10:59:56 -08001268 }
1269 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1270 EXPECT_EQ(1, mIndex++);
1271
1272 EXPECT_TRUE(state.computedState.transform.isIdentity())
1273 << "Transform should be reset within layer";
1274
Chris Craike4db79d2015-12-22 16:32:23 -08001275 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
Chris Craikd3daa312015-11-06 10:59:56 -08001276 << "Damage rect should be used to clip layer content";
1277 }
1278 void endLayer() override {
1279 EXPECT_EQ(2, mIndex++);
1280 }
Chris Craik98787e62015-11-13 10:55:30 -08001281 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001282 EXPECT_EQ(3, mIndex++);
1283 }
1284 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1285 EXPECT_EQ(4, mIndex++);
1286 }
Chris Craike4db79d2015-12-22 16:32:23 -08001287 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001288 EXPECT_EQ(5, mIndex++);
1289 }
1290 };
1291
Stan Iliev06152cd2016-07-27 17:55:43 -04001292 auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
John Reck16c9d6a2015-11-17 15:51:08 -08001293 [](RenderProperties& props, RecordingCanvas& canvas) {
1294 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001295 SkPaint paint;
1296 paint.setColor(SK_ColorWHITE);
1297 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001298 });
Chris Craik98787e62015-11-13 10:55:30 -08001299 OffscreenBuffer** layerHandle = node->getLayerHandle();
Chris Craik0b7e8242015-10-28 16:50:44 -07001300
Chris Craik98787e62015-11-13 10:55:30 -08001301 // create RenderNode's layer here in same way prepareTree would
1302 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1303 *layerHandle = &layer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001304
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001305 auto syncedNode = TestUtils::getSyncedNode(node);
Chris Craik0b7e8242015-10-28 16:50:44 -07001306
1307 // only enqueue partial damage
Chris Craik98787e62015-11-13 10:55:30 -08001308 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001309 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1310
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001311 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1312 sLightGeometry, Caches::getInstance());
1313 frameBuilder.deferLayers(layerUpdateQueue);
1314 frameBuilder.deferRenderNode(*syncedNode);
1315
Chris Craik0b7e8242015-10-28 16:50:44 -07001316 HwLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001317 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001318 EXPECT_EQ(6, renderer.getIndex());
1319
1320 // clean up layer pointer, so we can safely destruct RenderNode
Chris Craik98787e62015-11-13 10:55:30 -08001321 *layerHandle = nullptr;
Chris Craik0b7e8242015-10-28 16:50:44 -07001322}
1323
Chris Craikf158b492016-01-12 14:45:08 -08001324RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
Chris Craikd3daa312015-11-06 10:59:56 -08001325 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
1326 * - startRepaintLayer(child), rect(grey), endLayer
1327 * - startTemporaryLayer, drawLayer(child), endLayer
1328 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
1329 * - startFrame, drawLayer(parent), endLayerb
1330 */
1331 class HwLayerComplexTestRenderer : public TestRendererBase {
1332 public:
1333 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1334 EXPECT_EQ(3, mIndex++); // savelayer first
1335 return (OffscreenBuffer*)0xabcd;
1336 }
Chris Craik98787e62015-11-13 10:55:30 -08001337 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001338 int index = mIndex++;
1339 if (index == 0) {
1340 // starting inner layer
Chris Craik98787e62015-11-13 10:55:30 -08001341 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1342 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001343 } else if (index == 6) {
1344 // starting outer layer
Chris Craik98787e62015-11-13 10:55:30 -08001345 EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
1346 EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001347 } else { ADD_FAILURE(); }
1348 }
1349 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1350 int index = mIndex++;
1351 if (index == 1) {
1352 // inner layer's rect (white)
1353 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1354 } else if (index == 7) {
1355 // outer layer's rect (grey)
1356 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1357 } else { ADD_FAILURE(); }
1358 }
1359 void endLayer() override {
1360 int index = mIndex++;
1361 EXPECT_TRUE(index == 2 || index == 5 || index == 9);
1362 }
Chris Craik98787e62015-11-13 10:55:30 -08001363 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001364 EXPECT_EQ(10, mIndex++);
1365 }
1366 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
Chris Craik98787e62015-11-13 10:55:30 -08001367 OffscreenBuffer* layer = *op.layerHandle;
Chris Craikd3daa312015-11-06 10:59:56 -08001368 int index = mIndex++;
1369 if (index == 4) {
Chris Craik98787e62015-11-13 10:55:30 -08001370 EXPECT_EQ(100u, layer->viewportWidth);
1371 EXPECT_EQ(100u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001372 } else if (index == 8) {
1373 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1374 } else if (index == 11) {
Chris Craik98787e62015-11-13 10:55:30 -08001375 EXPECT_EQ(200u, layer->viewportWidth);
1376 EXPECT_EQ(200u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001377 } else { ADD_FAILURE(); }
1378 }
Chris Craike4db79d2015-12-22 16:32:23 -08001379 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001380 EXPECT_EQ(12, mIndex++);
1381 }
Chris Craik74af6e22016-04-05 13:18:56 -07001382 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1383 EXPECT_EQ(13, mIndex++);
1384 }
Chris Craikd3daa312015-11-06 10:59:56 -08001385 };
1386
Stan Iliev06152cd2016-07-27 17:55:43 -04001387 auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150,
John Reck16c9d6a2015-11-17 15:51:08 -08001388 [](RenderProperties& props, RecordingCanvas& canvas) {
1389 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001390 SkPaint paint;
1391 paint.setColor(SK_ColorWHITE);
1392 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001393 });
Chris Craik98787e62015-11-13 10:55:30 -08001394 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1395 *(child->getLayerHandle()) = &childLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001396
1397 RenderNode* childPtr = child.get();
Stan Iliev06152cd2016-07-27 17:55:43 -04001398 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
John Reck16c9d6a2015-11-17 15:51:08 -08001399 [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1400 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001401 SkPaint paint;
1402 paint.setColor(SK_ColorDKGRAY);
1403 canvas.drawRect(0, 0, 200, 200, paint);
1404
Florin Malitaeecff562015-12-21 10:43:01 -05001405 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001406 canvas.drawRenderNode(childPtr);
1407 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001408 });
Chris Craik98787e62015-11-13 10:55:30 -08001409 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1410 *(parent->getLayerHandle()) = &parentLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001411
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001412 auto syncedNode = TestUtils::getSyncedNode(parent);
Chris Craik0b7e8242015-10-28 16:50:44 -07001413
Chris Craik98787e62015-11-13 10:55:30 -08001414 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001415 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1416 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1417
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001418 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1419 sLightGeometry, Caches::getInstance());
1420 frameBuilder.deferLayers(layerUpdateQueue);
1421 frameBuilder.deferRenderNode(*syncedNode);
1422
Chris Craik0b7e8242015-10-28 16:50:44 -07001423 HwLayerComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001424 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -07001425 EXPECT_EQ(14, renderer.getIndex());
Chris Craik0b7e8242015-10-28 16:50:44 -07001426
1427 // clean up layer pointers, so we can safely destruct RenderNodes
1428 *(child->getLayerHandle()) = nullptr;
1429 *(parent->getLayerHandle()) = nullptr;
1430}
1431
Chris Craik6246d2782016-03-29 15:01:41 -07001432
1433RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
1434 class BuildLayerTestRenderer : public TestRendererBase {
1435 public:
1436 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1437 EXPECT_EQ(0, mIndex++);
1438 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1439 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1440 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1441 }
1442 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
1443 EXPECT_EQ(1, mIndex++);
1444
1445 EXPECT_TRUE(state.computedState.transform.isIdentity())
1446 << "Transform should be reset within layer";
1447
1448 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1449 << "Damage rect should be used to clip layer content";
1450 }
1451 void endLayer() override {
1452 EXPECT_EQ(2, mIndex++);
1453 }
1454 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1455 ADD_FAILURE() << "Primary frame draw not expected in this test";
1456 }
1457 void endFrame(const Rect& repaintRect) override {
1458 ADD_FAILURE() << "Primary frame draw not expected in this test";
1459 }
1460 };
1461
Stan Iliev06152cd2016-07-27 17:55:43 -04001462 auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
Chris Craik6246d2782016-03-29 15:01:41 -07001463 [](RenderProperties& props, RecordingCanvas& canvas) {
1464 props.mutateLayerProperties().setType(LayerType::RenderLayer);
1465 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
1466 });
1467 OffscreenBuffer** layerHandle = node->getLayerHandle();
1468
1469 // create RenderNode's layer here in same way prepareTree would
1470 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1471 *layerHandle = &layer;
1472
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001473 TestUtils::syncHierarchyPropertiesAndDisplayList(node);
Chris Craik6246d2782016-03-29 15:01:41 -07001474
1475 // only enqueue partial damage
1476 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1477 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1478
1479 // Draw, but pass empty node list, so no work is done for primary frame
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001480 FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
Chris Craik6246d2782016-03-29 15:01:41 -07001481 BuildLayerTestRenderer renderer;
1482 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1483 EXPECT_EQ(3, renderer.getIndex());
1484
1485 // clean up layer pointer, so we can safely destruct RenderNode
1486 *layerHandle = nullptr;
1487}
1488
Stan Iliev06152cd2016-07-27 17:55:43 -04001489static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
Chris Craik161f54b2015-11-05 11:08:52 -08001490 SkPaint paint;
Chris Craik814ee6a92016-07-26 16:22:50 -07001491 // order put in blue channel, transparent so overlapped content doesn't get rejected
1492 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
Chris Craik161f54b2015-11-05 11:08:52 -08001493 canvas->drawRect(0, 0, 100, 100, paint);
1494}
Stan Iliev06152cd2016-07-27 17:55:43 -04001495static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
1496 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001497 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001498 drawOrderedRect(&canvas, expectedDrawOrder);
1499 });
1500 node->mutateStagingProperties().setTranslationZ(z);
1501 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1502 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1503}
Chris Craik3a5811b2016-03-22 15:03:08 -07001504RENDERTHREAD_TEST(FrameBuilder, zReorder) {
Chris Craikd3daa312015-11-06 10:59:56 -08001505 class ZReorderTestRenderer : public TestRendererBase {
1506 public:
1507 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1508 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1509 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1510 }
1511 };
1512
Stan Iliev06152cd2016-07-27 17:55:43 -04001513 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001514 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001515 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1516 drawOrderedRect(&canvas, 1);
1517 canvas.insertReorderBarrier(true);
1518 drawOrderedNode(&canvas, 6, 2.0f);
1519 drawOrderedRect(&canvas, 3);
1520 drawOrderedNode(&canvas, 4, 0.0f);
1521 drawOrderedRect(&canvas, 5);
1522 drawOrderedNode(&canvas, 2, -2.0f);
1523 drawOrderedNode(&canvas, 7, 2.0f);
1524 canvas.insertReorderBarrier(false);
1525 drawOrderedRect(&canvas, 8);
1526 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1527 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001528 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1529 sLightGeometry, Caches::getInstance());
1530 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1531
Chris Craik161f54b2015-11-05 11:08:52 -08001532 ZReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001533 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik161f54b2015-11-05 11:08:52 -08001534 EXPECT_EQ(10, renderer.getIndex());
1535};
1536
Chris Craik3a5811b2016-03-22 15:03:08 -07001537RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
Chris Craik8d1f2122015-11-24 16:40:09 -08001538 static const int scrollX = 5;
1539 static const int scrollY = 10;
1540 class ProjectionReorderTestRenderer : public TestRendererBase {
1541 public:
1542 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1543 const int index = mIndex++;
1544
1545 Matrix4 expectedMatrix;
1546 switch (index) {
1547 case 0:
1548 EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1549 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1550 expectedMatrix.loadIdentity();
Chris Craik678ff812016-03-01 13:27:54 -08001551 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001552 break;
1553 case 1:
1554 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1555 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
Chris Craik678ff812016-03-01 13:27:54 -08001556 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1557 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1558 EXPECT_EQ(Rect(-35, -30, 45, 50),
1559 Rect(state.computedState.localProjectionPathMask->getBounds()));
Chris Craik8d1f2122015-11-24 16:40:09 -08001560 break;
1561 case 2:
1562 EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1563 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1564 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
Chris Craik678ff812016-03-01 13:27:54 -08001565 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001566 break;
1567 default:
1568 ADD_FAILURE();
1569 }
Chris Craik678ff812016-03-01 13:27:54 -08001570 EXPECT_EQ(expectedMatrix, state.computedState.transform);
Chris Craik8d1f2122015-11-24 16:40:09 -08001571 }
1572 };
1573
1574 /**
1575 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1576 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1577 * draw, but because it is projected backwards, it's drawn in between B and C.
1578 *
1579 * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1580 * (which isn't affected by scroll).
1581 */
Stan Iliev06152cd2016-07-27 17:55:43 -04001582 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001583 [](RenderProperties& properties, RecordingCanvas& canvas) {
1584 properties.setProjectionReceiver(true);
1585 // scroll doesn't apply to background, so undone via translationX/Y
1586 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1587 properties.setTranslationX(scrollX);
1588 properties.setTranslationY(scrollY);
1589
1590 SkPaint paint;
1591 paint.setColor(SK_ColorWHITE);
1592 canvas.drawRect(0, 0, 100, 100, paint);
1593 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001594 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(50, 0, 100, 50,
Chris Craik8d1f2122015-11-24 16:40:09 -08001595 [](RenderProperties& properties, RecordingCanvas& canvas) {
1596 properties.setProjectBackwards(true);
1597 properties.setClipToBounds(false);
1598 SkPaint paint;
1599 paint.setColor(SK_ColorDKGRAY);
1600 canvas.drawRect(-10, -10, 60, 60, paint);
1601 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001602 auto child = TestUtils::createNode<RecordingCanvas>(0, 50, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001603 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1604 SkPaint paint;
1605 paint.setColor(SK_ColorBLUE);
1606 canvas.drawRect(0, 0, 100, 50, paint);
1607 canvas.drawRenderNode(projectingRipple.get());
1608 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001609 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001610 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
Chris Craik678ff812016-03-01 13:27:54 -08001611 // Set a rect outline for the projecting ripple to be masked against.
1612 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1613
Florin Malitaeecff562015-12-21 10:43:01 -05001614 canvas.save(SaveFlags::MatrixClip);
Chris Craik8d1f2122015-11-24 16:40:09 -08001615 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1616 canvas.drawRenderNode(receiverBackground.get());
1617 canvas.drawRenderNode(child.get());
1618 canvas.restore();
1619 });
1620
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001621 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
1622 sLightGeometry, Caches::getInstance());
1623 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1624
Chris Craik8d1f2122015-11-24 16:40:09 -08001625 ProjectionReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001626 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8d1f2122015-11-24 16:40:09 -08001627 EXPECT_EQ(3, renderer.getIndex());
1628}
1629
Chris Craik678ff812016-03-01 13:27:54 -08001630RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1631 static const int scrollX = 5;
1632 static const int scrollY = 10;
1633 class ProjectionHwLayerTestRenderer : public TestRendererBase {
1634 public:
1635 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1636 EXPECT_EQ(0, mIndex++);
1637 }
1638 void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1639 EXPECT_EQ(1, mIndex++);
1640 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1641 }
1642 void endLayer() override {
1643 EXPECT_EQ(2, mIndex++);
1644 }
1645 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1646 EXPECT_EQ(3, mIndex++);
1647 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1648 }
1649 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1650 EXPECT_EQ(4, mIndex++);
1651 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1652 Matrix4 expected;
1653 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1654 EXPECT_EQ(expected, state.computedState.transform);
1655 EXPECT_EQ(Rect(-85, -80, 295, 300),
1656 Rect(state.computedState.localProjectionPathMask->getBounds()));
1657 }
1658 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1659 EXPECT_EQ(5, mIndex++);
1660 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1661 }
1662 };
Stan Iliev06152cd2016-07-27 17:55:43 -04001663 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
Chris Craik678ff812016-03-01 13:27:54 -08001664 [](RenderProperties& properties, RecordingCanvas& canvas) {
1665 properties.setProjectionReceiver(true);
1666 // scroll doesn't apply to background, so undone via translationX/Y
1667 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1668 properties.setTranslationX(scrollX);
1669 properties.setTranslationY(scrollY);
1670
1671 canvas.drawRect(0, 0, 400, 400, SkPaint());
1672 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001673 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik678ff812016-03-01 13:27:54 -08001674 [](RenderProperties& properties, RecordingCanvas& canvas) {
1675 properties.setProjectBackwards(true);
1676 properties.setClipToBounds(false);
1677 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1678 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001679 auto child = TestUtils::createNode<RecordingCanvas>(100, 100, 300, 300,
Chris Craik678ff812016-03-01 13:27:54 -08001680 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1681 properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1682 canvas.drawRenderNode(projectingRipple.get());
1683 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1684 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001685 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
Chris Craik678ff812016-03-01 13:27:54 -08001686 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1687 // Set a rect outline for the projecting ripple to be masked against.
1688 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1689 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1690 canvas.drawRenderNode(receiverBackground.get());
1691 canvas.drawRenderNode(child.get());
1692 });
1693
1694 OffscreenBuffer** layerHandle = child->getLayerHandle();
1695
1696 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1697 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1698 Matrix4 windowTransform;
1699 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1700 layer.setWindowTransform(windowTransform);
1701 *layerHandle = &layer;
1702
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001703 auto syncedNode = TestUtils::getSyncedNode(parent);
1704
Chris Craik678ff812016-03-01 13:27:54 -08001705 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1706 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001707
1708 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1709 sLightGeometry, Caches::getInstance());
1710 frameBuilder.deferLayers(layerUpdateQueue);
1711 frameBuilder.deferRenderNode(*syncedNode);
1712
Chris Craik678ff812016-03-01 13:27:54 -08001713 ProjectionHwLayerTestRenderer renderer;
1714 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1715 EXPECT_EQ(6, renderer.getIndex());
1716
1717 // clean up layer pointer, so we can safely destruct RenderNode
1718 *layerHandle = nullptr;
1719}
1720
Chris Craika748c082016-03-01 18:48:37 -08001721RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1722 static const int scrollX = 500000;
1723 static const int scrollY = 0;
1724 class ProjectionChildScrollTestRenderer : public TestRendererBase {
1725 public:
1726 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1727 EXPECT_EQ(0, mIndex++);
1728 EXPECT_TRUE(state.computedState.transform.isIdentity());
1729 }
1730 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1731 EXPECT_EQ(1, mIndex++);
1732 ASSERT_NE(nullptr, state.computedState.clipState);
1733 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1734 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1735 EXPECT_TRUE(state.computedState.transform.isIdentity());
1736 }
1737 };
Stan Iliev06152cd2016-07-27 17:55:43 -04001738 auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
Chris Craika748c082016-03-01 18:48:37 -08001739 [](RenderProperties& properties, RecordingCanvas& canvas) {
1740 properties.setProjectionReceiver(true);
1741 canvas.drawRect(0, 0, 400, 400, SkPaint());
1742 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001743 auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craika748c082016-03-01 18:48:37 -08001744 [](RenderProperties& properties, RecordingCanvas& canvas) {
1745 // scroll doesn't apply to background, so undone via translationX/Y
1746 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1747 properties.setTranslationX(scrollX);
1748 properties.setTranslationY(scrollY);
1749 properties.setProjectBackwards(true);
1750 properties.setClipToBounds(false);
1751 canvas.drawOval(0, 0, 200, 200, SkPaint());
1752 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001753 auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
Chris Craika748c082016-03-01 18:48:37 -08001754 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1755 // Record time clip will be ignored by projectee
1756 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1757
1758 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1759 canvas.drawRenderNode(projectingRipple.get());
1760 });
Stan Iliev06152cd2016-07-27 17:55:43 -04001761 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
Chris Craika748c082016-03-01 18:48:37 -08001762 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1763 canvas.drawRenderNode(receiverBackground.get());
1764 canvas.drawRenderNode(child.get());
1765 });
1766
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001767 FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
1768 sLightGeometry, Caches::getInstance());
1769 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1770
Chris Craika748c082016-03-01 18:48:37 -08001771 ProjectionChildScrollTestRenderer renderer;
1772 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1773 EXPECT_EQ(2, renderer.getIndex());
1774}
1775
Chris Craik98787e62015-11-13 10:55:30 -08001776// creates a 100x100 shadow casting node with provided translationZ
1777static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
Stan Iliev06152cd2016-07-27 17:55:43 -04001778 return TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001779 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
John Reck16c9d6a2015-11-17 15:51:08 -08001780 properties.setTranslationZ(translationZ);
1781 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
Chris Craik98787e62015-11-13 10:55:30 -08001782 SkPaint paint;
1783 paint.setColor(SK_ColorWHITE);
1784 canvas.drawRect(0, 0, 100, 100, paint);
Chris Craik98787e62015-11-13 10:55:30 -08001785 });
1786}
1787
Chris Craik6e068c012016-01-15 16:15:30 -08001788RENDERTHREAD_TEST(FrameBuilder, shadow) {
Chris Craikd3daa312015-11-06 10:59:56 -08001789 class ShadowTestRenderer : public TestRendererBase {
1790 public:
1791 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1792 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001793 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
Chris Craik6e068c012016-01-15 16:15:30 -08001794 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1795 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
Chris Craik98787e62015-11-13 10:55:30 -08001796
1797 Matrix4 expectedZ;
1798 expectedZ.loadTranslate(0, 0, 5);
Chris Craik6e068c012016-01-15 16:15:30 -08001799 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
Chris Craikd3daa312015-11-06 10:59:56 -08001800 }
1801 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1802 EXPECT_EQ(1, mIndex++);
1803 }
1804 };
Chris Craik161f54b2015-11-05 11:08:52 -08001805
Stan Iliev06152cd2016-07-27 17:55:43 -04001806 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -08001807 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikd3daa312015-11-06 10:59:56 -08001808 canvas.insertReorderBarrier(true);
Chris Craik98787e62015-11-13 10:55:30 -08001809 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
Chris Craikd3daa312015-11-06 10:59:56 -08001810 });
1811
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001812 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
1813 sLightGeometry, Caches::getInstance());
1814 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1815
Chris Craikd3daa312015-11-06 10:59:56 -08001816 ShadowTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001817 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd3daa312015-11-06 10:59:56 -08001818 EXPECT_EQ(2, renderer.getIndex());
1819}
Chris Craik76caecf2015-11-02 19:17:45 -08001820
Chris Craik6e068c012016-01-15 16:15:30 -08001821RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001822 class ShadowSaveLayerTestRenderer : public TestRendererBase {
1823 public:
1824 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1825 EXPECT_EQ(0, mIndex++);
1826 return nullptr;
1827 }
1828 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1829 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001830 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1831 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
Chris Craik98787e62015-11-13 10:55:30 -08001832 }
1833 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1834 EXPECT_EQ(2, mIndex++);
1835 }
1836 void endLayer() override {
1837 EXPECT_EQ(3, mIndex++);
1838 }
1839 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1840 EXPECT_EQ(4, mIndex++);
1841 }
Chris Craik74af6e22016-04-05 13:18:56 -07001842 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1843 EXPECT_EQ(5, mIndex++);
1844 }
Chris Craik98787e62015-11-13 10:55:30 -08001845 };
1846
Stan Iliev06152cd2016-07-27 17:55:43 -04001847 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -08001848 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001849 // save/restore outside of reorderBarrier, so they don't get moved out of place
1850 canvas.translate(20, 10);
Florin Malitaeecff562015-12-21 10:43:01 -05001851 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001852 canvas.insertReorderBarrier(true);
1853 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1854 canvas.insertReorderBarrier(false);
1855 canvas.restoreToCount(count);
1856 });
1857
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001858 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001859 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001860 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1861
Chris Craik98787e62015-11-13 10:55:30 -08001862 ShadowSaveLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001863 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -07001864 EXPECT_EQ(6, renderer.getIndex());
Chris Craik98787e62015-11-13 10:55:30 -08001865}
1866
Chris Craikf158b492016-01-12 14:45:08 -08001867RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001868 class ShadowHwLayerTestRenderer : public TestRendererBase {
1869 public:
1870 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1871 EXPECT_EQ(0, mIndex++);
1872 }
1873 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1874 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001875 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1876 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1877 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
Chris Craik98787e62015-11-13 10:55:30 -08001878 }
1879 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1880 EXPECT_EQ(2, mIndex++);
1881 }
1882 void endLayer() override {
1883 EXPECT_EQ(3, mIndex++);
1884 }
1885 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1886 EXPECT_EQ(4, mIndex++);
1887 }
1888 };
1889
Stan Iliev06152cd2016-07-27 17:55:43 -04001890 auto parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160,
John Reck16c9d6a2015-11-17 15:51:08 -08001891 [](RenderProperties& props, RecordingCanvas& canvas) {
1892 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001893 canvas.insertReorderBarrier(true);
Florin Malitaeecff562015-12-21 10:43:01 -05001894 canvas.save(SaveFlags::MatrixClip);
Chris Craik98787e62015-11-13 10:55:30 -08001895 canvas.translate(20, 10);
1896 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1897 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001898 });
Chris Craik98787e62015-11-13 10:55:30 -08001899 OffscreenBuffer** layerHandle = parent->getLayerHandle();
1900
1901 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1902 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1903 Matrix4 windowTransform;
1904 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1905 layer.setWindowTransform(windowTransform);
1906 *layerHandle = &layer;
1907
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001908 auto syncedNode = TestUtils::getSyncedNode(parent);
Chris Craik98787e62015-11-13 10:55:30 -08001909 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1910 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001911
1912 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001913 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001914 frameBuilder.deferLayers(layerUpdateQueue);
1915 frameBuilder.deferRenderNode(*syncedNode);
1916
Chris Craik98787e62015-11-13 10:55:30 -08001917 ShadowHwLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001918 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001919 EXPECT_EQ(5, renderer.getIndex());
1920
1921 // clean up layer pointer, so we can safely destruct RenderNode
1922 *layerHandle = nullptr;
1923}
1924
Chris Craik3a5811b2016-03-22 15:03:08 -07001925RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
Chris Craik98787e62015-11-13 10:55:30 -08001926 class ShadowLayeringTestRenderer : public TestRendererBase {
1927 public:
1928 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1929 int index = mIndex++;
1930 EXPECT_TRUE(index == 0 || index == 1);
1931 }
1932 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1933 int index = mIndex++;
1934 EXPECT_TRUE(index == 2 || index == 3);
1935 }
1936 };
Stan Iliev06152cd2016-07-27 17:55:43 -04001937 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
Chris Craik8d1f2122015-11-24 16:40:09 -08001938 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001939 canvas.insertReorderBarrier(true);
1940 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1941 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1942 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001943 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001944 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001945 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1946
Chris Craik98787e62015-11-13 10:55:30 -08001947 ShadowLayeringTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001948 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001949 EXPECT_EQ(4, renderer.getIndex());
1950}
1951
Chris Craikd6456402016-04-11 12:24:23 -07001952RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
1953 class ShadowClippingTestRenderer : public TestRendererBase {
1954 public:
1955 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1956 EXPECT_EQ(0, mIndex++);
1957 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
1958 << "Shadow must respect pre-barrier canvas clip value.";
1959 }
1960 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1961 EXPECT_EQ(1, mIndex++);
1962 }
1963 };
Stan Iliev06152cd2016-07-27 17:55:43 -04001964 auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
Chris Craikd6456402016-04-11 12:24:23 -07001965 [](RenderProperties& props, RecordingCanvas& canvas) {
1966 // Apply a clip before the reorder barrier/shadow casting child is drawn.
1967 // This clip must be applied to the shadow cast by the child.
1968 canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op);
1969 canvas.insertReorderBarrier(true);
1970 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1971 });
1972
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001973 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
Chris Craikd6456402016-04-11 12:24:23 -07001974 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik9cd1bbe2016-04-14 16:08:25 -07001975 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
1976
Chris Craikd6456402016-04-11 12:24:23 -07001977 ShadowClippingTestRenderer renderer;
1978 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1979 EXPECT_EQ(2, renderer.getIndex());
1980}
1981
John Reck16c9d6a2015-11-17 15:51:08 -08001982static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
Chris Craik76caecf2015-11-02 19:17:45 -08001983 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
Chris Craikd3daa312015-11-06 10:59:56 -08001984 class PropertyTestRenderer : public TestRendererBase {
1985 public:
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -07001986 explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
Chris Craikd3daa312015-11-06 10:59:56 -08001987 : mCallback(callback) {}
1988 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1989 EXPECT_EQ(mIndex++, 0);
1990 mCallback(op, state);
1991 }
1992 std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1993 };
1994
Stan Iliev06152cd2016-07-27 17:55:43 -04001995 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
John Reck16c9d6a2015-11-17 15:51:08 -08001996 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1997 propSetupCallback(props);
Chris Craik76caecf2015-11-02 19:17:45 -08001998 SkPaint paint;
1999 paint.setColor(SK_ColorWHITE);
2000 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08002001 });
Chris Craik76caecf2015-11-02 19:17:45 -08002002
Chris Craik9cd1bbe2016-04-14 16:08:25 -07002003 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
2004 sLightGeometry, Caches::getInstance());
2005 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
2006
Chris Craik76caecf2015-11-02 19:17:45 -08002007 PropertyTestRenderer renderer(opValidateCallback);
Chris Craikf158b492016-01-12 14:45:08 -08002008 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik76caecf2015-11-02 19:17:45 -08002009 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
2010}
2011
Chris Craik3a5811b2016-03-22 15:03:08 -07002012RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
Chris Craik76caecf2015-11-02 19:17:45 -08002013 testProperty([](RenderProperties& properties) {
2014 properties.setAlpha(0.5f);
2015 properties.setHasOverlappingRendering(false);
Chris Craik76caecf2015-11-02 19:17:45 -08002016 }, [](const RectOp& op, const BakedOpState& state) {
2017 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
2018 });
2019}
2020
Chris Craik3a5811b2016-03-22 15:03:08 -07002021RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
Chris Craik76caecf2015-11-02 19:17:45 -08002022 testProperty([](RenderProperties& properties) {
2023 properties.setClipToBounds(true);
2024 properties.setClipBounds(Rect(10, 20, 300, 400));
Chris Craik76caecf2015-11-02 19:17:45 -08002025 }, [](const RectOp& op, const BakedOpState& state) {
2026 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
2027 << "Clip rect should be intersection of node bounds and clip bounds";
2028 });
2029}
2030
Chris Craik3a5811b2016-03-22 15:03:08 -07002031RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08002032 testProperty([](RenderProperties& properties) {
2033 properties.mutableRevealClip().set(true, 50, 50, 25);
Chris Craik76caecf2015-11-02 19:17:45 -08002034 }, [](const RectOp& op, const BakedOpState& state) {
2035 ASSERT_NE(nullptr, state.roundRectClipState);
2036 EXPECT_TRUE(state.roundRectClipState->highPriority);
2037 EXPECT_EQ(25, state.roundRectClipState->radius);
2038 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
2039 });
2040}
2041
Chris Craik3a5811b2016-03-22 15:03:08 -07002042RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08002043 testProperty([](RenderProperties& properties) {
2044 properties.mutableOutline().setShouldClip(true);
2045 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
Chris Craik76caecf2015-11-02 19:17:45 -08002046 }, [](const RectOp& op, const BakedOpState& state) {
2047 ASSERT_NE(nullptr, state.roundRectClipState);
2048 EXPECT_FALSE(state.roundRectClipState->highPriority);
2049 EXPECT_EQ(5, state.roundRectClipState->radius);
2050 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
2051 });
2052}
2053
Chris Craik3a5811b2016-03-22 15:03:08 -07002054RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
Chris Craik76caecf2015-11-02 19:17:45 -08002055 testProperty([](RenderProperties& properties) {
2056 properties.setLeftTopRightBottom(10, 10, 110, 110);
2057
2058 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
2059 properties.setStaticMatrix(&staticMatrix);
2060
2061 // ignored, since static overrides animation
2062 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
2063 properties.setAnimationMatrix(&animationMatrix);
2064
2065 properties.setTranslationX(10);
2066 properties.setTranslationY(20);
2067 properties.setScaleX(0.5f);
2068 properties.setScaleY(0.7f);
Chris Craik76caecf2015-11-02 19:17:45 -08002069 }, [](const RectOp& op, const BakedOpState& state) {
2070 Matrix4 matrix;
2071 matrix.loadTranslate(10, 10, 0); // left, top
2072 matrix.scale(1.2f, 1.2f, 1); // static matrix
2073 // ignore animation matrix, since static overrides it
2074
2075 // translation xy
2076 matrix.translate(10, 20);
2077
2078 // scale xy (from default pivot - center)
2079 matrix.translate(50, 50);
2080 matrix.scale(0.5f, 0.7f, 1);
2081 matrix.translate(-50, -50);
2082 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
2083 << "Op draw matrix must match expected combination of transformation properties";
2084 });
2085}
Chris Craik161f54b2015-11-05 11:08:52 -08002086
Chris Craik8ecf41c2015-11-16 10:27:59 -08002087struct SaveLayerAlphaData {
2088 uint32_t layerWidth = 0;
2089 uint32_t layerHeight = 0;
2090 Rect rectClippedBounds;
2091 Matrix4 rectMatrix;
John Reckc9bb1a32016-05-24 15:06:01 -07002092 Matrix4 drawLayerMatrix;
Chris Craik8ecf41c2015-11-16 10:27:59 -08002093};
2094/**
2095 * Constructs a view to hit the temporary layer alpha property implementation:
2096 * a) 0 < alpha < 1
2097 * b) too big for layer (larger than maxTextureSize)
2098 * c) overlapping rendering content
2099 * returning observed data about layer size and content clip/transform.
2100 *
2101 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
2102 * (for efficiency, and to fit in layer size constraints) based on parent clip.
2103 */
2104void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
John Reck16c9d6a2015-11-17 15:51:08 -08002105 std::function<void(RenderProperties&)> propSetupCallback) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08002106 class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
2107 public:
Chih-Hung Hsiehd53e3be2016-05-03 10:02:51 -07002108 explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
Chris Craik8ecf41c2015-11-16 10:27:59 -08002109 : mOutData(outData) {}
2110
2111 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
2112 EXPECT_EQ(0, mIndex++);
2113 mOutData->layerWidth = width;
2114 mOutData->layerHeight = height;
2115 return nullptr;
2116 }
2117 void onRectOp(const RectOp& op, const BakedOpState& state) override {
2118 EXPECT_EQ(1, mIndex++);
2119
2120 mOutData->rectClippedBounds = state.computedState.clippedBounds;
2121 mOutData->rectMatrix = state.computedState.transform;
2122 }
2123 void endLayer() override {
2124 EXPECT_EQ(2, mIndex++);
2125 }
2126 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
2127 EXPECT_EQ(3, mIndex++);
John Reckc9bb1a32016-05-24 15:06:01 -07002128 mOutData->drawLayerMatrix = state.computedState.transform;
Chris Craik8ecf41c2015-11-16 10:27:59 -08002129 }
Chris Craik74af6e22016-04-05 13:18:56 -07002130 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
2131 EXPECT_EQ(4, mIndex++);
2132 }
Chris Craik8ecf41c2015-11-16 10:27:59 -08002133 private:
2134 SaveLayerAlphaData* mOutData;
2135 };
2136
2137 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
2138 << "Node must be bigger than max texture size to exercise saveLayer codepath";
Stan Iliev06152cd2016-07-27 17:55:43 -04002139 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 10000, 10000,
John Reck16c9d6a2015-11-17 15:51:08 -08002140 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
2141 properties.setHasOverlappingRendering(true);
2142 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
2143 // apply other properties
2144 propSetupCallback(properties);
2145
Chris Craik8ecf41c2015-11-16 10:27:59 -08002146 SkPaint paint;
2147 paint.setColor(SK_ColorWHITE);
2148 canvas.drawRect(0, 0, 10000, 10000, paint);
Chris Craik8ecf41c2015-11-16 10:27:59 -08002149 });
Chris Craik9cd1bbe2016-04-14 16:08:25 -07002150 auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
Chris Craik8ecf41c2015-11-16 10:27:59 -08002151
Chris Craik9cd1bbe2016-04-14 16:08:25 -07002152 FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
2153 sLightGeometry, Caches::getInstance());
2154 frameBuilder.deferRenderNode(*syncedNode);
2155
Chris Craik8ecf41c2015-11-16 10:27:59 -08002156 SaveLayerAlphaClipTestRenderer renderer(outObservedData);
Chris Craikf158b492016-01-12 14:45:08 -08002157 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8ecf41c2015-11-16 10:27:59 -08002158
2159 // assert, since output won't be valid if we haven't seen a save layer triggered
Chris Craik74af6e22016-04-05 13:18:56 -07002160 ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
Chris Craik8ecf41c2015-11-16 10:27:59 -08002161}
2162
Chris Craik3a5811b2016-03-22 15:03:08 -07002163RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08002164 SaveLayerAlphaData observedData;
2165 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2166 properties.setTranslationX(10); // offset rendering content
2167 properties.setTranslationY(-2000); // offset rendering content
Chris Craik8ecf41c2015-11-16 10:27:59 -08002168 });
2169 EXPECT_EQ(190u, observedData.layerWidth);
2170 EXPECT_EQ(200u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08002171 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
Chris Craik8ecf41c2015-11-16 10:27:59 -08002172 << "expect content to be clipped to screen area";
2173 Matrix4 expected;
2174 expected.loadTranslate(0, -2000, 0);
2175 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
2176 << "expect content to be translated as part of being clipped";
John Reckc9bb1a32016-05-24 15:06:01 -07002177 expected.loadTranslate(10, 0, 0);
2178 EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix)
2179 << "expect drawLayer to be translated as part of being clipped";
Chris Craik8ecf41c2015-11-16 10:27:59 -08002180}
2181
Chris Craik3a5811b2016-03-22 15:03:08 -07002182RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08002183 SaveLayerAlphaData observedData;
2184 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2185 // Translate and rotate the view so that the only visible part is the top left corner of
Chris Craik8d1f2122015-11-24 16:40:09 -08002186 // the view. It will form an isosceles right triangle with a long side length of 200 at the
Chris Craik8ecf41c2015-11-16 10:27:59 -08002187 // bottom of the viewport.
2188 properties.setTranslationX(100);
2189 properties.setTranslationY(100);
2190 properties.setPivotX(0);
2191 properties.setPivotY(0);
2192 properties.setRotation(45);
Chris Craik8ecf41c2015-11-16 10:27:59 -08002193 });
2194 // ceil(sqrt(2) / 2 * 200) = 142
2195 EXPECT_EQ(142u, observedData.layerWidth);
2196 EXPECT_EQ(142u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08002197 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08002198 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2199}
2200
Chris Craik3a5811b2016-03-22 15:03:08 -07002201RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08002202 SaveLayerAlphaData observedData;
2203 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
2204 properties.setPivotX(0);
2205 properties.setPivotY(0);
2206 properties.setScaleX(2);
2207 properties.setScaleY(0.5f);
Chris Craik8ecf41c2015-11-16 10:27:59 -08002208 });
2209 EXPECT_EQ(100u, observedData.layerWidth);
2210 EXPECT_EQ(400u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08002211 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08002212 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
2213}
2214
Chris Craik04d46eb2016-04-07 13:51:07 -07002215RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
2216 class ClipReplaceTestRenderer : public TestRendererBase {
2217 public:
2218 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
2219 EXPECT_EQ(0, mIndex++);
2220 EXPECT_TRUE(op.localClip->intersectWithRoot);
2221 EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect)
2222 << "Expect resolved clip to be intersection of viewport clip and clip op";
2223 }
2224 };
Stan Iliev06152cd2016-07-27 17:55:43 -04002225 auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
Chris Craik04d46eb2016-04-07 13:51:07 -07002226 [](RenderProperties& props, RecordingCanvas& canvas) {
2227 canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op);
2228 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
2229 });
2230
Chris Craik9cd1bbe2016-04-14 16:08:25 -07002231 FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
2232 sLightGeometry, Caches::getInstance());
2233 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
2234
Chris Craik04d46eb2016-04-07 13:51:07 -07002235 ClipReplaceTestRenderer renderer;
2236 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
2237 EXPECT_EQ(1, renderer.getIndex());
2238}
2239
Chris Craik6fe991e52015-10-20 09:39:42 -07002240} // namespace uirenderer
2241} // namespace android