blob: 0ea246f00fb7e035ec49d990046efd2816a63941 [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 Craik8ecf41c2015-11-16 10:27:59 -080032const LayerUpdateQueue sEmptyLayerUpdateQueue;
Chris Craik6246d2782016-03-29 15:01:41 -070033const std::vector< sp<RenderNode> > sEmptyNodeList;
Chris Craik6e068c012016-01-15 16:15:30 -080034const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
35
Chris Craik98787e62015-11-13 10:55:30 -080036
Chris Craik6fe991e52015-10-20 09:39:42 -070037/**
Chris Craik5854b342015-10-26 15:49:56 -070038 * Virtual class implemented by each test to redirect static operation / state transitions to
39 * virtual methods.
Chris Craik6fe991e52015-10-20 09:39:42 -070040 *
Chris Craik5854b342015-10-26 15:49:56 -070041 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
42 * and allows Renderer vs Dispatching behavior to be merged.
Chris Craik6fe991e52015-10-20 09:39:42 -070043 *
44 * onXXXOp methods fail by default - tests should override ops they expect
Chris Craikd3daa312015-11-06 10:59:56 -080045 * startRepaintLayer fails by default - tests should override if expected
Chris Craik6fe991e52015-10-20 09:39:42 -070046 * startFrame/endFrame do nothing by default - tests should override to intercept
47 */
Chris Craik5854b342015-10-26 15:49:56 -070048class TestRendererBase {
Chris Craik6fe991e52015-10-20 09:39:42 -070049public:
Chris Craik5854b342015-10-26 15:49:56 -070050 virtual ~TestRendererBase() {}
Chris Craikd3daa312015-11-06 10:59:56 -080051 virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
Chris Craik74af6e22016-04-05 13:18:56 -070052 ADD_FAILURE() << "Temporary layers not expected in this test";
Chris Craika6ac95e2015-11-02 18:06:59 -080053 return nullptr;
54 }
Chris Craik74af6e22016-04-05 13:18:56 -070055 virtual void recycleTemporaryLayer(OffscreenBuffer*) {
56 ADD_FAILURE() << "Temporary layers not expected in this test";
57 }
Chris Craik98787e62015-11-13 10:55:30 -080058 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
Chris Craika6ac95e2015-11-02 18:06:59 -080059 ADD_FAILURE() << "Layer repaint not expected in this test";
60 }
61 virtual void endLayer() {
62 ADD_FAILURE() << "Layer updates not expected in this test";
63 }
Chris Craik98787e62015-11-13 10:55:30 -080064 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
Chris Craike4db79d2015-12-22 16:32:23 -080065 virtual void endFrame(const Rect& repaintRect) {}
Chris Craik6fe991e52015-10-20 09:39:42 -070066
Chris Craik15c3f192015-12-03 12:16:56 -080067 // define virtual defaults for single draw methods
68#define X(Type) \
Chris Craika6ac95e2015-11-02 18:06:59 -080069 virtual void on##Type(const Type&, const BakedOpState&) { \
70 ADD_FAILURE() << #Type " not expected in this test"; \
71 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080072 MAP_RENDERABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080073#undef X
74
75 // define virtual defaults for merged draw methods
76#define X(Type) \
77 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
78 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
79 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080080 MAP_MERGEABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080081#undef X
82
Chris Craik5854b342015-10-26 15:49:56 -070083 int getIndex() { return mIndex; }
Chris Craik6fe991e52015-10-20 09:39:42 -070084
Chris Craik5854b342015-10-26 15:49:56 -070085protected:
86 int mIndex = 0;
87};
88
89/**
90 * Dispatches all static methods to similar formed methods on renderer, which fail by default but
Chris Craik8ecf41c2015-11-16 10:27:59 -080091 * are overridden by subclasses per test.
Chris Craik5854b342015-10-26 15:49:56 -070092 */
93class TestDispatcher {
94public:
Chris Craik15c3f192015-12-03 12:16:56 -080095 // define single op methods, which redirect to TestRendererBase
96#define X(Type) \
Chris Craik5854b342015-10-26 15:49:56 -070097 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
98 renderer.on##Type(op, state); \
Chris Craik6fe991e52015-10-20 09:39:42 -070099 }
Chris Craik7cbf63d2016-01-06 13:46:52 -0800100 MAP_RENDERABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -0800101#undef X
102
103 // define merged op methods, which redirect to TestRendererBase
104#define X(Type) \
105 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
106 renderer.onMerged##Type##s(opList); \
107 }
Chris Craik7cbf63d2016-01-06 13:46:52 -0800108 MAP_MERGEABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -0800109#undef X
Chris Craik6fe991e52015-10-20 09:39:42 -0700110};
Chris Craikb565df12015-10-05 13:00:52 -0700111
Chris Craik5854b342015-10-26 15:49:56 -0700112class FailRenderer : public TestRendererBase {};
Chris Craik6fe991e52015-10-20 09:39:42 -0700113
Chris Craik3a5811b2016-03-22 15:03:08 -0700114RENDERTHREAD_TEST(FrameBuilder, simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800115 class SimpleTestRenderer : public TestRendererBase {
116 public:
Chris Craik98787e62015-11-13 10:55:30 -0800117 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800118 EXPECT_EQ(0, mIndex++);
119 EXPECT_EQ(100u, width);
120 EXPECT_EQ(200u, height);
121 }
122 void onRectOp(const RectOp& op, const BakedOpState& state) override {
123 EXPECT_EQ(1, mIndex++);
124 }
125 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
126 EXPECT_EQ(2, mIndex++);
127 }
Chris Craike4db79d2015-12-22 16:32:23 -0800128 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800129 EXPECT_EQ(3, mIndex++);
130 }
131 };
132
Chris Craik8d1f2122015-11-24 16:40:09 -0800133 auto node = TestUtils::createNode(0, 0, 100, 200,
134 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700135 SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
Chris Craikb565df12015-10-05 13:00:52 -0700136 canvas.drawRect(0, 0, 100, 200, SkPaint());
137 canvas.drawBitmap(bitmap, 10, 10, nullptr);
138 });
Chris Craikf158b492016-01-12 14:45:08 -0800139 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700140 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700141 SimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800142 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700143 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
Chris Craik6fe991e52015-10-20 09:39:42 -0700144}
145
Chris Craik3a5811b2016-03-22 15:03:08 -0700146RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
Chris Craik386aa032015-12-07 17:08:25 -0800147 class SimpleStrokeTestRenderer : public TestRendererBase {
148 public:
149 void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
150 EXPECT_EQ(0, mIndex++);
151 // even though initial bounds are empty...
152 EXPECT_TRUE(op.unmappedBounds.isEmpty())
153 << "initial bounds should be empty, since they're unstroked";
154 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
155 << "final bounds should account for stroke";
156 }
157 };
158
159 auto node = TestUtils::createNode(0, 0, 100, 200,
160 [](RenderProperties& props, RecordingCanvas& canvas) {
161 SkPaint strokedPaint;
162 strokedPaint.setStrokeWidth(10);
163 canvas.drawPoint(50, 50, strokedPaint);
164 });
Chris Craikf158b492016-01-12 14:45:08 -0800165 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700166 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik386aa032015-12-07 17:08:25 -0800167 SimpleStrokeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800168 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik386aa032015-12-07 17:08:25 -0800169 EXPECT_EQ(1, renderer.getIndex());
170}
171
Chris Craik3a5811b2016-03-22 15:03:08 -0700172RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800173 auto node = TestUtils::createNode(0, 0, 200, 200,
174 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500175 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700176 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
177 canvas.drawRect(0, 0, 400, 400, SkPaint());
178 canvas.restore();
179 });
Chris Craikf158b492016-01-12 14:45:08 -0800180 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700181 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik6fe991e52015-10-20 09:39:42 -0700182
Chris Craik5854b342015-10-26 15:49:56 -0700183 FailRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800184 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb565df12015-10-05 13:00:52 -0700185}
186
Chris Craik3a5811b2016-03-22 15:03:08 -0700187RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
Chris Craika1717272015-11-19 13:02:43 -0800188 const int LOOPS = 5;
Chris Craikd3daa312015-11-06 10:59:56 -0800189 class SimpleBatchingTestRenderer : public TestRendererBase {
190 public:
191 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800192 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
Chris Craikd3daa312015-11-06 10:59:56 -0800193 }
194 void onRectOp(const RectOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800195 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
Chris Craikd3daa312015-11-06 10:59:56 -0800196 }
197 };
198
Chris Craik8d1f2122015-11-24 16:40:09 -0800199 auto node = TestUtils::createNode(0, 0, 200, 200,
200 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik15c3f192015-12-03 12:16:56 -0800201 SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
202 kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
Chris Craikb565df12015-10-05 13:00:52 -0700203
204 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
205 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
Florin Malitaeecff562015-12-21 10:43:01 -0500206 canvas.save(SaveFlags::MatrixClip);
Chris Craika1717272015-11-19 13:02:43 -0800207 for (int i = 0; i < LOOPS; i++) {
Chris Craikb565df12015-10-05 13:00:52 -0700208 canvas.translate(0, 10);
209 canvas.drawRect(0, 0, 10, 10, SkPaint());
210 canvas.drawBitmap(bitmap, 5, 0, nullptr);
211 }
212 canvas.restore();
213 });
214
Chris Craikf158b492016-01-12 14:45:08 -0800215 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700216 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700217 SimpleBatchingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800218 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800219 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craik15c3f192015-12-03 12:16:56 -0800220 << "Expect number of ops = 2 * loop count";
Chris Craika1717272015-11-19 13:02:43 -0800221}
222
Chris Craik6246d2782016-03-29 15:01:41 -0700223RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
224 class EmptyNoFbo0TestRenderer : public TestRendererBase {
225 public:
226 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
227 ADD_FAILURE() << "Primary frame draw not expected in this test";
228 }
229 void endFrame(const Rect& repaintRect) override {
230 ADD_FAILURE() << "Primary frame draw not expected in this test";
231 }
232 };
233
234 // Pass empty node list, so no work is enqueued for Fbo0
235 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
236 sEmptyNodeList, sLightGeometry, Caches::getInstance());
237 EmptyNoFbo0TestRenderer renderer;
238 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
239}
240
241RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
242 class EmptyWithFbo0TestRenderer : public TestRendererBase {
243 public:
244 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
245 EXPECT_EQ(0, mIndex++);
246 }
247 void endFrame(const Rect& repaintRect) override {
248 EXPECT_EQ(1, mIndex++);
249 }
250 };
251 auto node = TestUtils::createNode(10, 10, 110, 110,
252 [](RenderProperties& props, RecordingCanvas& canvas) {
253 // no drawn content
254 });
255 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
256
257 // Draw, but pass empty node list, so no work is done for primary frame
258 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
259 syncedNodeList, sLightGeometry, Caches::getInstance());
260 EmptyWithFbo0TestRenderer renderer;
261 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
262 EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
263 " but fbo0 update lifecycle should still be observed";
264}
265
Chris Craik80d2ade2016-03-28 12:54:07 -0700266RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
267 class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
268 public:
269 void onRectOp(const RectOp& op, const BakedOpState& state) override {
270 EXPECT_EQ(mIndex++, 0) << "Should be one rect";
271 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
272 << "Last rect should occlude others.";
273 }
274 };
275 auto node = TestUtils::createNode(0, 0, 200, 200,
276 [](RenderProperties& props, RecordingCanvas& canvas) {
277 canvas.drawRect(0, 0, 200, 200, SkPaint());
278 canvas.drawRect(0, 0, 200, 200, SkPaint());
279 canvas.drawRect(10, 10, 190, 190, SkPaint());
280 });
281
282 // Damage (and therefore clip) is same as last draw, subset of renderable area.
283 // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
284 SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
285 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
286 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
287
288 EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
289 << "Recording must not have rejected ops, in order for this test to be valid";
290
291 AvoidOverdrawRectsTestRenderer renderer;
292 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
293 EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
294}
295
296RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
297 static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
298 SkColorType::kRGB_565_SkColorType);
299 static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
300 SkColorType::kAlpha_8_SkColorType);
301 class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
302 public:
303 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craik80d2ade2016-03-28 12:54:07 -0700304 switch(mIndex++) {
305 case 0:
306 EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
307 break;
308 case 1:
309 EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
310 break;
311 default:
312 ADD_FAILURE() << "Only two ops expected.";
313 }
314 }
315 };
316
317 auto node = TestUtils::createNode(0, 0, 50, 50,
318 [](RenderProperties& props, RecordingCanvas& canvas) {
319 canvas.drawRect(0, 0, 50, 50, SkPaint());
320 canvas.drawRect(0, 0, 50, 50, SkPaint());
321 canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
322
323 // only the below draws should remain, since they're
324 canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
325 canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
326 });
327
328 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
329 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
330
331 EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
332 << "Recording must not have rejected ops, in order for this test to be valid";
333
334 AvoidOverdrawBitmapsTestRenderer renderer;
335 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
sergeyva82ffc52016-04-04 17:12:04 -0700336 EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
Chris Craik80d2ade2016-03-28 12:54:07 -0700337}
338
Chris Craik3a5811b2016-03-22 15:03:08 -0700339RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
Chris Craik93e53e02015-12-17 18:42:44 -0800340 class ClippedMergingTestRenderer : public TestRendererBase {
341 public:
342 void onMergedBitmapOps(const MergedBakedOpList& opList) override {
343 EXPECT_EQ(0, mIndex);
344 mIndex += opList.count;
345 EXPECT_EQ(4u, opList.count);
346 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
347 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
348 opList.clipSideFlags);
349 }
350 };
351 auto node = TestUtils::createNode(0, 0, 100, 100,
352 [](RenderProperties& props, TestCanvas& canvas) {
353 SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
354
355 // left side clipped (to inset left half)
356 canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
357 canvas.drawBitmap(bitmap, 0, 40, nullptr);
358
359 // top side clipped (to inset top half)
360 canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
361 canvas.drawBitmap(bitmap, 40, 0, nullptr);
362
363 // right side clipped (to inset right half)
364 canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
365 canvas.drawBitmap(bitmap, 80, 40, nullptr);
366
367 // bottom not clipped, just abutting (inset bottom half)
368 canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
369 canvas.drawBitmap(bitmap, 40, 70, nullptr);
370 });
371
Chris Craikf158b492016-01-12 14:45:08 -0800372 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -0700373 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik93e53e02015-12-17 18:42:44 -0800374 ClippedMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800375 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik93e53e02015-12-17 18:42:44 -0800376 EXPECT_EQ(4, renderer.getIndex());
377}
378
Chris Craik3a5811b2016-03-22 15:03:08 -0700379RENDERTHREAD_TEST(FrameBuilder, textMerging) {
Chris Craikd7448e62015-12-15 10:34:36 -0800380 class TextMergingTestRenderer : public TestRendererBase {
381 public:
382 void onMergedTextOps(const MergedBakedOpList& opList) override {
383 EXPECT_EQ(0, mIndex);
384 mIndex += opList.count;
385 EXPECT_EQ(2u, opList.count);
386 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
387 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
388 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
389 }
390 };
391 auto node = TestUtils::createNode(0, 0, 400, 400,
392 [](RenderProperties& props, TestCanvas& canvas) {
393 SkPaint paint;
394 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
395 paint.setAntiAlias(true);
396 paint.setTextSize(50);
sergeyvdccca442016-03-21 15:38:21 -0700397 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
398 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
Chris Craikd7448e62015-12-15 10:34:36 -0800399 });
Chris Craikf158b492016-01-12 14:45:08 -0800400 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -0700401 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikd7448e62015-12-15 10:34:36 -0800402 TextMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800403 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd7448e62015-12-15 10:34:36 -0800404 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
405}
406
Chris Craik3a5811b2016-03-22 15:03:08 -0700407RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
Chris Craika1717272015-11-19 13:02:43 -0800408 const int LOOPS = 5;
409 class TextStrikethroughTestRenderer : public TestRendererBase {
410 public:
411 void onRectOp(const RectOp& op, const BakedOpState& state) override {
412 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
413 }
Chris Craik15c3f192015-12-03 12:16:56 -0800414 void onMergedTextOps(const MergedBakedOpList& opList) override {
415 EXPECT_EQ(0, mIndex);
416 mIndex += opList.count;
417 EXPECT_EQ(5u, opList.count);
Chris Craika1717272015-11-19 13:02:43 -0800418 }
419 };
Chris Craik8d1f2122015-11-24 16:40:09 -0800420 auto node = TestUtils::createNode(0, 0, 200, 2000,
421 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craika1717272015-11-19 13:02:43 -0800422 SkPaint textPaint;
423 textPaint.setAntiAlias(true);
424 textPaint.setTextSize(20);
425 textPaint.setStrikeThruText(true);
Chris Craik42a54072015-11-24 11:41:54 -0800426 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Chris Craika1717272015-11-19 13:02:43 -0800427 for (int i = 0; i < LOOPS; i++) {
sergeyvdccca442016-03-21 15:38:21 -0700428 TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
Chris Craika1717272015-11-19 13:02:43 -0800429 }
430 });
Chris Craikf158b492016-01-12 14:45:08 -0800431 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
Chris Craik3a5811b2016-03-22 15:03:08 -0700432 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craika1717272015-11-19 13:02:43 -0800433 TextStrikethroughTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800434 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800435 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craikd7448e62015-12-15 10:34:36 -0800436 << "Expect number of ops = 2 * loop count";
Chris Craikb565df12015-10-05 13:00:52 -0700437}
438
Chris Craik7c02cab2016-03-16 17:15:12 -0700439static auto styles = {
440 SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
441
Chris Craik3a5811b2016-03-22 15:03:08 -0700442RENDERTHREAD_TEST(FrameBuilder, textStyle) {
Chris Craik7c02cab2016-03-16 17:15:12 -0700443 class TextStyleTestRenderer : public TestRendererBase {
444 public:
445 void onMergedTextOps(const MergedBakedOpList& opList) override {
446 ASSERT_EQ(0, mIndex);
447 ASSERT_EQ(3u, opList.count);
448 mIndex += opList.count;
449
450 int index = 0;
451 for (auto style : styles) {
452 auto state = opList.states[index++];
453 ASSERT_EQ(style, state->op->paint->getStyle())
454 << "Remainder of validation relies upon stable merged order";
455 ASSERT_EQ(0, state->computedState.clipSideFlags)
456 << "Clipped bounds validation requires unclipped ops";
457 }
458
459 Rect fill = opList.states[0]->computedState.clippedBounds;
460 Rect stroke = opList.states[1]->computedState.clippedBounds;
461 EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
462 << "Stroke+Fill should be same as stroke";
463
464 EXPECT_TRUE(stroke.contains(fill));
465 EXPECT_FALSE(fill.contains(stroke));
466
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400467 // outset by half the stroke width
Chris Craik7c02cab2016-03-16 17:15:12 -0700468 Rect outsetFill(fill);
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400469 outsetFill.outset(5);
Chris Craik7c02cab2016-03-16 17:15:12 -0700470 EXPECT_EQ(stroke, outsetFill);
471 }
472 };
473 auto node = TestUtils::createNode(0, 0, 400, 400,
474 [](RenderProperties& props, TestCanvas& canvas) {
475 SkPaint paint;
476 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
477 paint.setAntiAlias(true);
478 paint.setTextSize(50);
479 paint.setStrokeWidth(10);
480
481 // draw 3 copies of the same text overlapping, each with a different style.
482 // They'll get merged, but with
483 for (auto style : styles) {
484 paint.setStyle(style);
sergeyvdccca442016-03-21 15:38:21 -0700485 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
Chris Craik7c02cab2016-03-16 17:15:12 -0700486 }
487 });
488 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -0700489 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik7c02cab2016-03-16 17:15:12 -0700490 TextStyleTestRenderer renderer;
491 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
492 EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
493}
494
Chris Craikaafb01d2016-03-25 18:34:11 -0700495RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
496 class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800497 public:
498 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
499 EXPECT_EQ(0, mIndex++);
Chris Craike4db79d2015-12-22 16:32:23 -0800500 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800501 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
502
503 Matrix4 expected;
504 expected.loadTranslate(5, 5, 0);
505 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
506 }
507 };
508
509 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
Chris Craik243e85b2016-03-25 15:26:11 -0700510 SkMatrix::MakeTrans(5, 5));
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800511
512 auto node = TestUtils::createNode(0, 0, 200, 200,
513 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500514 canvas.save(SaveFlags::MatrixClip);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800515 canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
516 canvas.drawLayer(layerUpdater.get());
517 canvas.restore();
518 });
Chris Craikf158b492016-01-12 14:45:08 -0800519 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700520 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikaafb01d2016-03-25 18:34:11 -0700521 TextureLayerClipLocalMatrixTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800522 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800523 EXPECT_EQ(1, renderer.getIndex());
524}
525
Chris Craikaafb01d2016-03-25 18:34:11 -0700526RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
527 class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
528 public:
529 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
530 EXPECT_EQ(0, mIndex++);
531
532 Matrix4 expected;
533 expected.loadTranslate(35, 45, 0);
534 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
535 }
536 };
537
538 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
539 SkMatrix::MakeTrans(5, 5));
540
541 auto node = TestUtils::createNode(0, 0, 200, 200,
542 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
543 canvas.save(SaveFlags::MatrixClip);
544 canvas.translate(30, 40);
545 canvas.drawLayer(layerUpdater.get());
546 canvas.restore();
547 });
548
549 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
550 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
551 TextureLayerCombineMatricesTestRenderer renderer;
552 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
553 EXPECT_EQ(1, renderer.getIndex());
554}
555
556RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
557 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
558 SkMatrix::MakeTrans(5, 5));
559 layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
560
561 auto node = TestUtils::createNode(0, 0, 200, 200,
562 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
563 canvas.drawLayer(layerUpdater.get());
564 });
565 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
566 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
567 FailRenderer renderer;
568 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
569}
570
Chris Craik3a5811b2016-03-22 15:03:08 -0700571RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
Chris Craik223e3b62016-03-10 10:27:38 -0800572 class FunctorTestRenderer : public TestRendererBase {
573 public:
574 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
575 EXPECT_EQ(0, mIndex++);
576 }
577 };
578 Functor noopFunctor;
579
580 // 1 million pixel tall view, scrolled down 80%
581 auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
582 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
583 canvas.translate(0, -800000);
584 canvas.callDrawGLFunction(&noopFunctor);
585 });
586
587 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700588 TestUtils::createSyncedNodeList(scrolledFunctorView),
589 sLightGeometry, Caches::getInstance());
Chris Craik223e3b62016-03-10 10:27:38 -0800590 FunctorTestRenderer renderer;
591 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
592 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
593}
594
Chris Craika2048482016-03-25 14:17:49 -0700595RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
596 class ColorTestRenderer : public TestRendererBase {
597 public:
598 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
599 EXPECT_EQ(0, mIndex++);
600 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
601 << "Color op should be expanded to bounds of surrounding";
602 }
603 };
604
605 auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
606 [](RenderProperties& props, RecordingCanvas& canvas) {
607 props.setClipToBounds(false);
608 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
609 });
610
611 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
612 TestUtils::createSyncedNodeList(unclippedColorView),
613 sLightGeometry, Caches::getInstance());
614 ColorTestRenderer renderer;
615 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
616 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
617}
618
619TEST(FrameBuilder, renderNode) {
Chris Craikd3daa312015-11-06 10:59:56 -0800620 class RenderNodeTestRenderer : public TestRendererBase {
621 public:
622 void onRectOp(const RectOp& op, const BakedOpState& state) override {
623 switch(mIndex++) {
624 case 0:
Chris Craik5430ab22015-12-10 16:28:16 -0800625 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
Chris Craikd3daa312015-11-06 10:59:56 -0800626 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
627 break;
628 case 1:
629 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
630 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
631 break;
632 default:
633 ADD_FAILURE();
634 }
635 }
636 };
637
Chris Craik8d1f2122015-11-24 16:40:09 -0800638 auto child = TestUtils::createNode(10, 10, 110, 110,
639 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700640 SkPaint paint;
641 paint.setColor(SK_ColorWHITE);
642 canvas.drawRect(0, 0, 100, 100, paint);
643 });
644
Chris Craik8d1f2122015-11-24 16:40:09 -0800645 auto parent = TestUtils::createNode(0, 0, 200, 200,
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800646 [&child](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700647 SkPaint paint;
648 paint.setColor(SK_ColorDKGRAY);
649 canvas.drawRect(0, 0, 200, 200, paint);
Chris Craikb565df12015-10-05 13:00:52 -0700650
Florin Malitaeecff562015-12-21 10:43:01 -0500651 canvas.save(SaveFlags::MatrixClip);
Chris Craikddf22152015-10-14 17:42:47 -0700652 canvas.translate(40, 40);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800653 canvas.drawRenderNode(child.get());
Chris Craikddf22152015-10-14 17:42:47 -0700654 canvas.restore();
Chris Craikb565df12015-10-05 13:00:52 -0700655 });
656
Chris Craikf158b492016-01-12 14:45:08 -0800657 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700658 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700659 RenderNodeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800660 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik223e3b62016-03-10 10:27:38 -0800661 EXPECT_EQ(2, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700662}
663
Chris Craik3a5811b2016-03-22 15:03:08 -0700664RENDERTHREAD_TEST(FrameBuilder, clipped) {
Chris Craikd3daa312015-11-06 10:59:56 -0800665 class ClippedTestRenderer : public TestRendererBase {
666 public:
667 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
668 EXPECT_EQ(0, mIndex++);
669 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800670 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800671 EXPECT_TRUE(state.computedState.transform.isIdentity());
672 }
673 };
674
Chris Craik8d1f2122015-11-24 16:40:09 -0800675 auto node = TestUtils::createNode(0, 0, 200, 200,
676 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700677 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
678 canvas.drawBitmap(bitmap, 0, 0, nullptr);
679 });
Chris Craikddf22152015-10-14 17:42:47 -0700680
Chris Craikf158b492016-01-12 14:45:08 -0800681 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
Chris Craik0b7e8242015-10-28 16:50:44 -0700682 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
Chris Craik3a5811b2016-03-22 15:03:08 -0700683 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700684 ClippedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800685 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikddf22152015-10-14 17:42:47 -0700686}
687
Chris Craik3a5811b2016-03-22 15:03:08 -0700688RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800689 class SaveLayerSimpleTestRenderer : public TestRendererBase {
690 public:
691 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
692 EXPECT_EQ(0, mIndex++);
693 EXPECT_EQ(180u, width);
694 EXPECT_EQ(180u, height);
695 return nullptr;
696 }
697 void endLayer() override {
698 EXPECT_EQ(2, mIndex++);
699 }
700 void onRectOp(const RectOp& op, const BakedOpState& state) override {
701 EXPECT_EQ(1, mIndex++);
702 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
Chris Craik5430ab22015-12-10 16:28:16 -0800703 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800704 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800705
706 Matrix4 expectedTransform;
707 expectedTransform.loadTranslate(-10, -10, 0);
708 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
709 }
710 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
711 EXPECT_EQ(3, mIndex++);
712 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800713 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800714 EXPECT_TRUE(state.computedState.transform.isIdentity());
715 }
Chris Craik74af6e22016-04-05 13:18:56 -0700716 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
717 EXPECT_EQ(4, mIndex++);
718 EXPECT_EQ(nullptr, offscreenBuffer);
719 }
Chris Craikd3daa312015-11-06 10:59:56 -0800720 };
721
Chris Craik8d1f2122015-11-24 16:40:09 -0800722 auto node = TestUtils::createNode(0, 0, 200, 200,
723 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500724 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700725 canvas.drawRect(10, 10, 190, 190, SkPaint());
726 canvas.restore();
727 });
Chris Craikf158b492016-01-12 14:45:08 -0800728 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700729 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700730 SaveLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800731 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -0700732 EXPECT_EQ(5, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700733}
Chris Craik6fe991e52015-10-20 09:39:42 -0700734
Chris Craik3a5811b2016-03-22 15:03:08 -0700735RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
Chris Craikd3daa312015-11-06 10:59:56 -0800736 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
737 * - startTemporaryLayer2, rect2 endLayer2
738 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
739 * - startFrame, layerOp1, endFrame
740 */
741 class SaveLayerNestedTestRenderer : public TestRendererBase {
742 public:
743 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
744 const int index = mIndex++;
745 if (index == 0) {
746 EXPECT_EQ(400u, width);
747 EXPECT_EQ(400u, height);
748 return (OffscreenBuffer*) 0x400;
749 } else if (index == 3) {
750 EXPECT_EQ(800u, width);
751 EXPECT_EQ(800u, height);
752 return (OffscreenBuffer*) 0x800;
753 } else { ADD_FAILURE(); }
754 return (OffscreenBuffer*) nullptr;
755 }
756 void endLayer() override {
757 int index = mIndex++;
758 EXPECT_TRUE(index == 2 || index == 6);
759 }
Chris Craik98787e62015-11-13 10:55:30 -0800760 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800761 EXPECT_EQ(7, mIndex++);
762 }
Chris Craike4db79d2015-12-22 16:32:23 -0800763 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800764 EXPECT_EQ(9, mIndex++);
765 }
766 void onRectOp(const RectOp& op, const BakedOpState& state) override {
767 const int index = mIndex++;
768 if (index == 1) {
Chris Craik5430ab22015-12-10 16:28:16 -0800769 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
Chris Craikd3daa312015-11-06 10:59:56 -0800770 } else if (index == 4) {
Chris Craik5430ab22015-12-10 16:28:16 -0800771 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
Chris Craikd3daa312015-11-06 10:59:56 -0800772 } else { ADD_FAILURE(); }
773 }
774 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
775 const int index = mIndex++;
776 if (index == 5) {
777 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800778 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
Chris Craikd3daa312015-11-06 10:59:56 -0800779 } else if (index == 8) {
780 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800781 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
Chris Craikd3daa312015-11-06 10:59:56 -0800782 } else { ADD_FAILURE(); }
783 }
Chris Craik74af6e22016-04-05 13:18:56 -0700784 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
785 const int index = mIndex++;
786 // order isn't important, but we need to see both
787 if (index == 10) {
788 EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
789 } else if (index == 11) {
790 EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
791 } else { ADD_FAILURE(); }
792 }
Chris Craikd3daa312015-11-06 10:59:56 -0800793 };
794
Chris Craik8d1f2122015-11-24 16:40:09 -0800795 auto node = TestUtils::createNode(0, 0, 800, 800,
796 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500797 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700798 {
799 canvas.drawRect(0, 0, 800, 800, SkPaint());
Florin Malitaeecff562015-12-21 10:43:01 -0500800 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700801 {
802 canvas.drawRect(0, 0, 400, 400, SkPaint());
803 }
804 canvas.restore();
805 }
806 canvas.restore();
807 });
808
Chris Craikf158b492016-01-12 14:45:08 -0800809 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
Chris Craik3a5811b2016-03-22 15:03:08 -0700810 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700811 SaveLayerNestedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800812 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -0700813 EXPECT_EQ(12, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700814}
Chris Craik6fe991e52015-10-20 09:39:42 -0700815
Chris Craik3a5811b2016-03-22 15:03:08 -0700816RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800817 auto node = TestUtils::createNode(0, 0, 200, 200,
818 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500819 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700820 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
Florin Malitaeecff562015-12-21 10:43:01 -0500821 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700822
823 // draw within save layer may still be recorded, but shouldn't be drawn
824 canvas.drawRect(200, 200, 400, 400, SkPaint());
825
826 canvas.restore();
827 canvas.restore();
828 });
Chris Craikf158b492016-01-12 14:45:08 -0800829 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700830 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik6fe991e52015-10-20 09:39:42 -0700831
Chris Craik5854b342015-10-26 15:49:56 -0700832 FailRenderer renderer;
Chris Craik6fe991e52015-10-20 09:39:42 -0700833 // should see no ops, even within the layer, since the layer should be rejected
Chris Craikf158b492016-01-12 14:45:08 -0800834 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700835}
836
Chris Craik3a5811b2016-03-22 15:03:08 -0700837RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800838 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
839 public:
840 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
841 EXPECT_EQ(0, mIndex++);
842 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800843 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800844 EXPECT_TRUE(state.computedState.transform.isIdentity());
845 }
846 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
847 EXPECT_EQ(1, mIndex++);
848 ASSERT_NE(nullptr, op.paint);
849 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
850 }
851 void onRectOp(const RectOp& op, const BakedOpState& state) override {
852 EXPECT_EQ(2, mIndex++);
853 EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
854 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
855 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
856 EXPECT_TRUE(state.computedState.transform.isIdentity());
857 }
858 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
859 EXPECT_EQ(3, mIndex++);
860 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800861 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800862 EXPECT_TRUE(state.computedState.transform.isIdentity());
863 }
864 };
865
866 auto node = TestUtils::createNode(0, 0, 200, 200,
867 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500868 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
Chris Craikb87eadd2016-01-06 09:16:05 -0800869 canvas.drawRect(0, 0, 200, 200, SkPaint());
870 canvas.restore();
871 });
Chris Craikf158b492016-01-12 14:45:08 -0800872 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700873 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800874 SaveLayerUnclippedSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800875 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800876 EXPECT_EQ(4, renderer.getIndex());
877}
878
Chris Craik3a5811b2016-03-22 15:03:08 -0700879RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800880 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
881 public:
882 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
883 int index = mIndex++;
884 EXPECT_GT(4, index);
885 EXPECT_EQ(5, op.unmappedBounds.getWidth());
886 EXPECT_EQ(5, op.unmappedBounds.getHeight());
887 if (index == 0) {
888 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
889 } else if (index == 1) {
890 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
891 } else if (index == 2) {
892 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
893 } else if (index == 3) {
894 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
895 }
896 }
897 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
898 EXPECT_EQ(4, mIndex++);
899 ASSERT_EQ(op.vertexCount, 16u);
900 for (size_t i = 0; i < op.vertexCount; i++) {
901 auto v = op.vertices[i];
902 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
903 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
904 }
905 }
906 void onRectOp(const RectOp& op, const BakedOpState& state) override {
907 EXPECT_EQ(5, mIndex++);
908 }
909 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
910 EXPECT_LT(5, mIndex++);
911 }
912 };
913
914 auto node = TestUtils::createNode(0, 0, 200, 200,
915 [](RenderProperties& props, RecordingCanvas& canvas) {
916
Florin Malitaeecff562015-12-21 10:43:01 -0500917 int restoreTo = canvas.save(SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800918 canvas.scale(2, 2);
Florin Malitaeecff562015-12-21 10:43:01 -0500919 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
920 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
921 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
922 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800923 canvas.drawRect(0, 0, 100, 100, SkPaint());
924 canvas.restoreToCount(restoreTo);
925 });
Chris Craikf158b492016-01-12 14:45:08 -0800926 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700927 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800928 SaveLayerUnclippedMergedClearsTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800929 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800930 EXPECT_EQ(10, renderer.getIndex())
931 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
932}
933
Chris Craik3a5811b2016-03-22 15:03:08 -0700934RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
Chris Craik4876de12016-02-25 16:54:08 -0800935 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
936 public:
937 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
938 EXPECT_EQ(0, mIndex++);
939 }
940 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
941 EXPECT_EQ(1, mIndex++);
942 ASSERT_NE(nullptr, op.paint);
943 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
944 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
945 << "Expect dirty rect as clip";
946 ASSERT_NE(nullptr, state.computedState.clipState);
947 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
948 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
949 }
950 void onRectOp(const RectOp& op, const BakedOpState& state) override {
951 EXPECT_EQ(2, mIndex++);
952 }
953 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
954 EXPECT_EQ(3, mIndex++);
955 }
956 };
957
958 auto node = TestUtils::createNode(0, 0, 200, 200,
959 [](RenderProperties& props, RecordingCanvas& canvas) {
960 // save smaller than clip, so we get unclipped behavior
961 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
962 canvas.drawRect(0, 0, 200, 200, SkPaint());
963 canvas.restore();
964 });
965
966 // draw with partial screen dirty, and assert we see that rect later
967 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700968 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800969 SaveLayerUnclippedClearClipTestRenderer renderer;
970 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
971 EXPECT_EQ(4, renderer.getIndex());
972}
973
Chris Craik3a5811b2016-03-22 15:03:08 -0700974RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
Chris Craik4876de12016-02-25 16:54:08 -0800975 auto node = TestUtils::createNode(0, 0, 200, 200,
976 [](RenderProperties& props, RecordingCanvas& canvas) {
977 // unclipped savelayer + rect both in area that won't intersect with dirty
978 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
979 canvas.drawRect(100, 100, 200, 200, SkPaint());
980 canvas.restore();
981 });
982
983 // draw with partial screen dirty that doesn't intersect with savelayer
984 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700985 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800986 FailRenderer renderer;
987 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
988}
989
Chris Craikb87eadd2016-01-06 09:16:05 -0800990/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
991 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
992 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
993 */
Chris Craik3a5811b2016-03-22 15:03:08 -0700994RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800995 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
996 public:
997 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
998 EXPECT_EQ(0, mIndex++); // savelayer first
999 return (OffscreenBuffer*)0xabcd;
1000 }
1001 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
1002 int index = mIndex++;
1003 EXPECT_TRUE(index == 1 || index == 7);
1004 }
1005 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
1006 int index = mIndex++;
1007 EXPECT_TRUE(index == 2 || index == 8);
1008 }
1009 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1010 EXPECT_EQ(3, mIndex++);
1011 Matrix4 expected;
1012 expected.loadTranslate(-100, -100, 0);
1013 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
1014 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
1015 }
1016 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1017 int index = mIndex++;
1018 EXPECT_TRUE(index == 4 || index == 10);
1019 }
1020 void endLayer() override {
1021 EXPECT_EQ(5, mIndex++);
1022 }
1023 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1024 EXPECT_EQ(6, mIndex++);
1025 }
1026 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1027 EXPECT_EQ(9, mIndex++);
Chris Craik74af6e22016-04-05 13:18:56 -07001028 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
Chris Craikb87eadd2016-01-06 09:16:05 -08001029 }
1030 void endFrame(const Rect& repaintRect) override {
1031 EXPECT_EQ(11, mIndex++);
1032 }
Chris Craik74af6e22016-04-05 13:18:56 -07001033 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1034 EXPECT_EQ(12, mIndex++);
1035 EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
1036 }
Chris Craikb87eadd2016-01-06 09:16:05 -08001037 };
1038
1039 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
1040 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -05001041 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
1042 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
1043 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
Chris Craikb87eadd2016-01-06 09:16:05 -08001044 canvas.drawRect(200, 200, 300, 300, SkPaint());
1045 canvas.restore();
1046 canvas.restore();
1047 canvas.restore();
1048 });
Chris Craikf158b492016-01-12 14:45:08 -08001049 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
Chris Craik3a5811b2016-03-22 15:03:08 -07001050 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -08001051 SaveLayerUnclippedComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001052 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -07001053 EXPECT_EQ(13, renderer.getIndex());
Chris Craikb87eadd2016-01-06 09:16:05 -08001054}
1055
Chris Craikf158b492016-01-12 14:45:08 -08001056RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -08001057 class HwLayerSimpleTestRenderer : public TestRendererBase {
1058 public:
Chris Craik98787e62015-11-13 10:55:30 -08001059 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001060 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001061 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1062 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1063 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
Chris Craikd3daa312015-11-06 10:59:56 -08001064 }
1065 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1066 EXPECT_EQ(1, mIndex++);
1067
1068 EXPECT_TRUE(state.computedState.transform.isIdentity())
1069 << "Transform should be reset within layer";
1070
Chris Craike4db79d2015-12-22 16:32:23 -08001071 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
Chris Craikd3daa312015-11-06 10:59:56 -08001072 << "Damage rect should be used to clip layer content";
1073 }
1074 void endLayer() override {
1075 EXPECT_EQ(2, mIndex++);
1076 }
Chris Craik98787e62015-11-13 10:55:30 -08001077 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001078 EXPECT_EQ(3, mIndex++);
1079 }
1080 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1081 EXPECT_EQ(4, mIndex++);
1082 }
Chris Craike4db79d2015-12-22 16:32:23 -08001083 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001084 EXPECT_EQ(5, mIndex++);
1085 }
1086 };
1087
Chris Craik8d1f2122015-11-24 16:40:09 -08001088 auto node = TestUtils::createNode(10, 10, 110, 110,
John Reck16c9d6a2015-11-17 15:51:08 -08001089 [](RenderProperties& props, RecordingCanvas& canvas) {
1090 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001091 SkPaint paint;
1092 paint.setColor(SK_ColorWHITE);
1093 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001094 });
Chris Craik98787e62015-11-13 10:55:30 -08001095 OffscreenBuffer** layerHandle = node->getLayerHandle();
Chris Craik0b7e8242015-10-28 16:50:44 -07001096
Chris Craik98787e62015-11-13 10:55:30 -08001097 // create RenderNode's layer here in same way prepareTree would
1098 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1099 *layerHandle = &layer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001100
John Reck7db5ffb2016-01-15 13:17:09 -08001101 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
Chris Craik0b7e8242015-10-28 16:50:44 -07001102
1103 // only enqueue partial damage
Chris Craik98787e62015-11-13 10:55:30 -08001104 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001105 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1106
Chris Craikf158b492016-01-12 14:45:08 -08001107 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001108 syncedNodeList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -07001109 HwLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001110 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001111 EXPECT_EQ(6, renderer.getIndex());
1112
1113 // clean up layer pointer, so we can safely destruct RenderNode
Chris Craik98787e62015-11-13 10:55:30 -08001114 *layerHandle = nullptr;
Chris Craik0b7e8242015-10-28 16:50:44 -07001115}
1116
Chris Craikf158b492016-01-12 14:45:08 -08001117RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
Chris Craikd3daa312015-11-06 10:59:56 -08001118 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
1119 * - startRepaintLayer(child), rect(grey), endLayer
1120 * - startTemporaryLayer, drawLayer(child), endLayer
1121 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
1122 * - startFrame, drawLayer(parent), endLayerb
1123 */
1124 class HwLayerComplexTestRenderer : public TestRendererBase {
1125 public:
1126 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1127 EXPECT_EQ(3, mIndex++); // savelayer first
1128 return (OffscreenBuffer*)0xabcd;
1129 }
Chris Craik98787e62015-11-13 10:55:30 -08001130 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001131 int index = mIndex++;
1132 if (index == 0) {
1133 // starting inner layer
Chris Craik98787e62015-11-13 10:55:30 -08001134 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1135 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001136 } else if (index == 6) {
1137 // starting outer layer
Chris Craik98787e62015-11-13 10:55:30 -08001138 EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
1139 EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001140 } else { ADD_FAILURE(); }
1141 }
1142 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1143 int index = mIndex++;
1144 if (index == 1) {
1145 // inner layer's rect (white)
1146 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1147 } else if (index == 7) {
1148 // outer layer's rect (grey)
1149 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1150 } else { ADD_FAILURE(); }
1151 }
1152 void endLayer() override {
1153 int index = mIndex++;
1154 EXPECT_TRUE(index == 2 || index == 5 || index == 9);
1155 }
Chris Craik98787e62015-11-13 10:55:30 -08001156 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001157 EXPECT_EQ(10, mIndex++);
1158 }
1159 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
Chris Craik98787e62015-11-13 10:55:30 -08001160 OffscreenBuffer* layer = *op.layerHandle;
Chris Craikd3daa312015-11-06 10:59:56 -08001161 int index = mIndex++;
1162 if (index == 4) {
Chris Craik98787e62015-11-13 10:55:30 -08001163 EXPECT_EQ(100u, layer->viewportWidth);
1164 EXPECT_EQ(100u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001165 } else if (index == 8) {
1166 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1167 } else if (index == 11) {
Chris Craik98787e62015-11-13 10:55:30 -08001168 EXPECT_EQ(200u, layer->viewportWidth);
1169 EXPECT_EQ(200u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001170 } else { ADD_FAILURE(); }
1171 }
Chris Craike4db79d2015-12-22 16:32:23 -08001172 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001173 EXPECT_EQ(12, mIndex++);
1174 }
Chris Craik74af6e22016-04-05 13:18:56 -07001175 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1176 EXPECT_EQ(13, mIndex++);
1177 }
Chris Craikd3daa312015-11-06 10:59:56 -08001178 };
1179
John Reck16c9d6a2015-11-17 15:51:08 -08001180 auto child = TestUtils::createNode(50, 50, 150, 150,
1181 [](RenderProperties& props, RecordingCanvas& canvas) {
1182 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001183 SkPaint paint;
1184 paint.setColor(SK_ColorWHITE);
1185 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001186 });
Chris Craik98787e62015-11-13 10:55:30 -08001187 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1188 *(child->getLayerHandle()) = &childLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001189
1190 RenderNode* childPtr = child.get();
John Reck16c9d6a2015-11-17 15:51:08 -08001191 auto parent = TestUtils::createNode(0, 0, 200, 200,
1192 [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1193 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001194 SkPaint paint;
1195 paint.setColor(SK_ColorDKGRAY);
1196 canvas.drawRect(0, 0, 200, 200, paint);
1197
Florin Malitaeecff562015-12-21 10:43:01 -05001198 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001199 canvas.drawRenderNode(childPtr);
1200 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001201 });
Chris Craik98787e62015-11-13 10:55:30 -08001202 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1203 *(parent->getLayerHandle()) = &parentLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001204
John Reck7db5ffb2016-01-15 13:17:09 -08001205 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik0b7e8242015-10-28 16:50:44 -07001206
Chris Craik98787e62015-11-13 10:55:30 -08001207 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001208 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1209 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1210
Chris Craikf158b492016-01-12 14:45:08 -08001211 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001212 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -07001213 HwLayerComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001214 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -07001215 EXPECT_EQ(14, renderer.getIndex());
Chris Craik0b7e8242015-10-28 16:50:44 -07001216
1217 // clean up layer pointers, so we can safely destruct RenderNodes
1218 *(child->getLayerHandle()) = nullptr;
1219 *(parent->getLayerHandle()) = nullptr;
1220}
1221
Chris Craik6246d2782016-03-29 15:01:41 -07001222
1223RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
1224 class BuildLayerTestRenderer : public TestRendererBase {
1225 public:
1226 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1227 EXPECT_EQ(0, mIndex++);
1228 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1229 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1230 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1231 }
1232 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
1233 EXPECT_EQ(1, mIndex++);
1234
1235 EXPECT_TRUE(state.computedState.transform.isIdentity())
1236 << "Transform should be reset within layer";
1237
1238 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1239 << "Damage rect should be used to clip layer content";
1240 }
1241 void endLayer() override {
1242 EXPECT_EQ(2, mIndex++);
1243 }
1244 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1245 ADD_FAILURE() << "Primary frame draw not expected in this test";
1246 }
1247 void endFrame(const Rect& repaintRect) override {
1248 ADD_FAILURE() << "Primary frame draw not expected in this test";
1249 }
1250 };
1251
1252 auto node = TestUtils::createNode(10, 10, 110, 110,
1253 [](RenderProperties& props, RecordingCanvas& canvas) {
1254 props.mutateLayerProperties().setType(LayerType::RenderLayer);
1255 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
1256 });
1257 OffscreenBuffer** layerHandle = node->getLayerHandle();
1258
1259 // create RenderNode's layer here in same way prepareTree would
1260 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1261 *layerHandle = &layer;
1262
1263 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
1264
1265 // only enqueue partial damage
1266 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1267 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1268
1269 // Draw, but pass empty node list, so no work is done for primary frame
1270 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
1271 sEmptyNodeList, sLightGeometry, Caches::getInstance());
1272 BuildLayerTestRenderer renderer;
1273 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1274 EXPECT_EQ(3, renderer.getIndex());
1275
1276 // clean up layer pointer, so we can safely destruct RenderNode
1277 *layerHandle = nullptr;
1278}
1279
Chris Craik161f54b2015-11-05 11:08:52 -08001280static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1281 SkPaint paint;
1282 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
1283 canvas->drawRect(0, 0, 100, 100, paint);
1284}
1285static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
John Reck16c9d6a2015-11-17 15:51:08 -08001286 auto node = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001287 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001288 drawOrderedRect(&canvas, expectedDrawOrder);
1289 });
1290 node->mutateStagingProperties().setTranslationZ(z);
1291 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1292 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1293}
Chris Craik3a5811b2016-03-22 15:03:08 -07001294RENDERTHREAD_TEST(FrameBuilder, zReorder) {
Chris Craikd3daa312015-11-06 10:59:56 -08001295 class ZReorderTestRenderer : public TestRendererBase {
1296 public:
1297 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1298 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1299 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1300 }
1301 };
1302
John Reck16c9d6a2015-11-17 15:51:08 -08001303 auto parent = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001304 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001305 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1306 drawOrderedRect(&canvas, 1);
1307 canvas.insertReorderBarrier(true);
1308 drawOrderedNode(&canvas, 6, 2.0f);
1309 drawOrderedRect(&canvas, 3);
1310 drawOrderedNode(&canvas, 4, 0.0f);
1311 drawOrderedRect(&canvas, 5);
1312 drawOrderedNode(&canvas, 2, -2.0f);
1313 drawOrderedNode(&canvas, 7, 2.0f);
1314 canvas.insertReorderBarrier(false);
1315 drawOrderedRect(&canvas, 8);
1316 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1317 });
Chris Craikf158b492016-01-12 14:45:08 -08001318 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001319 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik161f54b2015-11-05 11:08:52 -08001320 ZReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001321 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik161f54b2015-11-05 11:08:52 -08001322 EXPECT_EQ(10, renderer.getIndex());
1323};
1324
Chris Craik3a5811b2016-03-22 15:03:08 -07001325RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
Chris Craik8d1f2122015-11-24 16:40:09 -08001326 static const int scrollX = 5;
1327 static const int scrollY = 10;
1328 class ProjectionReorderTestRenderer : public TestRendererBase {
1329 public:
1330 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1331 const int index = mIndex++;
1332
1333 Matrix4 expectedMatrix;
1334 switch (index) {
1335 case 0:
1336 EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1337 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1338 expectedMatrix.loadIdentity();
Chris Craik678ff812016-03-01 13:27:54 -08001339 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001340 break;
1341 case 1:
1342 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1343 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
Chris Craik678ff812016-03-01 13:27:54 -08001344 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1345 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1346 EXPECT_EQ(Rect(-35, -30, 45, 50),
1347 Rect(state.computedState.localProjectionPathMask->getBounds()));
Chris Craik8d1f2122015-11-24 16:40:09 -08001348 break;
1349 case 2:
1350 EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1351 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1352 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
Chris Craik678ff812016-03-01 13:27:54 -08001353 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001354 break;
1355 default:
1356 ADD_FAILURE();
1357 }
Chris Craik678ff812016-03-01 13:27:54 -08001358 EXPECT_EQ(expectedMatrix, state.computedState.transform);
Chris Craik8d1f2122015-11-24 16:40:09 -08001359 }
1360 };
1361
1362 /**
1363 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1364 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1365 * draw, but because it is projected backwards, it's drawn in between B and C.
1366 *
1367 * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1368 * (which isn't affected by scroll).
1369 */
1370 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1371 [](RenderProperties& properties, RecordingCanvas& canvas) {
1372 properties.setProjectionReceiver(true);
1373 // scroll doesn't apply to background, so undone via translationX/Y
1374 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1375 properties.setTranslationX(scrollX);
1376 properties.setTranslationY(scrollY);
1377
1378 SkPaint paint;
1379 paint.setColor(SK_ColorWHITE);
1380 canvas.drawRect(0, 0, 100, 100, paint);
1381 });
1382 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1383 [](RenderProperties& properties, RecordingCanvas& canvas) {
1384 properties.setProjectBackwards(true);
1385 properties.setClipToBounds(false);
1386 SkPaint paint;
1387 paint.setColor(SK_ColorDKGRAY);
1388 canvas.drawRect(-10, -10, 60, 60, paint);
1389 });
1390 auto child = TestUtils::createNode(0, 50, 100, 100,
1391 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1392 SkPaint paint;
1393 paint.setColor(SK_ColorBLUE);
1394 canvas.drawRect(0, 0, 100, 50, paint);
1395 canvas.drawRenderNode(projectingRipple.get());
1396 });
1397 auto parent = TestUtils::createNode(0, 0, 100, 100,
1398 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
Chris Craik678ff812016-03-01 13:27:54 -08001399 // Set a rect outline for the projecting ripple to be masked against.
1400 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1401
Florin Malitaeecff562015-12-21 10:43:01 -05001402 canvas.save(SaveFlags::MatrixClip);
Chris Craik8d1f2122015-11-24 16:40:09 -08001403 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1404 canvas.drawRenderNode(receiverBackground.get());
1405 canvas.drawRenderNode(child.get());
1406 canvas.restore();
1407 });
1408
Chris Craikf158b492016-01-12 14:45:08 -08001409 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001410 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik8d1f2122015-11-24 16:40:09 -08001411 ProjectionReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001412 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8d1f2122015-11-24 16:40:09 -08001413 EXPECT_EQ(3, renderer.getIndex());
1414}
1415
Chris Craik678ff812016-03-01 13:27:54 -08001416RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1417 static const int scrollX = 5;
1418 static const int scrollY = 10;
1419 class ProjectionHwLayerTestRenderer : public TestRendererBase {
1420 public:
1421 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1422 EXPECT_EQ(0, mIndex++);
1423 }
1424 void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1425 EXPECT_EQ(1, mIndex++);
1426 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1427 }
1428 void endLayer() override {
1429 EXPECT_EQ(2, mIndex++);
1430 }
1431 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1432 EXPECT_EQ(3, mIndex++);
1433 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1434 }
1435 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1436 EXPECT_EQ(4, mIndex++);
1437 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1438 Matrix4 expected;
1439 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1440 EXPECT_EQ(expected, state.computedState.transform);
1441 EXPECT_EQ(Rect(-85, -80, 295, 300),
1442 Rect(state.computedState.localProjectionPathMask->getBounds()));
1443 }
1444 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1445 EXPECT_EQ(5, mIndex++);
1446 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1447 }
1448 };
1449 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1450 [](RenderProperties& properties, RecordingCanvas& canvas) {
1451 properties.setProjectionReceiver(true);
1452 // scroll doesn't apply to background, so undone via translationX/Y
1453 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1454 properties.setTranslationX(scrollX);
1455 properties.setTranslationY(scrollY);
1456
1457 canvas.drawRect(0, 0, 400, 400, SkPaint());
1458 });
1459 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1460 [](RenderProperties& properties, RecordingCanvas& canvas) {
1461 properties.setProjectBackwards(true);
1462 properties.setClipToBounds(false);
1463 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1464 });
1465 auto child = TestUtils::createNode(100, 100, 300, 300,
1466 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1467 properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1468 canvas.drawRenderNode(projectingRipple.get());
1469 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1470 });
1471 auto parent = TestUtils::createNode(0, 0, 400, 400,
1472 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1473 // Set a rect outline for the projecting ripple to be masked against.
1474 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1475 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1476 canvas.drawRenderNode(receiverBackground.get());
1477 canvas.drawRenderNode(child.get());
1478 });
1479
1480 OffscreenBuffer** layerHandle = child->getLayerHandle();
1481
1482 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1483 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1484 Matrix4 windowTransform;
1485 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1486 layer.setWindowTransform(windowTransform);
1487 *layerHandle = &layer;
1488
1489 auto syncedList = TestUtils::createSyncedNodeList(parent);
1490 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1491 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1492 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001493 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik678ff812016-03-01 13:27:54 -08001494 ProjectionHwLayerTestRenderer renderer;
1495 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1496 EXPECT_EQ(6, renderer.getIndex());
1497
1498 // clean up layer pointer, so we can safely destruct RenderNode
1499 *layerHandle = nullptr;
1500}
1501
Chris Craika748c082016-03-01 18:48:37 -08001502RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1503 static const int scrollX = 500000;
1504 static const int scrollY = 0;
1505 class ProjectionChildScrollTestRenderer : public TestRendererBase {
1506 public:
1507 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1508 EXPECT_EQ(0, mIndex++);
1509 EXPECT_TRUE(state.computedState.transform.isIdentity());
1510 }
1511 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1512 EXPECT_EQ(1, mIndex++);
1513 ASSERT_NE(nullptr, state.computedState.clipState);
1514 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1515 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1516 EXPECT_TRUE(state.computedState.transform.isIdentity());
1517 }
1518 };
1519 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1520 [](RenderProperties& properties, RecordingCanvas& canvas) {
1521 properties.setProjectionReceiver(true);
1522 canvas.drawRect(0, 0, 400, 400, SkPaint());
1523 });
1524 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1525 [](RenderProperties& properties, RecordingCanvas& canvas) {
1526 // scroll doesn't apply to background, so undone via translationX/Y
1527 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1528 properties.setTranslationX(scrollX);
1529 properties.setTranslationY(scrollY);
1530 properties.setProjectBackwards(true);
1531 properties.setClipToBounds(false);
1532 canvas.drawOval(0, 0, 200, 200, SkPaint());
1533 });
1534 auto child = TestUtils::createNode(0, 0, 400, 400,
1535 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1536 // Record time clip will be ignored by projectee
1537 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1538
1539 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1540 canvas.drawRenderNode(projectingRipple.get());
1541 });
1542 auto parent = TestUtils::createNode(0, 0, 400, 400,
1543 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1544 canvas.drawRenderNode(receiverBackground.get());
1545 canvas.drawRenderNode(child.get());
1546 });
1547
1548 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001549 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craika748c082016-03-01 18:48:37 -08001550 ProjectionChildScrollTestRenderer renderer;
1551 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1552 EXPECT_EQ(2, renderer.getIndex());
1553}
1554
Chris Craik98787e62015-11-13 10:55:30 -08001555// creates a 100x100 shadow casting node with provided translationZ
1556static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
John Reck16c9d6a2015-11-17 15:51:08 -08001557 return TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001558 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
John Reck16c9d6a2015-11-17 15:51:08 -08001559 properties.setTranslationZ(translationZ);
1560 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
Chris Craik98787e62015-11-13 10:55:30 -08001561 SkPaint paint;
1562 paint.setColor(SK_ColorWHITE);
1563 canvas.drawRect(0, 0, 100, 100, paint);
Chris Craik98787e62015-11-13 10:55:30 -08001564 });
1565}
1566
Chris Craik6e068c012016-01-15 16:15:30 -08001567RENDERTHREAD_TEST(FrameBuilder, shadow) {
Chris Craikd3daa312015-11-06 10:59:56 -08001568 class ShadowTestRenderer : public TestRendererBase {
1569 public:
1570 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1571 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001572 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
Chris Craik6e068c012016-01-15 16:15:30 -08001573 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1574 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
Chris Craik98787e62015-11-13 10:55:30 -08001575
1576 Matrix4 expectedZ;
1577 expectedZ.loadTranslate(0, 0, 5);
Chris Craik6e068c012016-01-15 16:15:30 -08001578 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
Chris Craikd3daa312015-11-06 10:59:56 -08001579 }
1580 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1581 EXPECT_EQ(1, mIndex++);
1582 }
1583 };
Chris Craik161f54b2015-11-05 11:08:52 -08001584
Chris Craik8d1f2122015-11-24 16:40:09 -08001585 auto parent = TestUtils::createNode(0, 0, 200, 200,
1586 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikd3daa312015-11-06 10:59:56 -08001587 canvas.insertReorderBarrier(true);
Chris Craik98787e62015-11-13 10:55:30 -08001588 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
Chris Craikd3daa312015-11-06 10:59:56 -08001589 });
1590
Chris Craikf158b492016-01-12 14:45:08 -08001591 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001592 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craikd3daa312015-11-06 10:59:56 -08001593 ShadowTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001594 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd3daa312015-11-06 10:59:56 -08001595 EXPECT_EQ(2, renderer.getIndex());
1596}
Chris Craik76caecf2015-11-02 19:17:45 -08001597
Chris Craik6e068c012016-01-15 16:15:30 -08001598RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001599 class ShadowSaveLayerTestRenderer : public TestRendererBase {
1600 public:
1601 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1602 EXPECT_EQ(0, mIndex++);
1603 return nullptr;
1604 }
1605 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1606 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001607 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1608 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
Chris Craik98787e62015-11-13 10:55:30 -08001609 }
1610 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1611 EXPECT_EQ(2, mIndex++);
1612 }
1613 void endLayer() override {
1614 EXPECT_EQ(3, mIndex++);
1615 }
1616 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1617 EXPECT_EQ(4, mIndex++);
1618 }
Chris Craik74af6e22016-04-05 13:18:56 -07001619 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1620 EXPECT_EQ(5, mIndex++);
1621 }
Chris Craik98787e62015-11-13 10:55:30 -08001622 };
1623
Chris Craik8d1f2122015-11-24 16:40:09 -08001624 auto parent = TestUtils::createNode(0, 0, 200, 200,
1625 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001626 // save/restore outside of reorderBarrier, so they don't get moved out of place
1627 canvas.translate(20, 10);
Florin Malitaeecff562015-12-21 10:43:01 -05001628 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001629 canvas.insertReorderBarrier(true);
1630 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1631 canvas.insertReorderBarrier(false);
1632 canvas.restoreToCount(count);
1633 });
1634
Chris Craikf158b492016-01-12 14:45:08 -08001635 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001636 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001637 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001638 ShadowSaveLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001639 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik74af6e22016-04-05 13:18:56 -07001640 EXPECT_EQ(6, renderer.getIndex());
Chris Craik98787e62015-11-13 10:55:30 -08001641}
1642
Chris Craikf158b492016-01-12 14:45:08 -08001643RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001644 class ShadowHwLayerTestRenderer : public TestRendererBase {
1645 public:
1646 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1647 EXPECT_EQ(0, mIndex++);
1648 }
1649 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1650 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001651 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1652 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1653 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
Chris Craik98787e62015-11-13 10:55:30 -08001654 }
1655 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1656 EXPECT_EQ(2, mIndex++);
1657 }
1658 void endLayer() override {
1659 EXPECT_EQ(3, mIndex++);
1660 }
1661 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1662 EXPECT_EQ(4, mIndex++);
1663 }
1664 };
1665
Chris Craik8d1f2122015-11-24 16:40:09 -08001666 auto parent = TestUtils::createNode(50, 60, 150, 160,
John Reck16c9d6a2015-11-17 15:51:08 -08001667 [](RenderProperties& props, RecordingCanvas& canvas) {
1668 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001669 canvas.insertReorderBarrier(true);
Florin Malitaeecff562015-12-21 10:43:01 -05001670 canvas.save(SaveFlags::MatrixClip);
Chris Craik98787e62015-11-13 10:55:30 -08001671 canvas.translate(20, 10);
1672 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1673 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001674 });
Chris Craik98787e62015-11-13 10:55:30 -08001675 OffscreenBuffer** layerHandle = parent->getLayerHandle();
1676
1677 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1678 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1679 Matrix4 windowTransform;
1680 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1681 layer.setWindowTransform(windowTransform);
1682 *layerHandle = &layer;
1683
John Reck7db5ffb2016-01-15 13:17:09 -08001684 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik98787e62015-11-13 10:55:30 -08001685 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1686 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
Chris Craikf158b492016-01-12 14:45:08 -08001687 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001688 syncedList,
Chris Craik3a5811b2016-03-22 15:03:08 -07001689 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001690 ShadowHwLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001691 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001692 EXPECT_EQ(5, renderer.getIndex());
1693
1694 // clean up layer pointer, so we can safely destruct RenderNode
1695 *layerHandle = nullptr;
1696}
1697
Chris Craik3a5811b2016-03-22 15:03:08 -07001698RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
Chris Craik98787e62015-11-13 10:55:30 -08001699 class ShadowLayeringTestRenderer : public TestRendererBase {
1700 public:
1701 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1702 int index = mIndex++;
1703 EXPECT_TRUE(index == 0 || index == 1);
1704 }
1705 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1706 int index = mIndex++;
1707 EXPECT_TRUE(index == 2 || index == 3);
1708 }
1709 };
Chris Craik8d1f2122015-11-24 16:40:09 -08001710 auto parent = TestUtils::createNode(0, 0, 200, 200,
1711 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001712 canvas.insertReorderBarrier(true);
1713 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1714 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1715 });
1716
Chris Craikf158b492016-01-12 14:45:08 -08001717 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001718 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001719 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001720 ShadowLayeringTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001721 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001722 EXPECT_EQ(4, renderer.getIndex());
1723}
1724
John Reck16c9d6a2015-11-17 15:51:08 -08001725static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
Chris Craik76caecf2015-11-02 19:17:45 -08001726 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
Chris Craikd3daa312015-11-06 10:59:56 -08001727 class PropertyTestRenderer : public TestRendererBase {
1728 public:
1729 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1730 : mCallback(callback) {}
1731 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1732 EXPECT_EQ(mIndex++, 0);
1733 mCallback(op, state);
1734 }
1735 std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1736 };
1737
John Reck16c9d6a2015-11-17 15:51:08 -08001738 auto node = TestUtils::createNode(0, 0, 100, 100,
1739 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1740 propSetupCallback(props);
Chris Craik76caecf2015-11-02 19:17:45 -08001741 SkPaint paint;
1742 paint.setColor(SK_ColorWHITE);
1743 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001744 });
Chris Craik76caecf2015-11-02 19:17:45 -08001745
Chris Craikf158b492016-01-12 14:45:08 -08001746 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001747 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik76caecf2015-11-02 19:17:45 -08001748 PropertyTestRenderer renderer(opValidateCallback);
Chris Craikf158b492016-01-12 14:45:08 -08001749 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik76caecf2015-11-02 19:17:45 -08001750 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1751}
1752
Chris Craik3a5811b2016-03-22 15:03:08 -07001753RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
Chris Craik76caecf2015-11-02 19:17:45 -08001754 testProperty([](RenderProperties& properties) {
1755 properties.setAlpha(0.5f);
1756 properties.setHasOverlappingRendering(false);
Chris Craik76caecf2015-11-02 19:17:45 -08001757 }, [](const RectOp& op, const BakedOpState& state) {
1758 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1759 });
1760}
1761
Chris Craik3a5811b2016-03-22 15:03:08 -07001762RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
Chris Craik76caecf2015-11-02 19:17:45 -08001763 testProperty([](RenderProperties& properties) {
1764 properties.setClipToBounds(true);
1765 properties.setClipBounds(Rect(10, 20, 300, 400));
Chris Craik76caecf2015-11-02 19:17:45 -08001766 }, [](const RectOp& op, const BakedOpState& state) {
1767 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1768 << "Clip rect should be intersection of node bounds and clip bounds";
1769 });
1770}
1771
Chris Craik3a5811b2016-03-22 15:03:08 -07001772RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001773 testProperty([](RenderProperties& properties) {
1774 properties.mutableRevealClip().set(true, 50, 50, 25);
Chris Craik76caecf2015-11-02 19:17:45 -08001775 }, [](const RectOp& op, const BakedOpState& state) {
1776 ASSERT_NE(nullptr, state.roundRectClipState);
1777 EXPECT_TRUE(state.roundRectClipState->highPriority);
1778 EXPECT_EQ(25, state.roundRectClipState->radius);
1779 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1780 });
1781}
1782
Chris Craik3a5811b2016-03-22 15:03:08 -07001783RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001784 testProperty([](RenderProperties& properties) {
1785 properties.mutableOutline().setShouldClip(true);
1786 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
Chris Craik76caecf2015-11-02 19:17:45 -08001787 }, [](const RectOp& op, const BakedOpState& state) {
1788 ASSERT_NE(nullptr, state.roundRectClipState);
1789 EXPECT_FALSE(state.roundRectClipState->highPriority);
1790 EXPECT_EQ(5, state.roundRectClipState->radius);
1791 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1792 });
1793}
1794
Chris Craik3a5811b2016-03-22 15:03:08 -07001795RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
Chris Craik76caecf2015-11-02 19:17:45 -08001796 testProperty([](RenderProperties& properties) {
1797 properties.setLeftTopRightBottom(10, 10, 110, 110);
1798
1799 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1800 properties.setStaticMatrix(&staticMatrix);
1801
1802 // ignored, since static overrides animation
1803 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1804 properties.setAnimationMatrix(&animationMatrix);
1805
1806 properties.setTranslationX(10);
1807 properties.setTranslationY(20);
1808 properties.setScaleX(0.5f);
1809 properties.setScaleY(0.7f);
Chris Craik76caecf2015-11-02 19:17:45 -08001810 }, [](const RectOp& op, const BakedOpState& state) {
1811 Matrix4 matrix;
1812 matrix.loadTranslate(10, 10, 0); // left, top
1813 matrix.scale(1.2f, 1.2f, 1); // static matrix
1814 // ignore animation matrix, since static overrides it
1815
1816 // translation xy
1817 matrix.translate(10, 20);
1818
1819 // scale xy (from default pivot - center)
1820 matrix.translate(50, 50);
1821 matrix.scale(0.5f, 0.7f, 1);
1822 matrix.translate(-50, -50);
1823 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1824 << "Op draw matrix must match expected combination of transformation properties";
1825 });
1826}
Chris Craik161f54b2015-11-05 11:08:52 -08001827
Chris Craik8ecf41c2015-11-16 10:27:59 -08001828struct SaveLayerAlphaData {
1829 uint32_t layerWidth = 0;
1830 uint32_t layerHeight = 0;
1831 Rect rectClippedBounds;
1832 Matrix4 rectMatrix;
1833};
1834/**
1835 * Constructs a view to hit the temporary layer alpha property implementation:
1836 * a) 0 < alpha < 1
1837 * b) too big for layer (larger than maxTextureSize)
1838 * c) overlapping rendering content
1839 * returning observed data about layer size and content clip/transform.
1840 *
1841 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1842 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1843 */
1844void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
John Reck16c9d6a2015-11-17 15:51:08 -08001845 std::function<void(RenderProperties&)> propSetupCallback) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001846 class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1847 public:
1848 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1849 : mOutData(outData) {}
1850
1851 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1852 EXPECT_EQ(0, mIndex++);
1853 mOutData->layerWidth = width;
1854 mOutData->layerHeight = height;
1855 return nullptr;
1856 }
1857 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1858 EXPECT_EQ(1, mIndex++);
1859
1860 mOutData->rectClippedBounds = state.computedState.clippedBounds;
1861 mOutData->rectMatrix = state.computedState.transform;
1862 }
1863 void endLayer() override {
1864 EXPECT_EQ(2, mIndex++);
1865 }
1866 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1867 EXPECT_EQ(3, mIndex++);
1868 }
Chris Craik74af6e22016-04-05 13:18:56 -07001869 void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
1870 EXPECT_EQ(4, mIndex++);
1871 }
Chris Craik8ecf41c2015-11-16 10:27:59 -08001872 private:
1873 SaveLayerAlphaData* mOutData;
1874 };
1875
1876 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1877 << "Node must be bigger than max texture size to exercise saveLayer codepath";
John Reck16c9d6a2015-11-17 15:51:08 -08001878 auto node = TestUtils::createNode(0, 0, 10000, 10000,
1879 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1880 properties.setHasOverlappingRendering(true);
1881 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1882 // apply other properties
1883 propSetupCallback(properties);
1884
Chris Craik8ecf41c2015-11-16 10:27:59 -08001885 SkPaint paint;
1886 paint.setColor(SK_ColorWHITE);
1887 canvas.drawRect(0, 0, 10000, 10000, paint);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001888 });
John Reck7db5ffb2016-01-15 13:17:09 -08001889 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
Chris Craik8ecf41c2015-11-16 10:27:59 -08001890
Chris Craik6e068c012016-01-15 16:15:30 -08001891 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001892 nodes, sLightGeometry, Caches::getInstance());
Chris Craik8ecf41c2015-11-16 10:27:59 -08001893 SaveLayerAlphaClipTestRenderer renderer(outObservedData);
Chris Craikf158b492016-01-12 14:45:08 -08001894 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001895
1896 // assert, since output won't be valid if we haven't seen a save layer triggered
Chris Craik74af6e22016-04-05 13:18:56 -07001897 ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
Chris Craik8ecf41c2015-11-16 10:27:59 -08001898}
1899
Chris Craik3a5811b2016-03-22 15:03:08 -07001900RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001901 SaveLayerAlphaData observedData;
1902 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1903 properties.setTranslationX(10); // offset rendering content
1904 properties.setTranslationY(-2000); // offset rendering content
Chris Craik8ecf41c2015-11-16 10:27:59 -08001905 });
1906 EXPECT_EQ(190u, observedData.layerWidth);
1907 EXPECT_EQ(200u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001908 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
Chris Craik8ecf41c2015-11-16 10:27:59 -08001909 << "expect content to be clipped to screen area";
1910 Matrix4 expected;
1911 expected.loadTranslate(0, -2000, 0);
1912 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1913 << "expect content to be translated as part of being clipped";
1914}
1915
Chris Craik3a5811b2016-03-22 15:03:08 -07001916RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001917 SaveLayerAlphaData observedData;
1918 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1919 // Translate and rotate the view so that the only visible part is the top left corner of
Chris Craik8d1f2122015-11-24 16:40:09 -08001920 // 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 -08001921 // bottom of the viewport.
1922 properties.setTranslationX(100);
1923 properties.setTranslationY(100);
1924 properties.setPivotX(0);
1925 properties.setPivotY(0);
1926 properties.setRotation(45);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001927 });
1928 // ceil(sqrt(2) / 2 * 200) = 142
1929 EXPECT_EQ(142u, observedData.layerWidth);
1930 EXPECT_EQ(142u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001931 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001932 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1933}
1934
Chris Craik3a5811b2016-03-22 15:03:08 -07001935RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001936 SaveLayerAlphaData observedData;
1937 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1938 properties.setPivotX(0);
1939 properties.setPivotY(0);
1940 properties.setScaleX(2);
1941 properties.setScaleY(0.5f);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001942 });
1943 EXPECT_EQ(100u, observedData.layerWidth);
1944 EXPECT_EQ(400u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001945 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001946 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1947}
1948
Chris Craik6fe991e52015-10-20 09:39:42 -07001949} // namespace uirenderer
1950} // namespace android