blob: bcf31aeb66896e00270c1297c271f36001f76ffe [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 Craika6ac95e2015-11-02 18:06:59 -080052 ADD_FAILURE() << "Layer creation not expected in this test";
53 return nullptr;
54 }
Chris Craik98787e62015-11-13 10:55:30 -080055 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
Chris Craika6ac95e2015-11-02 18:06:59 -080056 ADD_FAILURE() << "Layer repaint not expected in this test";
57 }
58 virtual void endLayer() {
59 ADD_FAILURE() << "Layer updates not expected in this test";
60 }
Chris Craik98787e62015-11-13 10:55:30 -080061 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
Chris Craike4db79d2015-12-22 16:32:23 -080062 virtual void endFrame(const Rect& repaintRect) {}
Chris Craik6fe991e52015-10-20 09:39:42 -070063
Chris Craik15c3f192015-12-03 12:16:56 -080064 // define virtual defaults for single draw methods
65#define X(Type) \
Chris Craika6ac95e2015-11-02 18:06:59 -080066 virtual void on##Type(const Type&, const BakedOpState&) { \
67 ADD_FAILURE() << #Type " not expected in this test"; \
68 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080069 MAP_RENDERABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080070#undef X
71
72 // define virtual defaults for merged draw methods
73#define X(Type) \
74 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
75 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
76 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080077 MAP_MERGEABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080078#undef X
79
Chris Craik5854b342015-10-26 15:49:56 -070080 int getIndex() { return mIndex; }
Chris Craik6fe991e52015-10-20 09:39:42 -070081
Chris Craik5854b342015-10-26 15:49:56 -070082protected:
83 int mIndex = 0;
84};
85
86/**
87 * Dispatches all static methods to similar formed methods on renderer, which fail by default but
Chris Craik8ecf41c2015-11-16 10:27:59 -080088 * are overridden by subclasses per test.
Chris Craik5854b342015-10-26 15:49:56 -070089 */
90class TestDispatcher {
91public:
Chris Craik15c3f192015-12-03 12:16:56 -080092 // define single op methods, which redirect to TestRendererBase
93#define X(Type) \
Chris Craik5854b342015-10-26 15:49:56 -070094 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
95 renderer.on##Type(op, state); \
Chris Craik6fe991e52015-10-20 09:39:42 -070096 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080097 MAP_RENDERABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -080098#undef X
99
100 // define merged op methods, which redirect to TestRendererBase
101#define X(Type) \
102 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
103 renderer.onMerged##Type##s(opList); \
104 }
Chris Craik7cbf63d2016-01-06 13:46:52 -0800105 MAP_MERGEABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -0800106#undef X
Chris Craik6fe991e52015-10-20 09:39:42 -0700107};
Chris Craikb565df12015-10-05 13:00:52 -0700108
Chris Craik5854b342015-10-26 15:49:56 -0700109class FailRenderer : public TestRendererBase {};
Chris Craik6fe991e52015-10-20 09:39:42 -0700110
Chris Craik3a5811b2016-03-22 15:03:08 -0700111RENDERTHREAD_TEST(FrameBuilder, simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800112 class SimpleTestRenderer : public TestRendererBase {
113 public:
Chris Craik98787e62015-11-13 10:55:30 -0800114 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800115 EXPECT_EQ(0, mIndex++);
116 EXPECT_EQ(100u, width);
117 EXPECT_EQ(200u, height);
118 }
119 void onRectOp(const RectOp& op, const BakedOpState& state) override {
120 EXPECT_EQ(1, mIndex++);
121 }
122 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
123 EXPECT_EQ(2, mIndex++);
124 }
Chris Craike4db79d2015-12-22 16:32:23 -0800125 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800126 EXPECT_EQ(3, mIndex++);
127 }
128 };
129
Chris Craik8d1f2122015-11-24 16:40:09 -0800130 auto node = TestUtils::createNode(0, 0, 100, 200,
131 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700132 SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
Chris Craikb565df12015-10-05 13:00:52 -0700133 canvas.drawRect(0, 0, 100, 200, SkPaint());
134 canvas.drawBitmap(bitmap, 10, 10, nullptr);
135 });
Chris Craikf158b492016-01-12 14:45:08 -0800136 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700137 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700138 SimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800139 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700140 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
Chris Craik6fe991e52015-10-20 09:39:42 -0700141}
142
Chris Craik3a5811b2016-03-22 15:03:08 -0700143RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
Chris Craik386aa032015-12-07 17:08:25 -0800144 class SimpleStrokeTestRenderer : public TestRendererBase {
145 public:
146 void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
147 EXPECT_EQ(0, mIndex++);
148 // even though initial bounds are empty...
149 EXPECT_TRUE(op.unmappedBounds.isEmpty())
150 << "initial bounds should be empty, since they're unstroked";
151 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
152 << "final bounds should account for stroke";
153 }
154 };
155
156 auto node = TestUtils::createNode(0, 0, 100, 200,
157 [](RenderProperties& props, RecordingCanvas& canvas) {
158 SkPaint strokedPaint;
159 strokedPaint.setStrokeWidth(10);
160 canvas.drawPoint(50, 50, strokedPaint);
161 });
Chris Craikf158b492016-01-12 14:45:08 -0800162 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700163 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik386aa032015-12-07 17:08:25 -0800164 SimpleStrokeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800165 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik386aa032015-12-07 17:08:25 -0800166 EXPECT_EQ(1, renderer.getIndex());
167}
168
Chris Craik3a5811b2016-03-22 15:03:08 -0700169RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800170 auto node = TestUtils::createNode(0, 0, 200, 200,
171 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500172 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700173 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
174 canvas.drawRect(0, 0, 400, 400, SkPaint());
175 canvas.restore();
176 });
Chris Craikf158b492016-01-12 14:45:08 -0800177 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700178 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik6fe991e52015-10-20 09:39:42 -0700179
Chris Craik5854b342015-10-26 15:49:56 -0700180 FailRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800181 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb565df12015-10-05 13:00:52 -0700182}
183
Chris Craik3a5811b2016-03-22 15:03:08 -0700184RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
Chris Craika1717272015-11-19 13:02:43 -0800185 const int LOOPS = 5;
Chris Craikd3daa312015-11-06 10:59:56 -0800186 class SimpleBatchingTestRenderer : public TestRendererBase {
187 public:
188 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800189 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
Chris Craikd3daa312015-11-06 10:59:56 -0800190 }
191 void onRectOp(const RectOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800192 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
Chris Craikd3daa312015-11-06 10:59:56 -0800193 }
194 };
195
Chris Craik8d1f2122015-11-24 16:40:09 -0800196 auto node = TestUtils::createNode(0, 0, 200, 200,
197 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik15c3f192015-12-03 12:16:56 -0800198 SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
199 kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
Chris Craikb565df12015-10-05 13:00:52 -0700200
201 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
202 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
Florin Malitaeecff562015-12-21 10:43:01 -0500203 canvas.save(SaveFlags::MatrixClip);
Chris Craika1717272015-11-19 13:02:43 -0800204 for (int i = 0; i < LOOPS; i++) {
Chris Craikb565df12015-10-05 13:00:52 -0700205 canvas.translate(0, 10);
206 canvas.drawRect(0, 0, 10, 10, SkPaint());
207 canvas.drawBitmap(bitmap, 5, 0, nullptr);
208 }
209 canvas.restore();
210 });
211
Chris Craikf158b492016-01-12 14:45:08 -0800212 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700213 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700214 SimpleBatchingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800215 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800216 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craik15c3f192015-12-03 12:16:56 -0800217 << "Expect number of ops = 2 * loop count";
Chris Craika1717272015-11-19 13:02:43 -0800218}
219
Chris Craik6246d2782016-03-29 15:01:41 -0700220RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
221 class EmptyNoFbo0TestRenderer : public TestRendererBase {
222 public:
223 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
224 ADD_FAILURE() << "Primary frame draw not expected in this test";
225 }
226 void endFrame(const Rect& repaintRect) override {
227 ADD_FAILURE() << "Primary frame draw not expected in this test";
228 }
229 };
230
231 // Pass empty node list, so no work is enqueued for Fbo0
232 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
233 sEmptyNodeList, sLightGeometry, Caches::getInstance());
234 EmptyNoFbo0TestRenderer renderer;
235 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
236}
237
238RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
239 class EmptyWithFbo0TestRenderer : public TestRendererBase {
240 public:
241 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
242 EXPECT_EQ(0, mIndex++);
243 }
244 void endFrame(const Rect& repaintRect) override {
245 EXPECT_EQ(1, mIndex++);
246 }
247 };
248 auto node = TestUtils::createNode(10, 10, 110, 110,
249 [](RenderProperties& props, RecordingCanvas& canvas) {
250 // no drawn content
251 });
252 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
253
254 // Draw, but pass empty node list, so no work is done for primary frame
255 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
256 syncedNodeList, sLightGeometry, Caches::getInstance());
257 EmptyWithFbo0TestRenderer renderer;
258 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
259 EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
260 " but fbo0 update lifecycle should still be observed";
261}
262
Chris Craik80d2ade2016-03-28 12:54:07 -0700263RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
264 class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
265 public:
266 void onRectOp(const RectOp& op, const BakedOpState& state) override {
267 EXPECT_EQ(mIndex++, 0) << "Should be one rect";
268 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
269 << "Last rect should occlude others.";
270 }
271 };
272 auto node = TestUtils::createNode(0, 0, 200, 200,
273 [](RenderProperties& props, RecordingCanvas& canvas) {
274 canvas.drawRect(0, 0, 200, 200, SkPaint());
275 canvas.drawRect(0, 0, 200, 200, SkPaint());
276 canvas.drawRect(10, 10, 190, 190, SkPaint());
277 });
278
279 // Damage (and therefore clip) is same as last draw, subset of renderable area.
280 // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
281 SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
282 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
283 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
284
285 EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
286 << "Recording must not have rejected ops, in order for this test to be valid";
287
288 AvoidOverdrawRectsTestRenderer renderer;
289 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
290 EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
291}
292
293RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
294 static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
295 SkColorType::kRGB_565_SkColorType);
296 static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
297 SkColorType::kAlpha_8_SkColorType);
298 class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
299 public:
300 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craik80d2ade2016-03-28 12:54:07 -0700301 switch(mIndex++) {
302 case 0:
303 EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
304 break;
305 case 1:
306 EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
307 break;
308 default:
309 ADD_FAILURE() << "Only two ops expected.";
310 }
311 }
312 };
313
314 auto node = TestUtils::createNode(0, 0, 50, 50,
315 [](RenderProperties& props, RecordingCanvas& canvas) {
316 canvas.drawRect(0, 0, 50, 50, SkPaint());
317 canvas.drawRect(0, 0, 50, 50, SkPaint());
318 canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
319
320 // only the below draws should remain, since they're
321 canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
322 canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
323 });
324
325 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
326 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
327
328 EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
329 << "Recording must not have rejected ops, in order for this test to be valid";
330
331 AvoidOverdrawBitmapsTestRenderer renderer;
332 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
sergeyva82ffc52016-04-04 17:12:04 -0700333 EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
Chris Craik80d2ade2016-03-28 12:54:07 -0700334}
335
Chris Craik3a5811b2016-03-22 15:03:08 -0700336RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
Chris Craik93e53e02015-12-17 18:42:44 -0800337 class ClippedMergingTestRenderer : public TestRendererBase {
338 public:
339 void onMergedBitmapOps(const MergedBakedOpList& opList) override {
340 EXPECT_EQ(0, mIndex);
341 mIndex += opList.count;
342 EXPECT_EQ(4u, opList.count);
343 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
344 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
345 opList.clipSideFlags);
346 }
347 };
348 auto node = TestUtils::createNode(0, 0, 100, 100,
349 [](RenderProperties& props, TestCanvas& canvas) {
350 SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
351
352 // left side clipped (to inset left half)
353 canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
354 canvas.drawBitmap(bitmap, 0, 40, nullptr);
355
356 // top side clipped (to inset top half)
357 canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
358 canvas.drawBitmap(bitmap, 40, 0, nullptr);
359
360 // right side clipped (to inset right half)
361 canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
362 canvas.drawBitmap(bitmap, 80, 40, nullptr);
363
364 // bottom not clipped, just abutting (inset bottom half)
365 canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
366 canvas.drawBitmap(bitmap, 40, 70, nullptr);
367 });
368
Chris Craikf158b492016-01-12 14:45:08 -0800369 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -0700370 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik93e53e02015-12-17 18:42:44 -0800371 ClippedMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800372 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik93e53e02015-12-17 18:42:44 -0800373 EXPECT_EQ(4, renderer.getIndex());
374}
375
Chris Craik3a5811b2016-03-22 15:03:08 -0700376RENDERTHREAD_TEST(FrameBuilder, textMerging) {
Chris Craikd7448e62015-12-15 10:34:36 -0800377 class TextMergingTestRenderer : public TestRendererBase {
378 public:
379 void onMergedTextOps(const MergedBakedOpList& opList) override {
380 EXPECT_EQ(0, mIndex);
381 mIndex += opList.count;
382 EXPECT_EQ(2u, opList.count);
383 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
384 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
385 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
386 }
387 };
388 auto node = TestUtils::createNode(0, 0, 400, 400,
389 [](RenderProperties& props, TestCanvas& canvas) {
390 SkPaint paint;
391 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
392 paint.setAntiAlias(true);
393 paint.setTextSize(50);
sergeyvdccca442016-03-21 15:38:21 -0700394 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
395 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
Chris Craikd7448e62015-12-15 10:34:36 -0800396 });
Chris Craikf158b492016-01-12 14:45:08 -0800397 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -0700398 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikd7448e62015-12-15 10:34:36 -0800399 TextMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800400 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd7448e62015-12-15 10:34:36 -0800401 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
402}
403
Chris Craik3a5811b2016-03-22 15:03:08 -0700404RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
Chris Craika1717272015-11-19 13:02:43 -0800405 const int LOOPS = 5;
406 class TextStrikethroughTestRenderer : public TestRendererBase {
407 public:
408 void onRectOp(const RectOp& op, const BakedOpState& state) override {
409 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
410 }
Chris Craik15c3f192015-12-03 12:16:56 -0800411 void onMergedTextOps(const MergedBakedOpList& opList) override {
412 EXPECT_EQ(0, mIndex);
413 mIndex += opList.count;
414 EXPECT_EQ(5u, opList.count);
Chris Craika1717272015-11-19 13:02:43 -0800415 }
416 };
Chris Craik8d1f2122015-11-24 16:40:09 -0800417 auto node = TestUtils::createNode(0, 0, 200, 2000,
418 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craika1717272015-11-19 13:02:43 -0800419 SkPaint textPaint;
420 textPaint.setAntiAlias(true);
421 textPaint.setTextSize(20);
422 textPaint.setStrikeThruText(true);
Chris Craik42a54072015-11-24 11:41:54 -0800423 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Chris Craika1717272015-11-19 13:02:43 -0800424 for (int i = 0; i < LOOPS; i++) {
sergeyvdccca442016-03-21 15:38:21 -0700425 TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
Chris Craika1717272015-11-19 13:02:43 -0800426 }
427 });
Chris Craikf158b492016-01-12 14:45:08 -0800428 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
Chris Craik3a5811b2016-03-22 15:03:08 -0700429 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craika1717272015-11-19 13:02:43 -0800430 TextStrikethroughTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800431 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800432 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craikd7448e62015-12-15 10:34:36 -0800433 << "Expect number of ops = 2 * loop count";
Chris Craikb565df12015-10-05 13:00:52 -0700434}
435
Chris Craik7c02cab2016-03-16 17:15:12 -0700436static auto styles = {
437 SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
438
Chris Craik3a5811b2016-03-22 15:03:08 -0700439RENDERTHREAD_TEST(FrameBuilder, textStyle) {
Chris Craik7c02cab2016-03-16 17:15:12 -0700440 class TextStyleTestRenderer : public TestRendererBase {
441 public:
442 void onMergedTextOps(const MergedBakedOpList& opList) override {
443 ASSERT_EQ(0, mIndex);
444 ASSERT_EQ(3u, opList.count);
445 mIndex += opList.count;
446
447 int index = 0;
448 for (auto style : styles) {
449 auto state = opList.states[index++];
450 ASSERT_EQ(style, state->op->paint->getStyle())
451 << "Remainder of validation relies upon stable merged order";
452 ASSERT_EQ(0, state->computedState.clipSideFlags)
453 << "Clipped bounds validation requires unclipped ops";
454 }
455
456 Rect fill = opList.states[0]->computedState.clippedBounds;
457 Rect stroke = opList.states[1]->computedState.clippedBounds;
458 EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
459 << "Stroke+Fill should be same as stroke";
460
461 EXPECT_TRUE(stroke.contains(fill));
462 EXPECT_FALSE(fill.contains(stroke));
463
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400464 // outset by half the stroke width
Chris Craik7c02cab2016-03-16 17:15:12 -0700465 Rect outsetFill(fill);
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400466 outsetFill.outset(5);
Chris Craik7c02cab2016-03-16 17:15:12 -0700467 EXPECT_EQ(stroke, outsetFill);
468 }
469 };
470 auto node = TestUtils::createNode(0, 0, 400, 400,
471 [](RenderProperties& props, TestCanvas& canvas) {
472 SkPaint paint;
473 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
474 paint.setAntiAlias(true);
475 paint.setTextSize(50);
476 paint.setStrokeWidth(10);
477
478 // draw 3 copies of the same text overlapping, each with a different style.
479 // They'll get merged, but with
480 for (auto style : styles) {
481 paint.setStyle(style);
sergeyvdccca442016-03-21 15:38:21 -0700482 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
Chris Craik7c02cab2016-03-16 17:15:12 -0700483 }
484 });
485 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -0700486 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik7c02cab2016-03-16 17:15:12 -0700487 TextStyleTestRenderer renderer;
488 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
489 EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
490}
491
Chris Craikaafb01d2016-03-25 18:34:11 -0700492RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
493 class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800494 public:
495 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
496 EXPECT_EQ(0, mIndex++);
Chris Craike4db79d2015-12-22 16:32:23 -0800497 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800498 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
499
500 Matrix4 expected;
501 expected.loadTranslate(5, 5, 0);
502 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
503 }
504 };
505
506 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
Chris Craik243e85b2016-03-25 15:26:11 -0700507 SkMatrix::MakeTrans(5, 5));
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800508
509 auto node = TestUtils::createNode(0, 0, 200, 200,
510 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500511 canvas.save(SaveFlags::MatrixClip);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800512 canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
513 canvas.drawLayer(layerUpdater.get());
514 canvas.restore();
515 });
Chris Craikf158b492016-01-12 14:45:08 -0800516 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700517 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikaafb01d2016-03-25 18:34:11 -0700518 TextureLayerClipLocalMatrixTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800519 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800520 EXPECT_EQ(1, renderer.getIndex());
521}
522
Chris Craikaafb01d2016-03-25 18:34:11 -0700523RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
524 class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
525 public:
526 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
527 EXPECT_EQ(0, mIndex++);
528
529 Matrix4 expected;
530 expected.loadTranslate(35, 45, 0);
531 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
532 }
533 };
534
535 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
536 SkMatrix::MakeTrans(5, 5));
537
538 auto node = TestUtils::createNode(0, 0, 200, 200,
539 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
540 canvas.save(SaveFlags::MatrixClip);
541 canvas.translate(30, 40);
542 canvas.drawLayer(layerUpdater.get());
543 canvas.restore();
544 });
545
546 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
547 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
548 TextureLayerCombineMatricesTestRenderer renderer;
549 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
550 EXPECT_EQ(1, renderer.getIndex());
551}
552
553RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
554 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
555 SkMatrix::MakeTrans(5, 5));
556 layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
557
558 auto node = TestUtils::createNode(0, 0, 200, 200,
559 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
560 canvas.drawLayer(layerUpdater.get());
561 });
562 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
563 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
564 FailRenderer renderer;
565 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
566}
567
Chris Craik3a5811b2016-03-22 15:03:08 -0700568RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
Chris Craik223e3b62016-03-10 10:27:38 -0800569 class FunctorTestRenderer : public TestRendererBase {
570 public:
571 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
572 EXPECT_EQ(0, mIndex++);
573 }
574 };
575 Functor noopFunctor;
576
577 // 1 million pixel tall view, scrolled down 80%
578 auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
579 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
580 canvas.translate(0, -800000);
581 canvas.callDrawGLFunction(&noopFunctor);
582 });
583
584 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700585 TestUtils::createSyncedNodeList(scrolledFunctorView),
586 sLightGeometry, Caches::getInstance());
Chris Craik223e3b62016-03-10 10:27:38 -0800587 FunctorTestRenderer renderer;
588 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
589 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
590}
591
Chris Craika2048482016-03-25 14:17:49 -0700592RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
593 class ColorTestRenderer : public TestRendererBase {
594 public:
595 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
596 EXPECT_EQ(0, mIndex++);
597 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
598 << "Color op should be expanded to bounds of surrounding";
599 }
600 };
601
602 auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
603 [](RenderProperties& props, RecordingCanvas& canvas) {
604 props.setClipToBounds(false);
605 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
606 });
607
608 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
609 TestUtils::createSyncedNodeList(unclippedColorView),
610 sLightGeometry, Caches::getInstance());
611 ColorTestRenderer renderer;
612 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
613 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
614}
615
616TEST(FrameBuilder, renderNode) {
Chris Craikd3daa312015-11-06 10:59:56 -0800617 class RenderNodeTestRenderer : public TestRendererBase {
618 public:
619 void onRectOp(const RectOp& op, const BakedOpState& state) override {
620 switch(mIndex++) {
621 case 0:
Chris Craik5430ab22015-12-10 16:28:16 -0800622 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
Chris Craikd3daa312015-11-06 10:59:56 -0800623 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
624 break;
625 case 1:
626 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
627 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
628 break;
629 default:
630 ADD_FAILURE();
631 }
632 }
633 };
634
Chris Craik8d1f2122015-11-24 16:40:09 -0800635 auto child = TestUtils::createNode(10, 10, 110, 110,
636 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700637 SkPaint paint;
638 paint.setColor(SK_ColorWHITE);
639 canvas.drawRect(0, 0, 100, 100, paint);
640 });
641
Chris Craik8d1f2122015-11-24 16:40:09 -0800642 auto parent = TestUtils::createNode(0, 0, 200, 200,
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800643 [&child](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700644 SkPaint paint;
645 paint.setColor(SK_ColorDKGRAY);
646 canvas.drawRect(0, 0, 200, 200, paint);
Chris Craikb565df12015-10-05 13:00:52 -0700647
Florin Malitaeecff562015-12-21 10:43:01 -0500648 canvas.save(SaveFlags::MatrixClip);
Chris Craikddf22152015-10-14 17:42:47 -0700649 canvas.translate(40, 40);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800650 canvas.drawRenderNode(child.get());
Chris Craikddf22152015-10-14 17:42:47 -0700651 canvas.restore();
Chris Craikb565df12015-10-05 13:00:52 -0700652 });
653
Chris Craikf158b492016-01-12 14:45:08 -0800654 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700655 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700656 RenderNodeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800657 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik223e3b62016-03-10 10:27:38 -0800658 EXPECT_EQ(2, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700659}
660
Chris Craik3a5811b2016-03-22 15:03:08 -0700661RENDERTHREAD_TEST(FrameBuilder, clipped) {
Chris Craikd3daa312015-11-06 10:59:56 -0800662 class ClippedTestRenderer : public TestRendererBase {
663 public:
664 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
665 EXPECT_EQ(0, mIndex++);
666 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800667 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800668 EXPECT_TRUE(state.computedState.transform.isIdentity());
669 }
670 };
671
Chris Craik8d1f2122015-11-24 16:40:09 -0800672 auto node = TestUtils::createNode(0, 0, 200, 200,
673 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700674 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
675 canvas.drawBitmap(bitmap, 0, 0, nullptr);
676 });
Chris Craikddf22152015-10-14 17:42:47 -0700677
Chris Craikf158b492016-01-12 14:45:08 -0800678 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
Chris Craik0b7e8242015-10-28 16:50:44 -0700679 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
Chris Craik3a5811b2016-03-22 15:03:08 -0700680 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700681 ClippedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800682 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikddf22152015-10-14 17:42:47 -0700683}
684
Chris Craik3a5811b2016-03-22 15:03:08 -0700685RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800686 class SaveLayerSimpleTestRenderer : public TestRendererBase {
687 public:
688 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
689 EXPECT_EQ(0, mIndex++);
690 EXPECT_EQ(180u, width);
691 EXPECT_EQ(180u, height);
692 return nullptr;
693 }
694 void endLayer() override {
695 EXPECT_EQ(2, mIndex++);
696 }
697 void onRectOp(const RectOp& op, const BakedOpState& state) override {
698 EXPECT_EQ(1, mIndex++);
699 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
Chris Craik5430ab22015-12-10 16:28:16 -0800700 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800701 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800702
703 Matrix4 expectedTransform;
704 expectedTransform.loadTranslate(-10, -10, 0);
705 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
706 }
707 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
708 EXPECT_EQ(3, mIndex++);
709 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800710 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800711 EXPECT_TRUE(state.computedState.transform.isIdentity());
712 }
713 };
714
Chris Craik8d1f2122015-11-24 16:40:09 -0800715 auto node = TestUtils::createNode(0, 0, 200, 200,
716 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500717 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700718 canvas.drawRect(10, 10, 190, 190, SkPaint());
719 canvas.restore();
720 });
Chris Craikf158b492016-01-12 14:45:08 -0800721 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700722 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700723 SaveLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800724 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700725 EXPECT_EQ(4, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700726}
Chris Craik6fe991e52015-10-20 09:39:42 -0700727
Chris Craik3a5811b2016-03-22 15:03:08 -0700728RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
Chris Craikd3daa312015-11-06 10:59:56 -0800729 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
730 * - startTemporaryLayer2, rect2 endLayer2
731 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
732 * - startFrame, layerOp1, endFrame
733 */
734 class SaveLayerNestedTestRenderer : public TestRendererBase {
735 public:
736 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
737 const int index = mIndex++;
738 if (index == 0) {
739 EXPECT_EQ(400u, width);
740 EXPECT_EQ(400u, height);
741 return (OffscreenBuffer*) 0x400;
742 } else if (index == 3) {
743 EXPECT_EQ(800u, width);
744 EXPECT_EQ(800u, height);
745 return (OffscreenBuffer*) 0x800;
746 } else { ADD_FAILURE(); }
747 return (OffscreenBuffer*) nullptr;
748 }
749 void endLayer() override {
750 int index = mIndex++;
751 EXPECT_TRUE(index == 2 || index == 6);
752 }
Chris Craik98787e62015-11-13 10:55:30 -0800753 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800754 EXPECT_EQ(7, mIndex++);
755 }
Chris Craike4db79d2015-12-22 16:32:23 -0800756 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800757 EXPECT_EQ(9, mIndex++);
758 }
759 void onRectOp(const RectOp& op, const BakedOpState& state) override {
760 const int index = mIndex++;
761 if (index == 1) {
Chris Craik5430ab22015-12-10 16:28:16 -0800762 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
Chris Craikd3daa312015-11-06 10:59:56 -0800763 } else if (index == 4) {
Chris Craik5430ab22015-12-10 16:28:16 -0800764 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
Chris Craikd3daa312015-11-06 10:59:56 -0800765 } else { ADD_FAILURE(); }
766 }
767 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
768 const int index = mIndex++;
769 if (index == 5) {
770 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800771 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
Chris Craikd3daa312015-11-06 10:59:56 -0800772 } else if (index == 8) {
773 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800774 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
Chris Craikd3daa312015-11-06 10:59:56 -0800775 } else { ADD_FAILURE(); }
776 }
777 };
778
Chris Craik8d1f2122015-11-24 16:40:09 -0800779 auto node = TestUtils::createNode(0, 0, 800, 800,
780 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500781 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700782 {
783 canvas.drawRect(0, 0, 800, 800, SkPaint());
Florin Malitaeecff562015-12-21 10:43:01 -0500784 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700785 {
786 canvas.drawRect(0, 0, 400, 400, SkPaint());
787 }
788 canvas.restore();
789 }
790 canvas.restore();
791 });
792
Chris Craikf158b492016-01-12 14:45:08 -0800793 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
Chris Craik3a5811b2016-03-22 15:03:08 -0700794 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700795 SaveLayerNestedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800796 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700797 EXPECT_EQ(10, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700798}
Chris Craik6fe991e52015-10-20 09:39:42 -0700799
Chris Craik3a5811b2016-03-22 15:03:08 -0700800RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800801 auto node = TestUtils::createNode(0, 0, 200, 200,
802 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500803 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700804 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
Florin Malitaeecff562015-12-21 10:43:01 -0500805 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700806
807 // draw within save layer may still be recorded, but shouldn't be drawn
808 canvas.drawRect(200, 200, 400, 400, SkPaint());
809
810 canvas.restore();
811 canvas.restore();
812 });
Chris Craikf158b492016-01-12 14:45:08 -0800813 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700814 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik6fe991e52015-10-20 09:39:42 -0700815
Chris Craik5854b342015-10-26 15:49:56 -0700816 FailRenderer renderer;
Chris Craik6fe991e52015-10-20 09:39:42 -0700817 // should see no ops, even within the layer, since the layer should be rejected
Chris Craikf158b492016-01-12 14:45:08 -0800818 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700819}
820
Chris Craik3a5811b2016-03-22 15:03:08 -0700821RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800822 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
823 public:
824 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
825 EXPECT_EQ(0, mIndex++);
826 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800827 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800828 EXPECT_TRUE(state.computedState.transform.isIdentity());
829 }
830 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
831 EXPECT_EQ(1, mIndex++);
832 ASSERT_NE(nullptr, op.paint);
833 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
834 }
835 void onRectOp(const RectOp& op, const BakedOpState& state) override {
836 EXPECT_EQ(2, mIndex++);
837 EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
838 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
839 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
840 EXPECT_TRUE(state.computedState.transform.isIdentity());
841 }
842 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
843 EXPECT_EQ(3, mIndex++);
844 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800845 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800846 EXPECT_TRUE(state.computedState.transform.isIdentity());
847 }
848 };
849
850 auto node = TestUtils::createNode(0, 0, 200, 200,
851 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500852 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
Chris Craikb87eadd2016-01-06 09:16:05 -0800853 canvas.drawRect(0, 0, 200, 200, SkPaint());
854 canvas.restore();
855 });
Chris Craikf158b492016-01-12 14:45:08 -0800856 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700857 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800858 SaveLayerUnclippedSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800859 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800860 EXPECT_EQ(4, renderer.getIndex());
861}
862
Chris Craik3a5811b2016-03-22 15:03:08 -0700863RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800864 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
865 public:
866 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
867 int index = mIndex++;
868 EXPECT_GT(4, index);
869 EXPECT_EQ(5, op.unmappedBounds.getWidth());
870 EXPECT_EQ(5, op.unmappedBounds.getHeight());
871 if (index == 0) {
872 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
873 } else if (index == 1) {
874 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
875 } else if (index == 2) {
876 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
877 } else if (index == 3) {
878 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
879 }
880 }
881 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
882 EXPECT_EQ(4, mIndex++);
883 ASSERT_EQ(op.vertexCount, 16u);
884 for (size_t i = 0; i < op.vertexCount; i++) {
885 auto v = op.vertices[i];
886 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
887 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
888 }
889 }
890 void onRectOp(const RectOp& op, const BakedOpState& state) override {
891 EXPECT_EQ(5, mIndex++);
892 }
893 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
894 EXPECT_LT(5, mIndex++);
895 }
896 };
897
898 auto node = TestUtils::createNode(0, 0, 200, 200,
899 [](RenderProperties& props, RecordingCanvas& canvas) {
900
Florin Malitaeecff562015-12-21 10:43:01 -0500901 int restoreTo = canvas.save(SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800902 canvas.scale(2, 2);
Florin Malitaeecff562015-12-21 10:43:01 -0500903 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
904 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
905 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
906 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800907 canvas.drawRect(0, 0, 100, 100, SkPaint());
908 canvas.restoreToCount(restoreTo);
909 });
Chris Craikf158b492016-01-12 14:45:08 -0800910 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700911 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800912 SaveLayerUnclippedMergedClearsTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800913 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800914 EXPECT_EQ(10, renderer.getIndex())
915 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
916}
917
Chris Craik3a5811b2016-03-22 15:03:08 -0700918RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
Chris Craik4876de12016-02-25 16:54:08 -0800919 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
920 public:
921 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
922 EXPECT_EQ(0, mIndex++);
923 }
924 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
925 EXPECT_EQ(1, mIndex++);
926 ASSERT_NE(nullptr, op.paint);
927 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
928 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
929 << "Expect dirty rect as clip";
930 ASSERT_NE(nullptr, state.computedState.clipState);
931 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
932 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
933 }
934 void onRectOp(const RectOp& op, const BakedOpState& state) override {
935 EXPECT_EQ(2, mIndex++);
936 }
937 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
938 EXPECT_EQ(3, mIndex++);
939 }
940 };
941
942 auto node = TestUtils::createNode(0, 0, 200, 200,
943 [](RenderProperties& props, RecordingCanvas& canvas) {
944 // save smaller than clip, so we get unclipped behavior
945 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
946 canvas.drawRect(0, 0, 200, 200, SkPaint());
947 canvas.restore();
948 });
949
950 // draw with partial screen dirty, and assert we see that rect later
951 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700952 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800953 SaveLayerUnclippedClearClipTestRenderer renderer;
954 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
955 EXPECT_EQ(4, renderer.getIndex());
956}
957
Chris Craik3a5811b2016-03-22 15:03:08 -0700958RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
Chris Craik4876de12016-02-25 16:54:08 -0800959 auto node = TestUtils::createNode(0, 0, 200, 200,
960 [](RenderProperties& props, RecordingCanvas& canvas) {
961 // unclipped savelayer + rect both in area that won't intersect with dirty
962 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
963 canvas.drawRect(100, 100, 200, 200, SkPaint());
964 canvas.restore();
965 });
966
967 // draw with partial screen dirty that doesn't intersect with savelayer
968 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700969 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800970 FailRenderer renderer;
971 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
972}
973
Chris Craikb87eadd2016-01-06 09:16:05 -0800974/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
975 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
976 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
977 */
Chris Craik3a5811b2016-03-22 15:03:08 -0700978RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800979 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
980 public:
981 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
982 EXPECT_EQ(0, mIndex++); // savelayer first
983 return (OffscreenBuffer*)0xabcd;
984 }
985 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
986 int index = mIndex++;
987 EXPECT_TRUE(index == 1 || index == 7);
988 }
989 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
990 int index = mIndex++;
991 EXPECT_TRUE(index == 2 || index == 8);
992 }
993 void onRectOp(const RectOp& op, const BakedOpState& state) override {
994 EXPECT_EQ(3, mIndex++);
995 Matrix4 expected;
996 expected.loadTranslate(-100, -100, 0);
997 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
998 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
999 }
1000 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
1001 int index = mIndex++;
1002 EXPECT_TRUE(index == 4 || index == 10);
1003 }
1004 void endLayer() override {
1005 EXPECT_EQ(5, mIndex++);
1006 }
1007 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1008 EXPECT_EQ(6, mIndex++);
1009 }
1010 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1011 EXPECT_EQ(9, mIndex++);
1012 }
1013 void endFrame(const Rect& repaintRect) override {
1014 EXPECT_EQ(11, mIndex++);
1015 }
1016 };
1017
1018 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
1019 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -05001020 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
1021 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
1022 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
Chris Craikb87eadd2016-01-06 09:16:05 -08001023 canvas.drawRect(200, 200, 300, 300, SkPaint());
1024 canvas.restore();
1025 canvas.restore();
1026 canvas.restore();
1027 });
Chris Craikf158b492016-01-12 14:45:08 -08001028 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
Chris Craik3a5811b2016-03-22 15:03:08 -07001029 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -08001030 SaveLayerUnclippedComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001031 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -08001032 EXPECT_EQ(12, renderer.getIndex());
1033}
1034
Chris Craikf158b492016-01-12 14:45:08 -08001035RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -08001036 class HwLayerSimpleTestRenderer : public TestRendererBase {
1037 public:
Chris Craik98787e62015-11-13 10:55:30 -08001038 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001039 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001040 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1041 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1042 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
Chris Craikd3daa312015-11-06 10:59:56 -08001043 }
1044 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1045 EXPECT_EQ(1, mIndex++);
1046
1047 EXPECT_TRUE(state.computedState.transform.isIdentity())
1048 << "Transform should be reset within layer";
1049
Chris Craike4db79d2015-12-22 16:32:23 -08001050 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
Chris Craikd3daa312015-11-06 10:59:56 -08001051 << "Damage rect should be used to clip layer content";
1052 }
1053 void endLayer() override {
1054 EXPECT_EQ(2, mIndex++);
1055 }
Chris Craik98787e62015-11-13 10:55:30 -08001056 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001057 EXPECT_EQ(3, mIndex++);
1058 }
1059 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1060 EXPECT_EQ(4, mIndex++);
1061 }
Chris Craike4db79d2015-12-22 16:32:23 -08001062 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001063 EXPECT_EQ(5, mIndex++);
1064 }
1065 };
1066
Chris Craik8d1f2122015-11-24 16:40:09 -08001067 auto node = TestUtils::createNode(10, 10, 110, 110,
John Reck16c9d6a2015-11-17 15:51:08 -08001068 [](RenderProperties& props, RecordingCanvas& canvas) {
1069 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001070 SkPaint paint;
1071 paint.setColor(SK_ColorWHITE);
1072 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001073 });
Chris Craik98787e62015-11-13 10:55:30 -08001074 OffscreenBuffer** layerHandle = node->getLayerHandle();
Chris Craik0b7e8242015-10-28 16:50:44 -07001075
Chris Craik98787e62015-11-13 10:55:30 -08001076 // create RenderNode's layer here in same way prepareTree would
1077 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1078 *layerHandle = &layer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001079
John Reck7db5ffb2016-01-15 13:17:09 -08001080 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
Chris Craik0b7e8242015-10-28 16:50:44 -07001081
1082 // only enqueue partial damage
Chris Craik98787e62015-11-13 10:55:30 -08001083 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001084 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1085
Chris Craikf158b492016-01-12 14:45:08 -08001086 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001087 syncedNodeList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -07001088 HwLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001089 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001090 EXPECT_EQ(6, renderer.getIndex());
1091
1092 // clean up layer pointer, so we can safely destruct RenderNode
Chris Craik98787e62015-11-13 10:55:30 -08001093 *layerHandle = nullptr;
Chris Craik0b7e8242015-10-28 16:50:44 -07001094}
1095
Chris Craikf158b492016-01-12 14:45:08 -08001096RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
Chris Craikd3daa312015-11-06 10:59:56 -08001097 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
1098 * - startRepaintLayer(child), rect(grey), endLayer
1099 * - startTemporaryLayer, drawLayer(child), endLayer
1100 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
1101 * - startFrame, drawLayer(parent), endLayerb
1102 */
1103 class HwLayerComplexTestRenderer : public TestRendererBase {
1104 public:
1105 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
1106 EXPECT_EQ(3, mIndex++); // savelayer first
1107 return (OffscreenBuffer*)0xabcd;
1108 }
Chris Craik98787e62015-11-13 10:55:30 -08001109 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001110 int index = mIndex++;
1111 if (index == 0) {
1112 // starting inner layer
Chris Craik98787e62015-11-13 10:55:30 -08001113 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1114 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001115 } else if (index == 6) {
1116 // starting outer layer
Chris Craik98787e62015-11-13 10:55:30 -08001117 EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
1118 EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001119 } else { ADD_FAILURE(); }
1120 }
1121 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1122 int index = mIndex++;
1123 if (index == 1) {
1124 // inner layer's rect (white)
1125 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1126 } else if (index == 7) {
1127 // outer layer's rect (grey)
1128 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
1129 } else { ADD_FAILURE(); }
1130 }
1131 void endLayer() override {
1132 int index = mIndex++;
1133 EXPECT_TRUE(index == 2 || index == 5 || index == 9);
1134 }
Chris Craik98787e62015-11-13 10:55:30 -08001135 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001136 EXPECT_EQ(10, mIndex++);
1137 }
1138 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
Chris Craik98787e62015-11-13 10:55:30 -08001139 OffscreenBuffer* layer = *op.layerHandle;
Chris Craikd3daa312015-11-06 10:59:56 -08001140 int index = mIndex++;
1141 if (index == 4) {
Chris Craik98787e62015-11-13 10:55:30 -08001142 EXPECT_EQ(100u, layer->viewportWidth);
1143 EXPECT_EQ(100u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001144 } else if (index == 8) {
1145 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
1146 } else if (index == 11) {
Chris Craik98787e62015-11-13 10:55:30 -08001147 EXPECT_EQ(200u, layer->viewportWidth);
1148 EXPECT_EQ(200u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -08001149 } else { ADD_FAILURE(); }
1150 }
Chris Craike4db79d2015-12-22 16:32:23 -08001151 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -08001152 EXPECT_EQ(12, mIndex++);
1153 }
1154 };
1155
John Reck16c9d6a2015-11-17 15:51:08 -08001156 auto child = TestUtils::createNode(50, 50, 150, 150,
1157 [](RenderProperties& props, RecordingCanvas& canvas) {
1158 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001159 SkPaint paint;
1160 paint.setColor(SK_ColorWHITE);
1161 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001162 });
Chris Craik98787e62015-11-13 10:55:30 -08001163 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1164 *(child->getLayerHandle()) = &childLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001165
1166 RenderNode* childPtr = child.get();
John Reck16c9d6a2015-11-17 15:51:08 -08001167 auto parent = TestUtils::createNode(0, 0, 200, 200,
1168 [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1169 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001170 SkPaint paint;
1171 paint.setColor(SK_ColorDKGRAY);
1172 canvas.drawRect(0, 0, 200, 200, paint);
1173
Florin Malitaeecff562015-12-21 10:43:01 -05001174 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001175 canvas.drawRenderNode(childPtr);
1176 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001177 });
Chris Craik98787e62015-11-13 10:55:30 -08001178 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1179 *(parent->getLayerHandle()) = &parentLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001180
John Reck7db5ffb2016-01-15 13:17:09 -08001181 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik0b7e8242015-10-28 16:50:44 -07001182
Chris Craik98787e62015-11-13 10:55:30 -08001183 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001184 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1185 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1186
Chris Craikf158b492016-01-12 14:45:08 -08001187 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001188 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -07001189 HwLayerComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001190 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001191 EXPECT_EQ(13, renderer.getIndex());
1192
1193 // clean up layer pointers, so we can safely destruct RenderNodes
1194 *(child->getLayerHandle()) = nullptr;
1195 *(parent->getLayerHandle()) = nullptr;
1196}
1197
Chris Craik6246d2782016-03-29 15:01:41 -07001198
1199RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
1200 class BuildLayerTestRenderer : public TestRendererBase {
1201 public:
1202 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1203 EXPECT_EQ(0, mIndex++);
1204 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
1205 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
1206 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
1207 }
1208 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
1209 EXPECT_EQ(1, mIndex++);
1210
1211 EXPECT_TRUE(state.computedState.transform.isIdentity())
1212 << "Transform should be reset within layer";
1213
1214 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
1215 << "Damage rect should be used to clip layer content";
1216 }
1217 void endLayer() override {
1218 EXPECT_EQ(2, mIndex++);
1219 }
1220 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
1221 ADD_FAILURE() << "Primary frame draw not expected in this test";
1222 }
1223 void endFrame(const Rect& repaintRect) override {
1224 ADD_FAILURE() << "Primary frame draw not expected in this test";
1225 }
1226 };
1227
1228 auto node = TestUtils::createNode(10, 10, 110, 110,
1229 [](RenderProperties& props, RecordingCanvas& canvas) {
1230 props.mutateLayerProperties().setType(LayerType::RenderLayer);
1231 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
1232 });
1233 OffscreenBuffer** layerHandle = node->getLayerHandle();
1234
1235 // create RenderNode's layer here in same way prepareTree would
1236 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1237 *layerHandle = &layer;
1238
1239 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
1240
1241 // only enqueue partial damage
1242 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1243 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
1244
1245 // Draw, but pass empty node list, so no work is done for primary frame
1246 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
1247 sEmptyNodeList, sLightGeometry, Caches::getInstance());
1248 BuildLayerTestRenderer renderer;
1249 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1250 EXPECT_EQ(3, renderer.getIndex());
1251
1252 // clean up layer pointer, so we can safely destruct RenderNode
1253 *layerHandle = nullptr;
1254}
1255
Chris Craik161f54b2015-11-05 11:08:52 -08001256static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1257 SkPaint paint;
1258 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
1259 canvas->drawRect(0, 0, 100, 100, paint);
1260}
1261static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
John Reck16c9d6a2015-11-17 15:51:08 -08001262 auto node = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001263 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001264 drawOrderedRect(&canvas, expectedDrawOrder);
1265 });
1266 node->mutateStagingProperties().setTranslationZ(z);
1267 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1268 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1269}
Chris Craik3a5811b2016-03-22 15:03:08 -07001270RENDERTHREAD_TEST(FrameBuilder, zReorder) {
Chris Craikd3daa312015-11-06 10:59:56 -08001271 class ZReorderTestRenderer : public TestRendererBase {
1272 public:
1273 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1274 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1275 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1276 }
1277 };
1278
John Reck16c9d6a2015-11-17 15:51:08 -08001279 auto parent = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001280 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001281 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1282 drawOrderedRect(&canvas, 1);
1283 canvas.insertReorderBarrier(true);
1284 drawOrderedNode(&canvas, 6, 2.0f);
1285 drawOrderedRect(&canvas, 3);
1286 drawOrderedNode(&canvas, 4, 0.0f);
1287 drawOrderedRect(&canvas, 5);
1288 drawOrderedNode(&canvas, 2, -2.0f);
1289 drawOrderedNode(&canvas, 7, 2.0f);
1290 canvas.insertReorderBarrier(false);
1291 drawOrderedRect(&canvas, 8);
1292 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1293 });
Chris Craikf158b492016-01-12 14:45:08 -08001294 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001295 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik161f54b2015-11-05 11:08:52 -08001296 ZReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001297 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik161f54b2015-11-05 11:08:52 -08001298 EXPECT_EQ(10, renderer.getIndex());
1299};
1300
Chris Craik3a5811b2016-03-22 15:03:08 -07001301RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
Chris Craik8d1f2122015-11-24 16:40:09 -08001302 static const int scrollX = 5;
1303 static const int scrollY = 10;
1304 class ProjectionReorderTestRenderer : public TestRendererBase {
1305 public:
1306 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1307 const int index = mIndex++;
1308
1309 Matrix4 expectedMatrix;
1310 switch (index) {
1311 case 0:
1312 EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1313 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1314 expectedMatrix.loadIdentity();
Chris Craik678ff812016-03-01 13:27:54 -08001315 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001316 break;
1317 case 1:
1318 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1319 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
Chris Craik678ff812016-03-01 13:27:54 -08001320 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1321 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1322 EXPECT_EQ(Rect(-35, -30, 45, 50),
1323 Rect(state.computedState.localProjectionPathMask->getBounds()));
Chris Craik8d1f2122015-11-24 16:40:09 -08001324 break;
1325 case 2:
1326 EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1327 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1328 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
Chris Craik678ff812016-03-01 13:27:54 -08001329 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001330 break;
1331 default:
1332 ADD_FAILURE();
1333 }
Chris Craik678ff812016-03-01 13:27:54 -08001334 EXPECT_EQ(expectedMatrix, state.computedState.transform);
Chris Craik8d1f2122015-11-24 16:40:09 -08001335 }
1336 };
1337
1338 /**
1339 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1340 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1341 * draw, but because it is projected backwards, it's drawn in between B and C.
1342 *
1343 * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1344 * (which isn't affected by scroll).
1345 */
1346 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1347 [](RenderProperties& properties, RecordingCanvas& canvas) {
1348 properties.setProjectionReceiver(true);
1349 // scroll doesn't apply to background, so undone via translationX/Y
1350 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1351 properties.setTranslationX(scrollX);
1352 properties.setTranslationY(scrollY);
1353
1354 SkPaint paint;
1355 paint.setColor(SK_ColorWHITE);
1356 canvas.drawRect(0, 0, 100, 100, paint);
1357 });
1358 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1359 [](RenderProperties& properties, RecordingCanvas& canvas) {
1360 properties.setProjectBackwards(true);
1361 properties.setClipToBounds(false);
1362 SkPaint paint;
1363 paint.setColor(SK_ColorDKGRAY);
1364 canvas.drawRect(-10, -10, 60, 60, paint);
1365 });
1366 auto child = TestUtils::createNode(0, 50, 100, 100,
1367 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1368 SkPaint paint;
1369 paint.setColor(SK_ColorBLUE);
1370 canvas.drawRect(0, 0, 100, 50, paint);
1371 canvas.drawRenderNode(projectingRipple.get());
1372 });
1373 auto parent = TestUtils::createNode(0, 0, 100, 100,
1374 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
Chris Craik678ff812016-03-01 13:27:54 -08001375 // Set a rect outline for the projecting ripple to be masked against.
1376 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1377
Florin Malitaeecff562015-12-21 10:43:01 -05001378 canvas.save(SaveFlags::MatrixClip);
Chris Craik8d1f2122015-11-24 16:40:09 -08001379 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1380 canvas.drawRenderNode(receiverBackground.get());
1381 canvas.drawRenderNode(child.get());
1382 canvas.restore();
1383 });
1384
Chris Craikf158b492016-01-12 14:45:08 -08001385 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001386 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik8d1f2122015-11-24 16:40:09 -08001387 ProjectionReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001388 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8d1f2122015-11-24 16:40:09 -08001389 EXPECT_EQ(3, renderer.getIndex());
1390}
1391
Chris Craik678ff812016-03-01 13:27:54 -08001392RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1393 static const int scrollX = 5;
1394 static const int scrollY = 10;
1395 class ProjectionHwLayerTestRenderer : public TestRendererBase {
1396 public:
1397 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1398 EXPECT_EQ(0, mIndex++);
1399 }
1400 void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1401 EXPECT_EQ(1, mIndex++);
1402 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1403 }
1404 void endLayer() override {
1405 EXPECT_EQ(2, mIndex++);
1406 }
1407 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1408 EXPECT_EQ(3, mIndex++);
1409 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1410 }
1411 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1412 EXPECT_EQ(4, mIndex++);
1413 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1414 Matrix4 expected;
1415 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1416 EXPECT_EQ(expected, state.computedState.transform);
1417 EXPECT_EQ(Rect(-85, -80, 295, 300),
1418 Rect(state.computedState.localProjectionPathMask->getBounds()));
1419 }
1420 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1421 EXPECT_EQ(5, mIndex++);
1422 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1423 }
1424 };
1425 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1426 [](RenderProperties& properties, RecordingCanvas& canvas) {
1427 properties.setProjectionReceiver(true);
1428 // scroll doesn't apply to background, so undone via translationX/Y
1429 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1430 properties.setTranslationX(scrollX);
1431 properties.setTranslationY(scrollY);
1432
1433 canvas.drawRect(0, 0, 400, 400, SkPaint());
1434 });
1435 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1436 [](RenderProperties& properties, RecordingCanvas& canvas) {
1437 properties.setProjectBackwards(true);
1438 properties.setClipToBounds(false);
1439 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1440 });
1441 auto child = TestUtils::createNode(100, 100, 300, 300,
1442 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1443 properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1444 canvas.drawRenderNode(projectingRipple.get());
1445 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1446 });
1447 auto parent = TestUtils::createNode(0, 0, 400, 400,
1448 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1449 // Set a rect outline for the projecting ripple to be masked against.
1450 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1451 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1452 canvas.drawRenderNode(receiverBackground.get());
1453 canvas.drawRenderNode(child.get());
1454 });
1455
1456 OffscreenBuffer** layerHandle = child->getLayerHandle();
1457
1458 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1459 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1460 Matrix4 windowTransform;
1461 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1462 layer.setWindowTransform(windowTransform);
1463 *layerHandle = &layer;
1464
1465 auto syncedList = TestUtils::createSyncedNodeList(parent);
1466 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1467 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1468 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001469 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik678ff812016-03-01 13:27:54 -08001470 ProjectionHwLayerTestRenderer renderer;
1471 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1472 EXPECT_EQ(6, renderer.getIndex());
1473
1474 // clean up layer pointer, so we can safely destruct RenderNode
1475 *layerHandle = nullptr;
1476}
1477
Chris Craika748c082016-03-01 18:48:37 -08001478RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1479 static const int scrollX = 500000;
1480 static const int scrollY = 0;
1481 class ProjectionChildScrollTestRenderer : public TestRendererBase {
1482 public:
1483 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1484 EXPECT_EQ(0, mIndex++);
1485 EXPECT_TRUE(state.computedState.transform.isIdentity());
1486 }
1487 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1488 EXPECT_EQ(1, mIndex++);
1489 ASSERT_NE(nullptr, state.computedState.clipState);
1490 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1491 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1492 EXPECT_TRUE(state.computedState.transform.isIdentity());
1493 }
1494 };
1495 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1496 [](RenderProperties& properties, RecordingCanvas& canvas) {
1497 properties.setProjectionReceiver(true);
1498 canvas.drawRect(0, 0, 400, 400, SkPaint());
1499 });
1500 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1501 [](RenderProperties& properties, RecordingCanvas& canvas) {
1502 // scroll doesn't apply to background, so undone via translationX/Y
1503 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1504 properties.setTranslationX(scrollX);
1505 properties.setTranslationY(scrollY);
1506 properties.setProjectBackwards(true);
1507 properties.setClipToBounds(false);
1508 canvas.drawOval(0, 0, 200, 200, SkPaint());
1509 });
1510 auto child = TestUtils::createNode(0, 0, 400, 400,
1511 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1512 // Record time clip will be ignored by projectee
1513 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1514
1515 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1516 canvas.drawRenderNode(projectingRipple.get());
1517 });
1518 auto parent = TestUtils::createNode(0, 0, 400, 400,
1519 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1520 canvas.drawRenderNode(receiverBackground.get());
1521 canvas.drawRenderNode(child.get());
1522 });
1523
1524 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001525 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craika748c082016-03-01 18:48:37 -08001526 ProjectionChildScrollTestRenderer renderer;
1527 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1528 EXPECT_EQ(2, renderer.getIndex());
1529}
1530
Chris Craik98787e62015-11-13 10:55:30 -08001531// creates a 100x100 shadow casting node with provided translationZ
1532static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
John Reck16c9d6a2015-11-17 15:51:08 -08001533 return TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001534 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
John Reck16c9d6a2015-11-17 15:51:08 -08001535 properties.setTranslationZ(translationZ);
1536 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
Chris Craik98787e62015-11-13 10:55:30 -08001537 SkPaint paint;
1538 paint.setColor(SK_ColorWHITE);
1539 canvas.drawRect(0, 0, 100, 100, paint);
Chris Craik98787e62015-11-13 10:55:30 -08001540 });
1541}
1542
Chris Craik6e068c012016-01-15 16:15:30 -08001543RENDERTHREAD_TEST(FrameBuilder, shadow) {
Chris Craikd3daa312015-11-06 10:59:56 -08001544 class ShadowTestRenderer : public TestRendererBase {
1545 public:
1546 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1547 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001548 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
Chris Craik6e068c012016-01-15 16:15:30 -08001549 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1550 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
Chris Craik98787e62015-11-13 10:55:30 -08001551
1552 Matrix4 expectedZ;
1553 expectedZ.loadTranslate(0, 0, 5);
Chris Craik6e068c012016-01-15 16:15:30 -08001554 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
Chris Craikd3daa312015-11-06 10:59:56 -08001555 }
1556 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1557 EXPECT_EQ(1, mIndex++);
1558 }
1559 };
Chris Craik161f54b2015-11-05 11:08:52 -08001560
Chris Craik8d1f2122015-11-24 16:40:09 -08001561 auto parent = TestUtils::createNode(0, 0, 200, 200,
1562 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikd3daa312015-11-06 10:59:56 -08001563 canvas.insertReorderBarrier(true);
Chris Craik98787e62015-11-13 10:55:30 -08001564 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
Chris Craikd3daa312015-11-06 10:59:56 -08001565 });
1566
Chris Craikf158b492016-01-12 14:45:08 -08001567 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001568 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craikd3daa312015-11-06 10:59:56 -08001569 ShadowTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001570 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd3daa312015-11-06 10:59:56 -08001571 EXPECT_EQ(2, renderer.getIndex());
1572}
Chris Craik76caecf2015-11-02 19:17:45 -08001573
Chris Craik6e068c012016-01-15 16:15:30 -08001574RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001575 class ShadowSaveLayerTestRenderer : public TestRendererBase {
1576 public:
1577 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1578 EXPECT_EQ(0, mIndex++);
1579 return nullptr;
1580 }
1581 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1582 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001583 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1584 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
Chris Craik98787e62015-11-13 10:55:30 -08001585 }
1586 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1587 EXPECT_EQ(2, mIndex++);
1588 }
1589 void endLayer() override {
1590 EXPECT_EQ(3, mIndex++);
1591 }
1592 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1593 EXPECT_EQ(4, mIndex++);
1594 }
1595 };
1596
Chris Craik8d1f2122015-11-24 16:40:09 -08001597 auto parent = TestUtils::createNode(0, 0, 200, 200,
1598 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001599 // save/restore outside of reorderBarrier, so they don't get moved out of place
1600 canvas.translate(20, 10);
Florin Malitaeecff562015-12-21 10:43:01 -05001601 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001602 canvas.insertReorderBarrier(true);
1603 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1604 canvas.insertReorderBarrier(false);
1605 canvas.restoreToCount(count);
1606 });
1607
Chris Craikf158b492016-01-12 14:45:08 -08001608 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001609 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001610 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001611 ShadowSaveLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001612 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001613 EXPECT_EQ(5, renderer.getIndex());
1614}
1615
Chris Craikf158b492016-01-12 14:45:08 -08001616RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001617 class ShadowHwLayerTestRenderer : public TestRendererBase {
1618 public:
1619 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1620 EXPECT_EQ(0, mIndex++);
1621 }
1622 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1623 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001624 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1625 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1626 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
Chris Craik98787e62015-11-13 10:55:30 -08001627 }
1628 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1629 EXPECT_EQ(2, mIndex++);
1630 }
1631 void endLayer() override {
1632 EXPECT_EQ(3, mIndex++);
1633 }
1634 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1635 EXPECT_EQ(4, mIndex++);
1636 }
1637 };
1638
Chris Craik8d1f2122015-11-24 16:40:09 -08001639 auto parent = TestUtils::createNode(50, 60, 150, 160,
John Reck16c9d6a2015-11-17 15:51:08 -08001640 [](RenderProperties& props, RecordingCanvas& canvas) {
1641 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001642 canvas.insertReorderBarrier(true);
Florin Malitaeecff562015-12-21 10:43:01 -05001643 canvas.save(SaveFlags::MatrixClip);
Chris Craik98787e62015-11-13 10:55:30 -08001644 canvas.translate(20, 10);
1645 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1646 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001647 });
Chris Craik98787e62015-11-13 10:55:30 -08001648 OffscreenBuffer** layerHandle = parent->getLayerHandle();
1649
1650 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1651 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1652 Matrix4 windowTransform;
1653 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1654 layer.setWindowTransform(windowTransform);
1655 *layerHandle = &layer;
1656
John Reck7db5ffb2016-01-15 13:17:09 -08001657 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik98787e62015-11-13 10:55:30 -08001658 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1659 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
Chris Craikf158b492016-01-12 14:45:08 -08001660 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001661 syncedList,
Chris Craik3a5811b2016-03-22 15:03:08 -07001662 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001663 ShadowHwLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001664 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001665 EXPECT_EQ(5, renderer.getIndex());
1666
1667 // clean up layer pointer, so we can safely destruct RenderNode
1668 *layerHandle = nullptr;
1669}
1670
Chris Craik3a5811b2016-03-22 15:03:08 -07001671RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
Chris Craik98787e62015-11-13 10:55:30 -08001672 class ShadowLayeringTestRenderer : public TestRendererBase {
1673 public:
1674 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1675 int index = mIndex++;
1676 EXPECT_TRUE(index == 0 || index == 1);
1677 }
1678 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1679 int index = mIndex++;
1680 EXPECT_TRUE(index == 2 || index == 3);
1681 }
1682 };
Chris Craik8d1f2122015-11-24 16:40:09 -08001683 auto parent = TestUtils::createNode(0, 0, 200, 200,
1684 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001685 canvas.insertReorderBarrier(true);
1686 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1687 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1688 });
1689
Chris Craikf158b492016-01-12 14:45:08 -08001690 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001691 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001692 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001693 ShadowLayeringTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001694 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001695 EXPECT_EQ(4, renderer.getIndex());
1696}
1697
John Reck16c9d6a2015-11-17 15:51:08 -08001698static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
Chris Craik76caecf2015-11-02 19:17:45 -08001699 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
Chris Craikd3daa312015-11-06 10:59:56 -08001700 class PropertyTestRenderer : public TestRendererBase {
1701 public:
1702 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1703 : mCallback(callback) {}
1704 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1705 EXPECT_EQ(mIndex++, 0);
1706 mCallback(op, state);
1707 }
1708 std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1709 };
1710
John Reck16c9d6a2015-11-17 15:51:08 -08001711 auto node = TestUtils::createNode(0, 0, 100, 100,
1712 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1713 propSetupCallback(props);
Chris Craik76caecf2015-11-02 19:17:45 -08001714 SkPaint paint;
1715 paint.setColor(SK_ColorWHITE);
1716 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001717 });
Chris Craik76caecf2015-11-02 19:17:45 -08001718
Chris Craikf158b492016-01-12 14:45:08 -08001719 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001720 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik76caecf2015-11-02 19:17:45 -08001721 PropertyTestRenderer renderer(opValidateCallback);
Chris Craikf158b492016-01-12 14:45:08 -08001722 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik76caecf2015-11-02 19:17:45 -08001723 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1724}
1725
Chris Craik3a5811b2016-03-22 15:03:08 -07001726RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
Chris Craik76caecf2015-11-02 19:17:45 -08001727 testProperty([](RenderProperties& properties) {
1728 properties.setAlpha(0.5f);
1729 properties.setHasOverlappingRendering(false);
Chris Craik76caecf2015-11-02 19:17:45 -08001730 }, [](const RectOp& op, const BakedOpState& state) {
1731 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1732 });
1733}
1734
Chris Craik3a5811b2016-03-22 15:03:08 -07001735RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
Chris Craik76caecf2015-11-02 19:17:45 -08001736 testProperty([](RenderProperties& properties) {
1737 properties.setClipToBounds(true);
1738 properties.setClipBounds(Rect(10, 20, 300, 400));
Chris Craik76caecf2015-11-02 19:17:45 -08001739 }, [](const RectOp& op, const BakedOpState& state) {
1740 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1741 << "Clip rect should be intersection of node bounds and clip bounds";
1742 });
1743}
1744
Chris Craik3a5811b2016-03-22 15:03:08 -07001745RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001746 testProperty([](RenderProperties& properties) {
1747 properties.mutableRevealClip().set(true, 50, 50, 25);
Chris Craik76caecf2015-11-02 19:17:45 -08001748 }, [](const RectOp& op, const BakedOpState& state) {
1749 ASSERT_NE(nullptr, state.roundRectClipState);
1750 EXPECT_TRUE(state.roundRectClipState->highPriority);
1751 EXPECT_EQ(25, state.roundRectClipState->radius);
1752 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1753 });
1754}
1755
Chris Craik3a5811b2016-03-22 15:03:08 -07001756RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001757 testProperty([](RenderProperties& properties) {
1758 properties.mutableOutline().setShouldClip(true);
1759 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
Chris Craik76caecf2015-11-02 19:17:45 -08001760 }, [](const RectOp& op, const BakedOpState& state) {
1761 ASSERT_NE(nullptr, state.roundRectClipState);
1762 EXPECT_FALSE(state.roundRectClipState->highPriority);
1763 EXPECT_EQ(5, state.roundRectClipState->radius);
1764 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1765 });
1766}
1767
Chris Craik3a5811b2016-03-22 15:03:08 -07001768RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
Chris Craik76caecf2015-11-02 19:17:45 -08001769 testProperty([](RenderProperties& properties) {
1770 properties.setLeftTopRightBottom(10, 10, 110, 110);
1771
1772 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1773 properties.setStaticMatrix(&staticMatrix);
1774
1775 // ignored, since static overrides animation
1776 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1777 properties.setAnimationMatrix(&animationMatrix);
1778
1779 properties.setTranslationX(10);
1780 properties.setTranslationY(20);
1781 properties.setScaleX(0.5f);
1782 properties.setScaleY(0.7f);
Chris Craik76caecf2015-11-02 19:17:45 -08001783 }, [](const RectOp& op, const BakedOpState& state) {
1784 Matrix4 matrix;
1785 matrix.loadTranslate(10, 10, 0); // left, top
1786 matrix.scale(1.2f, 1.2f, 1); // static matrix
1787 // ignore animation matrix, since static overrides it
1788
1789 // translation xy
1790 matrix.translate(10, 20);
1791
1792 // scale xy (from default pivot - center)
1793 matrix.translate(50, 50);
1794 matrix.scale(0.5f, 0.7f, 1);
1795 matrix.translate(-50, -50);
1796 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1797 << "Op draw matrix must match expected combination of transformation properties";
1798 });
1799}
Chris Craik161f54b2015-11-05 11:08:52 -08001800
Chris Craik8ecf41c2015-11-16 10:27:59 -08001801struct SaveLayerAlphaData {
1802 uint32_t layerWidth = 0;
1803 uint32_t layerHeight = 0;
1804 Rect rectClippedBounds;
1805 Matrix4 rectMatrix;
1806};
1807/**
1808 * Constructs a view to hit the temporary layer alpha property implementation:
1809 * a) 0 < alpha < 1
1810 * b) too big for layer (larger than maxTextureSize)
1811 * c) overlapping rendering content
1812 * returning observed data about layer size and content clip/transform.
1813 *
1814 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1815 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1816 */
1817void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
John Reck16c9d6a2015-11-17 15:51:08 -08001818 std::function<void(RenderProperties&)> propSetupCallback) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001819 class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1820 public:
1821 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1822 : mOutData(outData) {}
1823
1824 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1825 EXPECT_EQ(0, mIndex++);
1826 mOutData->layerWidth = width;
1827 mOutData->layerHeight = height;
1828 return nullptr;
1829 }
1830 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1831 EXPECT_EQ(1, mIndex++);
1832
1833 mOutData->rectClippedBounds = state.computedState.clippedBounds;
1834 mOutData->rectMatrix = state.computedState.transform;
1835 }
1836 void endLayer() override {
1837 EXPECT_EQ(2, mIndex++);
1838 }
1839 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1840 EXPECT_EQ(3, mIndex++);
1841 }
1842 private:
1843 SaveLayerAlphaData* mOutData;
1844 };
1845
1846 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1847 << "Node must be bigger than max texture size to exercise saveLayer codepath";
John Reck16c9d6a2015-11-17 15:51:08 -08001848 auto node = TestUtils::createNode(0, 0, 10000, 10000,
1849 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1850 properties.setHasOverlappingRendering(true);
1851 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1852 // apply other properties
1853 propSetupCallback(properties);
1854
Chris Craik8ecf41c2015-11-16 10:27:59 -08001855 SkPaint paint;
1856 paint.setColor(SK_ColorWHITE);
1857 canvas.drawRect(0, 0, 10000, 10000, paint);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001858 });
John Reck7db5ffb2016-01-15 13:17:09 -08001859 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
Chris Craik8ecf41c2015-11-16 10:27:59 -08001860
Chris Craik6e068c012016-01-15 16:15:30 -08001861 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001862 nodes, sLightGeometry, Caches::getInstance());
Chris Craik8ecf41c2015-11-16 10:27:59 -08001863 SaveLayerAlphaClipTestRenderer renderer(outObservedData);
Chris Craikf158b492016-01-12 14:45:08 -08001864 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001865
1866 // assert, since output won't be valid if we haven't seen a save layer triggered
1867 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1868}
1869
Chris Craik3a5811b2016-03-22 15:03:08 -07001870RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001871 SaveLayerAlphaData observedData;
1872 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1873 properties.setTranslationX(10); // offset rendering content
1874 properties.setTranslationY(-2000); // offset rendering content
Chris Craik8ecf41c2015-11-16 10:27:59 -08001875 });
1876 EXPECT_EQ(190u, observedData.layerWidth);
1877 EXPECT_EQ(200u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001878 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
Chris Craik8ecf41c2015-11-16 10:27:59 -08001879 << "expect content to be clipped to screen area";
1880 Matrix4 expected;
1881 expected.loadTranslate(0, -2000, 0);
1882 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1883 << "expect content to be translated as part of being clipped";
1884}
1885
Chris Craik3a5811b2016-03-22 15:03:08 -07001886RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001887 SaveLayerAlphaData observedData;
1888 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1889 // Translate and rotate the view so that the only visible part is the top left corner of
Chris Craik8d1f2122015-11-24 16:40:09 -08001890 // 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 -08001891 // bottom of the viewport.
1892 properties.setTranslationX(100);
1893 properties.setTranslationY(100);
1894 properties.setPivotX(0);
1895 properties.setPivotY(0);
1896 properties.setRotation(45);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001897 });
1898 // ceil(sqrt(2) / 2 * 200) = 142
1899 EXPECT_EQ(142u, observedData.layerWidth);
1900 EXPECT_EQ(142u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001901 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001902 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1903}
1904
Chris Craik3a5811b2016-03-22 15:03:08 -07001905RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001906 SaveLayerAlphaData observedData;
1907 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1908 properties.setPivotX(0);
1909 properties.setPivotY(0);
1910 properties.setScaleX(2);
1911 properties.setScaleY(0.5f);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001912 });
1913 EXPECT_EQ(100u, observedData.layerWidth);
1914 EXPECT_EQ(400u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001915 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001916 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1917}
1918
Chris Craik6fe991e52015-10-20 09:39:42 -07001919} // namespace uirenderer
1920} // namespace android