blob: dca56d425f3ac25b94822ac0435ff05d04757a4c [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 Craik6e068c012016-01-15 16:15:30 -080033const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
34
Chris Craik98787e62015-11-13 10:55:30 -080035
Chris Craik6fe991e52015-10-20 09:39:42 -070036/**
Chris Craik5854b342015-10-26 15:49:56 -070037 * Virtual class implemented by each test to redirect static operation / state transitions to
38 * virtual methods.
Chris Craik6fe991e52015-10-20 09:39:42 -070039 *
Chris Craik5854b342015-10-26 15:49:56 -070040 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
41 * and allows Renderer vs Dispatching behavior to be merged.
Chris Craik6fe991e52015-10-20 09:39:42 -070042 *
43 * onXXXOp methods fail by default - tests should override ops they expect
Chris Craikd3daa312015-11-06 10:59:56 -080044 * startRepaintLayer fails by default - tests should override if expected
Chris Craik6fe991e52015-10-20 09:39:42 -070045 * startFrame/endFrame do nothing by default - tests should override to intercept
46 */
Chris Craik5854b342015-10-26 15:49:56 -070047class TestRendererBase {
Chris Craik6fe991e52015-10-20 09:39:42 -070048public:
Chris Craik5854b342015-10-26 15:49:56 -070049 virtual ~TestRendererBase() {}
Chris Craikd3daa312015-11-06 10:59:56 -080050 virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
Chris Craika6ac95e2015-11-02 18:06:59 -080051 ADD_FAILURE() << "Layer creation not expected in this test";
52 return nullptr;
53 }
Chris Craik98787e62015-11-13 10:55:30 -080054 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
Chris Craika6ac95e2015-11-02 18:06:59 -080055 ADD_FAILURE() << "Layer repaint not expected in this test";
56 }
57 virtual void endLayer() {
58 ADD_FAILURE() << "Layer updates not expected in this test";
59 }
Chris Craik98787e62015-11-13 10:55:30 -080060 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
Chris Craike4db79d2015-12-22 16:32:23 -080061 virtual void endFrame(const Rect& repaintRect) {}
Chris Craik6fe991e52015-10-20 09:39:42 -070062
Chris Craik15c3f192015-12-03 12:16:56 -080063 // define virtual defaults for single draw methods
64#define X(Type) \
Chris Craika6ac95e2015-11-02 18:06:59 -080065 virtual void on##Type(const Type&, const BakedOpState&) { \
66 ADD_FAILURE() << #Type " not expected in this test"; \
67 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080068 MAP_RENDERABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080069#undef X
70
71 // define virtual defaults for merged draw methods
72#define X(Type) \
73 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
74 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
75 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080076 MAP_MERGEABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080077#undef X
78
Chris Craik5854b342015-10-26 15:49:56 -070079 int getIndex() { return mIndex; }
Chris Craik6fe991e52015-10-20 09:39:42 -070080
Chris Craik5854b342015-10-26 15:49:56 -070081protected:
82 int mIndex = 0;
83};
84
85/**
86 * Dispatches all static methods to similar formed methods on renderer, which fail by default but
Chris Craik8ecf41c2015-11-16 10:27:59 -080087 * are overridden by subclasses per test.
Chris Craik5854b342015-10-26 15:49:56 -070088 */
89class TestDispatcher {
90public:
Chris Craik15c3f192015-12-03 12:16:56 -080091 // define single op methods, which redirect to TestRendererBase
92#define X(Type) \
Chris Craik5854b342015-10-26 15:49:56 -070093 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
94 renderer.on##Type(op, state); \
Chris Craik6fe991e52015-10-20 09:39:42 -070095 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080096 MAP_RENDERABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -080097#undef X
98
99 // define merged op methods, which redirect to TestRendererBase
100#define X(Type) \
101 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
102 renderer.onMerged##Type##s(opList); \
103 }
Chris Craik7cbf63d2016-01-06 13:46:52 -0800104 MAP_MERGEABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -0800105#undef X
Chris Craik6fe991e52015-10-20 09:39:42 -0700106};
Chris Craikb565df12015-10-05 13:00:52 -0700107
Chris Craik5854b342015-10-26 15:49:56 -0700108class FailRenderer : public TestRendererBase {};
Chris Craik6fe991e52015-10-20 09:39:42 -0700109
Chris Craik3a5811b2016-03-22 15:03:08 -0700110RENDERTHREAD_TEST(FrameBuilder, simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800111 class SimpleTestRenderer : public TestRendererBase {
112 public:
Chris Craik98787e62015-11-13 10:55:30 -0800113 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800114 EXPECT_EQ(0, mIndex++);
115 EXPECT_EQ(100u, width);
116 EXPECT_EQ(200u, height);
117 }
118 void onRectOp(const RectOp& op, const BakedOpState& state) override {
119 EXPECT_EQ(1, mIndex++);
120 }
121 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
122 EXPECT_EQ(2, mIndex++);
123 }
Chris Craike4db79d2015-12-22 16:32:23 -0800124 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800125 EXPECT_EQ(3, mIndex++);
126 }
127 };
128
Chris Craik8d1f2122015-11-24 16:40:09 -0800129 auto node = TestUtils::createNode(0, 0, 100, 200,
130 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700131 SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
Chris Craikb565df12015-10-05 13:00:52 -0700132 canvas.drawRect(0, 0, 100, 200, SkPaint());
133 canvas.drawBitmap(bitmap, 10, 10, nullptr);
134 });
Chris Craikf158b492016-01-12 14:45:08 -0800135 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700136 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700137 SimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800138 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700139 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
Chris Craik6fe991e52015-10-20 09:39:42 -0700140}
141
Chris Craik3a5811b2016-03-22 15:03:08 -0700142RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
Chris Craik386aa032015-12-07 17:08:25 -0800143 class SimpleStrokeTestRenderer : public TestRendererBase {
144 public:
145 void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
146 EXPECT_EQ(0, mIndex++);
147 // even though initial bounds are empty...
148 EXPECT_TRUE(op.unmappedBounds.isEmpty())
149 << "initial bounds should be empty, since they're unstroked";
150 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
151 << "final bounds should account for stroke";
152 }
153 };
154
155 auto node = TestUtils::createNode(0, 0, 100, 200,
156 [](RenderProperties& props, RecordingCanvas& canvas) {
157 SkPaint strokedPaint;
158 strokedPaint.setStrokeWidth(10);
159 canvas.drawPoint(50, 50, strokedPaint);
160 });
Chris Craikf158b492016-01-12 14:45:08 -0800161 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700162 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik386aa032015-12-07 17:08:25 -0800163 SimpleStrokeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800164 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik386aa032015-12-07 17:08:25 -0800165 EXPECT_EQ(1, renderer.getIndex());
166}
167
Chris Craik3a5811b2016-03-22 15:03:08 -0700168RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800169 auto node = TestUtils::createNode(0, 0, 200, 200,
170 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500171 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700172 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
173 canvas.drawRect(0, 0, 400, 400, SkPaint());
174 canvas.restore();
175 });
Chris Craikf158b492016-01-12 14:45:08 -0800176 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700177 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik6fe991e52015-10-20 09:39:42 -0700178
Chris Craik5854b342015-10-26 15:49:56 -0700179 FailRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800180 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb565df12015-10-05 13:00:52 -0700181}
182
Chris Craik3a5811b2016-03-22 15:03:08 -0700183RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
Chris Craika1717272015-11-19 13:02:43 -0800184 const int LOOPS = 5;
Chris Craikd3daa312015-11-06 10:59:56 -0800185 class SimpleBatchingTestRenderer : public TestRendererBase {
186 public:
187 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800188 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
Chris Craikd3daa312015-11-06 10:59:56 -0800189 }
190 void onRectOp(const RectOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800191 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
Chris Craikd3daa312015-11-06 10:59:56 -0800192 }
193 };
194
Chris Craik8d1f2122015-11-24 16:40:09 -0800195 auto node = TestUtils::createNode(0, 0, 200, 200,
196 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik15c3f192015-12-03 12:16:56 -0800197 SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
198 kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
Chris Craikb565df12015-10-05 13:00:52 -0700199
200 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
201 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
Florin Malitaeecff562015-12-21 10:43:01 -0500202 canvas.save(SaveFlags::MatrixClip);
Chris Craika1717272015-11-19 13:02:43 -0800203 for (int i = 0; i < LOOPS; i++) {
Chris Craikb565df12015-10-05 13:00:52 -0700204 canvas.translate(0, 10);
205 canvas.drawRect(0, 0, 10, 10, SkPaint());
206 canvas.drawBitmap(bitmap, 5, 0, nullptr);
207 }
208 canvas.restore();
209 });
210
Chris Craikf158b492016-01-12 14:45:08 -0800211 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700212 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700213 SimpleBatchingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800214 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800215 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craik15c3f192015-12-03 12:16:56 -0800216 << "Expect number of ops = 2 * loop count";
Chris Craika1717272015-11-19 13:02:43 -0800217}
218
Chris Craik3a5811b2016-03-22 15:03:08 -0700219RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
Chris Craik93e53e02015-12-17 18:42:44 -0800220 class ClippedMergingTestRenderer : public TestRendererBase {
221 public:
222 void onMergedBitmapOps(const MergedBakedOpList& opList) override {
223 EXPECT_EQ(0, mIndex);
224 mIndex += opList.count;
225 EXPECT_EQ(4u, opList.count);
226 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
227 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
228 opList.clipSideFlags);
229 }
230 };
231 auto node = TestUtils::createNode(0, 0, 100, 100,
232 [](RenderProperties& props, TestCanvas& canvas) {
233 SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
234
235 // left side clipped (to inset left half)
236 canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
237 canvas.drawBitmap(bitmap, 0, 40, nullptr);
238
239 // top side clipped (to inset top half)
240 canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
241 canvas.drawBitmap(bitmap, 40, 0, nullptr);
242
243 // right side clipped (to inset right half)
244 canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
245 canvas.drawBitmap(bitmap, 80, 40, nullptr);
246
247 // bottom not clipped, just abutting (inset bottom half)
248 canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
249 canvas.drawBitmap(bitmap, 40, 70, nullptr);
250 });
251
Chris Craikf158b492016-01-12 14:45:08 -0800252 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -0700253 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik93e53e02015-12-17 18:42:44 -0800254 ClippedMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800255 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik93e53e02015-12-17 18:42:44 -0800256 EXPECT_EQ(4, renderer.getIndex());
257}
258
Chris Craik3a5811b2016-03-22 15:03:08 -0700259RENDERTHREAD_TEST(FrameBuilder, textMerging) {
Chris Craikd7448e62015-12-15 10:34:36 -0800260 class TextMergingTestRenderer : public TestRendererBase {
261 public:
262 void onMergedTextOps(const MergedBakedOpList& opList) override {
263 EXPECT_EQ(0, mIndex);
264 mIndex += opList.count;
265 EXPECT_EQ(2u, opList.count);
266 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
267 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
268 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
269 }
270 };
271 auto node = TestUtils::createNode(0, 0, 400, 400,
272 [](RenderProperties& props, TestCanvas& canvas) {
273 SkPaint paint;
274 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
275 paint.setAntiAlias(true);
276 paint.setTextSize(50);
sergeyvdccca442016-03-21 15:38:21 -0700277 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
278 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
Chris Craikd7448e62015-12-15 10:34:36 -0800279 });
Chris Craikf158b492016-01-12 14:45:08 -0800280 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -0700281 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikd7448e62015-12-15 10:34:36 -0800282 TextMergingTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800283 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd7448e62015-12-15 10:34:36 -0800284 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
285}
286
Chris Craik3a5811b2016-03-22 15:03:08 -0700287RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
Chris Craika1717272015-11-19 13:02:43 -0800288 const int LOOPS = 5;
289 class TextStrikethroughTestRenderer : public TestRendererBase {
290 public:
291 void onRectOp(const RectOp& op, const BakedOpState& state) override {
292 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
293 }
Chris Craik15c3f192015-12-03 12:16:56 -0800294 void onMergedTextOps(const MergedBakedOpList& opList) override {
295 EXPECT_EQ(0, mIndex);
296 mIndex += opList.count;
297 EXPECT_EQ(5u, opList.count);
Chris Craika1717272015-11-19 13:02:43 -0800298 }
299 };
Chris Craik8d1f2122015-11-24 16:40:09 -0800300 auto node = TestUtils::createNode(0, 0, 200, 2000,
301 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craika1717272015-11-19 13:02:43 -0800302 SkPaint textPaint;
303 textPaint.setAntiAlias(true);
304 textPaint.setTextSize(20);
305 textPaint.setStrikeThruText(true);
Chris Craik42a54072015-11-24 11:41:54 -0800306 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Chris Craika1717272015-11-19 13:02:43 -0800307 for (int i = 0; i < LOOPS; i++) {
sergeyvdccca442016-03-21 15:38:21 -0700308 TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
Chris Craika1717272015-11-19 13:02:43 -0800309 }
310 });
Chris Craikf158b492016-01-12 14:45:08 -0800311 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
Chris Craik3a5811b2016-03-22 15:03:08 -0700312 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craika1717272015-11-19 13:02:43 -0800313 TextStrikethroughTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800314 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800315 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craikd7448e62015-12-15 10:34:36 -0800316 << "Expect number of ops = 2 * loop count";
Chris Craikb565df12015-10-05 13:00:52 -0700317}
318
Chris Craik7c02cab2016-03-16 17:15:12 -0700319static auto styles = {
320 SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
321
Chris Craik3a5811b2016-03-22 15:03:08 -0700322RENDERTHREAD_TEST(FrameBuilder, textStyle) {
Chris Craik7c02cab2016-03-16 17:15:12 -0700323 class TextStyleTestRenderer : public TestRendererBase {
324 public:
325 void onMergedTextOps(const MergedBakedOpList& opList) override {
326 ASSERT_EQ(0, mIndex);
327 ASSERT_EQ(3u, opList.count);
328 mIndex += opList.count;
329
330 int index = 0;
331 for (auto style : styles) {
332 auto state = opList.states[index++];
333 ASSERT_EQ(style, state->op->paint->getStyle())
334 << "Remainder of validation relies upon stable merged order";
335 ASSERT_EQ(0, state->computedState.clipSideFlags)
336 << "Clipped bounds validation requires unclipped ops";
337 }
338
339 Rect fill = opList.states[0]->computedState.clippedBounds;
340 Rect stroke = opList.states[1]->computedState.clippedBounds;
341 EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
342 << "Stroke+Fill should be same as stroke";
343
344 EXPECT_TRUE(stroke.contains(fill));
345 EXPECT_FALSE(fill.contains(stroke));
346
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400347 // outset by half the stroke width
Chris Craik7c02cab2016-03-16 17:15:12 -0700348 Rect outsetFill(fill);
Derek Sollenberger79abbf22016-03-24 11:07:19 -0400349 outsetFill.outset(5);
Chris Craik7c02cab2016-03-16 17:15:12 -0700350 EXPECT_EQ(stroke, outsetFill);
351 }
352 };
353 auto node = TestUtils::createNode(0, 0, 400, 400,
354 [](RenderProperties& props, TestCanvas& canvas) {
355 SkPaint paint;
356 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
357 paint.setAntiAlias(true);
358 paint.setTextSize(50);
359 paint.setStrokeWidth(10);
360
361 // draw 3 copies of the same text overlapping, each with a different style.
362 // They'll get merged, but with
363 for (auto style : styles) {
364 paint.setStyle(style);
sergeyvdccca442016-03-21 15:38:21 -0700365 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
Chris Craik7c02cab2016-03-16 17:15:12 -0700366 }
367 });
368 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -0700369 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik7c02cab2016-03-16 17:15:12 -0700370 TextStyleTestRenderer renderer;
371 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
372 EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
373}
374
Chris Craikf158b492016-01-12 14:45:08 -0800375RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800376 class TextureLayerTestRenderer : public TestRendererBase {
377 public:
378 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
379 EXPECT_EQ(0, mIndex++);
Chris Craike4db79d2015-12-22 16:32:23 -0800380 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800381 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
382
383 Matrix4 expected;
384 expected.loadTranslate(5, 5, 0);
385 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
386 }
387 };
388
389 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
Chris Craik243e85b2016-03-25 15:26:11 -0700390 SkMatrix::MakeTrans(5, 5));
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800391
392 auto node = TestUtils::createNode(0, 0, 200, 200,
393 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500394 canvas.save(SaveFlags::MatrixClip);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800395 canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
396 canvas.drawLayer(layerUpdater.get());
397 canvas.restore();
398 });
Chris Craikf158b492016-01-12 14:45:08 -0800399 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700400 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800401 TextureLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800402 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800403 EXPECT_EQ(1, renderer.getIndex());
404}
405
Chris Craik3a5811b2016-03-22 15:03:08 -0700406RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
Chris Craik223e3b62016-03-10 10:27:38 -0800407 class FunctorTestRenderer : public TestRendererBase {
408 public:
409 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
410 EXPECT_EQ(0, mIndex++);
411 }
412 };
413 Functor noopFunctor;
414
415 // 1 million pixel tall view, scrolled down 80%
416 auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
417 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
418 canvas.translate(0, -800000);
419 canvas.callDrawGLFunction(&noopFunctor);
420 });
421
422 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700423 TestUtils::createSyncedNodeList(scrolledFunctorView),
424 sLightGeometry, Caches::getInstance());
Chris Craik223e3b62016-03-10 10:27:38 -0800425 FunctorTestRenderer renderer;
426 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
427 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
428}
429
Chris Craika2048482016-03-25 14:17:49 -0700430RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
431 class ColorTestRenderer : public TestRendererBase {
432 public:
433 void onColorOp(const ColorOp& op, const BakedOpState& state) override {
434 EXPECT_EQ(0, mIndex++);
435 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
436 << "Color op should be expanded to bounds of surrounding";
437 }
438 };
439
440 auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
441 [](RenderProperties& props, RecordingCanvas& canvas) {
442 props.setClipToBounds(false);
443 canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
444 });
445
446 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
447 TestUtils::createSyncedNodeList(unclippedColorView),
448 sLightGeometry, Caches::getInstance());
449 ColorTestRenderer renderer;
450 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
451 EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
452}
453
454TEST(FrameBuilder, renderNode) {
Chris Craikd3daa312015-11-06 10:59:56 -0800455 class RenderNodeTestRenderer : public TestRendererBase {
456 public:
457 void onRectOp(const RectOp& op, const BakedOpState& state) override {
458 switch(mIndex++) {
459 case 0:
Chris Craik5430ab22015-12-10 16:28:16 -0800460 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
Chris Craikd3daa312015-11-06 10:59:56 -0800461 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
462 break;
463 case 1:
464 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
465 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
466 break;
467 default:
468 ADD_FAILURE();
469 }
470 }
471 };
472
Chris Craik8d1f2122015-11-24 16:40:09 -0800473 auto child = TestUtils::createNode(10, 10, 110, 110,
474 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700475 SkPaint paint;
476 paint.setColor(SK_ColorWHITE);
477 canvas.drawRect(0, 0, 100, 100, paint);
478 });
479
Chris Craik8d1f2122015-11-24 16:40:09 -0800480 auto parent = TestUtils::createNode(0, 0, 200, 200,
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800481 [&child](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700482 SkPaint paint;
483 paint.setColor(SK_ColorDKGRAY);
484 canvas.drawRect(0, 0, 200, 200, paint);
Chris Craikb565df12015-10-05 13:00:52 -0700485
Florin Malitaeecff562015-12-21 10:43:01 -0500486 canvas.save(SaveFlags::MatrixClip);
Chris Craikddf22152015-10-14 17:42:47 -0700487 canvas.translate(40, 40);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800488 canvas.drawRenderNode(child.get());
Chris Craikddf22152015-10-14 17:42:47 -0700489 canvas.restore();
Chris Craikb565df12015-10-05 13:00:52 -0700490 });
491
Chris Craikf158b492016-01-12 14:45:08 -0800492 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700493 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700494 RenderNodeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800495 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik223e3b62016-03-10 10:27:38 -0800496 EXPECT_EQ(2, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700497}
498
Chris Craik3a5811b2016-03-22 15:03:08 -0700499RENDERTHREAD_TEST(FrameBuilder, clipped) {
Chris Craikd3daa312015-11-06 10:59:56 -0800500 class ClippedTestRenderer : public TestRendererBase {
501 public:
502 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
503 EXPECT_EQ(0, mIndex++);
504 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800505 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800506 EXPECT_TRUE(state.computedState.transform.isIdentity());
507 }
508 };
509
Chris Craik8d1f2122015-11-24 16:40:09 -0800510 auto node = TestUtils::createNode(0, 0, 200, 200,
511 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700512 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
513 canvas.drawBitmap(bitmap, 0, 0, nullptr);
514 });
Chris Craikddf22152015-10-14 17:42:47 -0700515
Chris Craikf158b492016-01-12 14:45:08 -0800516 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
Chris Craik0b7e8242015-10-28 16:50:44 -0700517 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
Chris Craik3a5811b2016-03-22 15:03:08 -0700518 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700519 ClippedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800520 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikddf22152015-10-14 17:42:47 -0700521}
522
Chris Craik3a5811b2016-03-22 15:03:08 -0700523RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800524 class SaveLayerSimpleTestRenderer : public TestRendererBase {
525 public:
526 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
527 EXPECT_EQ(0, mIndex++);
528 EXPECT_EQ(180u, width);
529 EXPECT_EQ(180u, height);
530 return nullptr;
531 }
532 void endLayer() override {
533 EXPECT_EQ(2, mIndex++);
534 }
535 void onRectOp(const RectOp& op, const BakedOpState& state) override {
536 EXPECT_EQ(1, mIndex++);
537 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
Chris Craik5430ab22015-12-10 16:28:16 -0800538 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800539 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800540
541 Matrix4 expectedTransform;
542 expectedTransform.loadTranslate(-10, -10, 0);
543 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
544 }
545 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
546 EXPECT_EQ(3, mIndex++);
547 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800548 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800549 EXPECT_TRUE(state.computedState.transform.isIdentity());
550 }
551 };
552
Chris Craik8d1f2122015-11-24 16:40:09 -0800553 auto node = TestUtils::createNode(0, 0, 200, 200,
554 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500555 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700556 canvas.drawRect(10, 10, 190, 190, SkPaint());
557 canvas.restore();
558 });
Chris Craikf158b492016-01-12 14:45:08 -0800559 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700560 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700561 SaveLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800562 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700563 EXPECT_EQ(4, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700564}
Chris Craik6fe991e52015-10-20 09:39:42 -0700565
Chris Craik3a5811b2016-03-22 15:03:08 -0700566RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
Chris Craikd3daa312015-11-06 10:59:56 -0800567 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
568 * - startTemporaryLayer2, rect2 endLayer2
569 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
570 * - startFrame, layerOp1, endFrame
571 */
572 class SaveLayerNestedTestRenderer : public TestRendererBase {
573 public:
574 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
575 const int index = mIndex++;
576 if (index == 0) {
577 EXPECT_EQ(400u, width);
578 EXPECT_EQ(400u, height);
579 return (OffscreenBuffer*) 0x400;
580 } else if (index == 3) {
581 EXPECT_EQ(800u, width);
582 EXPECT_EQ(800u, height);
583 return (OffscreenBuffer*) 0x800;
584 } else { ADD_FAILURE(); }
585 return (OffscreenBuffer*) nullptr;
586 }
587 void endLayer() override {
588 int index = mIndex++;
589 EXPECT_TRUE(index == 2 || index == 6);
590 }
Chris Craik98787e62015-11-13 10:55:30 -0800591 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800592 EXPECT_EQ(7, mIndex++);
593 }
Chris Craike4db79d2015-12-22 16:32:23 -0800594 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800595 EXPECT_EQ(9, mIndex++);
596 }
597 void onRectOp(const RectOp& op, const BakedOpState& state) override {
598 const int index = mIndex++;
599 if (index == 1) {
Chris Craik5430ab22015-12-10 16:28:16 -0800600 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
Chris Craikd3daa312015-11-06 10:59:56 -0800601 } else if (index == 4) {
Chris Craik5430ab22015-12-10 16:28:16 -0800602 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
Chris Craikd3daa312015-11-06 10:59:56 -0800603 } else { ADD_FAILURE(); }
604 }
605 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
606 const int index = mIndex++;
607 if (index == 5) {
608 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800609 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
Chris Craikd3daa312015-11-06 10:59:56 -0800610 } else if (index == 8) {
611 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800612 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
Chris Craikd3daa312015-11-06 10:59:56 -0800613 } else { ADD_FAILURE(); }
614 }
615 };
616
Chris Craik8d1f2122015-11-24 16:40:09 -0800617 auto node = TestUtils::createNode(0, 0, 800, 800,
618 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500619 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700620 {
621 canvas.drawRect(0, 0, 800, 800, SkPaint());
Florin Malitaeecff562015-12-21 10:43:01 -0500622 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700623 {
624 canvas.drawRect(0, 0, 400, 400, SkPaint());
625 }
626 canvas.restore();
627 }
628 canvas.restore();
629 });
630
Chris Craikf158b492016-01-12 14:45:08 -0800631 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
Chris Craik3a5811b2016-03-22 15:03:08 -0700632 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700633 SaveLayerNestedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800634 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700635 EXPECT_EQ(10, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700636}
Chris Craik6fe991e52015-10-20 09:39:42 -0700637
Chris Craik3a5811b2016-03-22 15:03:08 -0700638RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800639 auto node = TestUtils::createNode(0, 0, 200, 200,
640 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500641 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700642 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
Florin Malitaeecff562015-12-21 10:43:01 -0500643 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700644
645 // draw within save layer may still be recorded, but shouldn't be drawn
646 canvas.drawRect(200, 200, 400, 400, SkPaint());
647
648 canvas.restore();
649 canvas.restore();
650 });
Chris Craikf158b492016-01-12 14:45:08 -0800651 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700652 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik6fe991e52015-10-20 09:39:42 -0700653
Chris Craik5854b342015-10-26 15:49:56 -0700654 FailRenderer renderer;
Chris Craik6fe991e52015-10-20 09:39:42 -0700655 // should see no ops, even within the layer, since the layer should be rejected
Chris Craikf158b492016-01-12 14:45:08 -0800656 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700657}
658
Chris Craik3a5811b2016-03-22 15:03:08 -0700659RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800660 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
661 public:
662 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
663 EXPECT_EQ(0, mIndex++);
664 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800665 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800666 EXPECT_TRUE(state.computedState.transform.isIdentity());
667 }
668 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
669 EXPECT_EQ(1, mIndex++);
670 ASSERT_NE(nullptr, op.paint);
671 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
672 }
673 void onRectOp(const RectOp& op, const BakedOpState& state) override {
674 EXPECT_EQ(2, mIndex++);
675 EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
676 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
677 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
678 EXPECT_TRUE(state.computedState.transform.isIdentity());
679 }
680 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
681 EXPECT_EQ(3, mIndex++);
682 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800683 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800684 EXPECT_TRUE(state.computedState.transform.isIdentity());
685 }
686 };
687
688 auto node = TestUtils::createNode(0, 0, 200, 200,
689 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500690 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
Chris Craikb87eadd2016-01-06 09:16:05 -0800691 canvas.drawRect(0, 0, 200, 200, SkPaint());
692 canvas.restore();
693 });
Chris Craikf158b492016-01-12 14:45:08 -0800694 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700695 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800696 SaveLayerUnclippedSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800697 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800698 EXPECT_EQ(4, renderer.getIndex());
699}
700
Chris Craik3a5811b2016-03-22 15:03:08 -0700701RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800702 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
703 public:
704 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
705 int index = mIndex++;
706 EXPECT_GT(4, index);
707 EXPECT_EQ(5, op.unmappedBounds.getWidth());
708 EXPECT_EQ(5, op.unmappedBounds.getHeight());
709 if (index == 0) {
710 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
711 } else if (index == 1) {
712 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
713 } else if (index == 2) {
714 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
715 } else if (index == 3) {
716 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
717 }
718 }
719 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
720 EXPECT_EQ(4, mIndex++);
721 ASSERT_EQ(op.vertexCount, 16u);
722 for (size_t i = 0; i < op.vertexCount; i++) {
723 auto v = op.vertices[i];
724 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
725 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
726 }
727 }
728 void onRectOp(const RectOp& op, const BakedOpState& state) override {
729 EXPECT_EQ(5, mIndex++);
730 }
731 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
732 EXPECT_LT(5, mIndex++);
733 }
734 };
735
736 auto node = TestUtils::createNode(0, 0, 200, 200,
737 [](RenderProperties& props, RecordingCanvas& canvas) {
738
Florin Malitaeecff562015-12-21 10:43:01 -0500739 int restoreTo = canvas.save(SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800740 canvas.scale(2, 2);
Florin Malitaeecff562015-12-21 10:43:01 -0500741 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
742 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
743 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
744 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800745 canvas.drawRect(0, 0, 100, 100, SkPaint());
746 canvas.restoreToCount(restoreTo);
747 });
Chris Craikf158b492016-01-12 14:45:08 -0800748 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700749 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800750 SaveLayerUnclippedMergedClearsTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800751 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800752 EXPECT_EQ(10, renderer.getIndex())
753 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
754}
755
Chris Craik3a5811b2016-03-22 15:03:08 -0700756RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
Chris Craik4876de12016-02-25 16:54:08 -0800757 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
758 public:
759 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
760 EXPECT_EQ(0, mIndex++);
761 }
762 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
763 EXPECT_EQ(1, mIndex++);
764 ASSERT_NE(nullptr, op.paint);
765 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
766 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
767 << "Expect dirty rect as clip";
768 ASSERT_NE(nullptr, state.computedState.clipState);
769 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
770 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
771 }
772 void onRectOp(const RectOp& op, const BakedOpState& state) override {
773 EXPECT_EQ(2, mIndex++);
774 }
775 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
776 EXPECT_EQ(3, mIndex++);
777 }
778 };
779
780 auto node = TestUtils::createNode(0, 0, 200, 200,
781 [](RenderProperties& props, RecordingCanvas& canvas) {
782 // save smaller than clip, so we get unclipped behavior
783 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
784 canvas.drawRect(0, 0, 200, 200, SkPaint());
785 canvas.restore();
786 });
787
788 // draw with partial screen dirty, and assert we see that rect later
789 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700790 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800791 SaveLayerUnclippedClearClipTestRenderer renderer;
792 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
793 EXPECT_EQ(4, renderer.getIndex());
794}
795
Chris Craik3a5811b2016-03-22 15:03:08 -0700796RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
Chris Craik4876de12016-02-25 16:54:08 -0800797 auto node = TestUtils::createNode(0, 0, 200, 200,
798 [](RenderProperties& props, RecordingCanvas& canvas) {
799 // unclipped savelayer + rect both in area that won't intersect with dirty
800 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
801 canvas.drawRect(100, 100, 200, 200, SkPaint());
802 canvas.restore();
803 });
804
805 // draw with partial screen dirty that doesn't intersect with savelayer
806 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700807 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800808 FailRenderer renderer;
809 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
810}
811
Chris Craikb87eadd2016-01-06 09:16:05 -0800812/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
813 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
814 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
815 */
Chris Craik3a5811b2016-03-22 15:03:08 -0700816RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800817 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
818 public:
819 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
820 EXPECT_EQ(0, mIndex++); // savelayer first
821 return (OffscreenBuffer*)0xabcd;
822 }
823 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
824 int index = mIndex++;
825 EXPECT_TRUE(index == 1 || index == 7);
826 }
827 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
828 int index = mIndex++;
829 EXPECT_TRUE(index == 2 || index == 8);
830 }
831 void onRectOp(const RectOp& op, const BakedOpState& state) override {
832 EXPECT_EQ(3, mIndex++);
833 Matrix4 expected;
834 expected.loadTranslate(-100, -100, 0);
835 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
836 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
837 }
838 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
839 int index = mIndex++;
840 EXPECT_TRUE(index == 4 || index == 10);
841 }
842 void endLayer() override {
843 EXPECT_EQ(5, mIndex++);
844 }
845 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
846 EXPECT_EQ(6, mIndex++);
847 }
848 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
849 EXPECT_EQ(9, mIndex++);
850 }
851 void endFrame(const Rect& repaintRect) override {
852 EXPECT_EQ(11, mIndex++);
853 }
854 };
855
856 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
857 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500858 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
859 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
860 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
Chris Craikb87eadd2016-01-06 09:16:05 -0800861 canvas.drawRect(200, 200, 300, 300, SkPaint());
862 canvas.restore();
863 canvas.restore();
864 canvas.restore();
865 });
Chris Craikf158b492016-01-12 14:45:08 -0800866 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
Chris Craik3a5811b2016-03-22 15:03:08 -0700867 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800868 SaveLayerUnclippedComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800869 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800870 EXPECT_EQ(12, renderer.getIndex());
871}
872
Chris Craikf158b492016-01-12 14:45:08 -0800873RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800874 class HwLayerSimpleTestRenderer : public TestRendererBase {
875 public:
Chris Craik98787e62015-11-13 10:55:30 -0800876 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800877 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -0800878 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
879 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
880 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
Chris Craikd3daa312015-11-06 10:59:56 -0800881 }
882 void onRectOp(const RectOp& op, const BakedOpState& state) override {
883 EXPECT_EQ(1, mIndex++);
884
885 EXPECT_TRUE(state.computedState.transform.isIdentity())
886 << "Transform should be reset within layer";
887
Chris Craike4db79d2015-12-22 16:32:23 -0800888 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
Chris Craikd3daa312015-11-06 10:59:56 -0800889 << "Damage rect should be used to clip layer content";
890 }
891 void endLayer() override {
892 EXPECT_EQ(2, mIndex++);
893 }
Chris Craik98787e62015-11-13 10:55:30 -0800894 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800895 EXPECT_EQ(3, mIndex++);
896 }
897 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
898 EXPECT_EQ(4, mIndex++);
899 }
Chris Craike4db79d2015-12-22 16:32:23 -0800900 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800901 EXPECT_EQ(5, mIndex++);
902 }
903 };
904
Chris Craik8d1f2122015-11-24 16:40:09 -0800905 auto node = TestUtils::createNode(10, 10, 110, 110,
John Reck16c9d6a2015-11-17 15:51:08 -0800906 [](RenderProperties& props, RecordingCanvas& canvas) {
907 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700908 SkPaint paint;
909 paint.setColor(SK_ColorWHITE);
910 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -0800911 });
Chris Craik98787e62015-11-13 10:55:30 -0800912 OffscreenBuffer** layerHandle = node->getLayerHandle();
Chris Craik0b7e8242015-10-28 16:50:44 -0700913
Chris Craik98787e62015-11-13 10:55:30 -0800914 // create RenderNode's layer here in same way prepareTree would
915 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
916 *layerHandle = &layer;
Chris Craik0b7e8242015-10-28 16:50:44 -0700917
John Reck7db5ffb2016-01-15 13:17:09 -0800918 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
Chris Craik0b7e8242015-10-28 16:50:44 -0700919
920 // only enqueue partial damage
Chris Craik98787e62015-11-13 10:55:30 -0800921 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -0700922 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
923
Chris Craikf158b492016-01-12 14:45:08 -0800924 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700925 syncedNodeList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -0700926 HwLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800927 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700928 EXPECT_EQ(6, renderer.getIndex());
929
930 // clean up layer pointer, so we can safely destruct RenderNode
Chris Craik98787e62015-11-13 10:55:30 -0800931 *layerHandle = nullptr;
Chris Craik0b7e8242015-10-28 16:50:44 -0700932}
933
Chris Craikf158b492016-01-12 14:45:08 -0800934RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
Chris Craikd3daa312015-11-06 10:59:56 -0800935 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
936 * - startRepaintLayer(child), rect(grey), endLayer
937 * - startTemporaryLayer, drawLayer(child), endLayer
938 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
939 * - startFrame, drawLayer(parent), endLayerb
940 */
941 class HwLayerComplexTestRenderer : public TestRendererBase {
942 public:
943 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
944 EXPECT_EQ(3, mIndex++); // savelayer first
945 return (OffscreenBuffer*)0xabcd;
946 }
Chris Craik98787e62015-11-13 10:55:30 -0800947 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800948 int index = mIndex++;
949 if (index == 0) {
950 // starting inner layer
Chris Craik98787e62015-11-13 10:55:30 -0800951 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
952 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800953 } else if (index == 6) {
954 // starting outer layer
Chris Craik98787e62015-11-13 10:55:30 -0800955 EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
956 EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800957 } else { ADD_FAILURE(); }
958 }
959 void onRectOp(const RectOp& op, const BakedOpState& state) override {
960 int index = mIndex++;
961 if (index == 1) {
962 // inner layer's rect (white)
963 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
964 } else if (index == 7) {
965 // outer layer's rect (grey)
966 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
967 } else { ADD_FAILURE(); }
968 }
969 void endLayer() override {
970 int index = mIndex++;
971 EXPECT_TRUE(index == 2 || index == 5 || index == 9);
972 }
Chris Craik98787e62015-11-13 10:55:30 -0800973 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800974 EXPECT_EQ(10, mIndex++);
975 }
976 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
Chris Craik98787e62015-11-13 10:55:30 -0800977 OffscreenBuffer* layer = *op.layerHandle;
Chris Craikd3daa312015-11-06 10:59:56 -0800978 int index = mIndex++;
979 if (index == 4) {
Chris Craik98787e62015-11-13 10:55:30 -0800980 EXPECT_EQ(100u, layer->viewportWidth);
981 EXPECT_EQ(100u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800982 } else if (index == 8) {
983 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
984 } else if (index == 11) {
Chris Craik98787e62015-11-13 10:55:30 -0800985 EXPECT_EQ(200u, layer->viewportWidth);
986 EXPECT_EQ(200u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800987 } else { ADD_FAILURE(); }
988 }
Chris Craike4db79d2015-12-22 16:32:23 -0800989 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800990 EXPECT_EQ(12, mIndex++);
991 }
992 };
993
John Reck16c9d6a2015-11-17 15:51:08 -0800994 auto child = TestUtils::createNode(50, 50, 150, 150,
995 [](RenderProperties& props, RecordingCanvas& canvas) {
996 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700997 SkPaint paint;
998 paint.setColor(SK_ColorWHITE);
999 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001000 });
Chris Craik98787e62015-11-13 10:55:30 -08001001 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1002 *(child->getLayerHandle()) = &childLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001003
1004 RenderNode* childPtr = child.get();
John Reck16c9d6a2015-11-17 15:51:08 -08001005 auto parent = TestUtils::createNode(0, 0, 200, 200,
1006 [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
1007 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001008 SkPaint paint;
1009 paint.setColor(SK_ColorDKGRAY);
1010 canvas.drawRect(0, 0, 200, 200, paint);
1011
Florin Malitaeecff562015-12-21 10:43:01 -05001012 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001013 canvas.drawRenderNode(childPtr);
1014 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001015 });
Chris Craik98787e62015-11-13 10:55:30 -08001016 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1017 *(parent->getLayerHandle()) = &parentLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -07001018
John Reck7db5ffb2016-01-15 13:17:09 -08001019 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik0b7e8242015-10-28 16:50:44 -07001020
Chris Craik98787e62015-11-13 10:55:30 -08001021 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001022 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1023 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1024
Chris Craikf158b492016-01-12 14:45:08 -08001025 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001026 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -07001027 HwLayerComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001028 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001029 EXPECT_EQ(13, renderer.getIndex());
1030
1031 // clean up layer pointers, so we can safely destruct RenderNodes
1032 *(child->getLayerHandle()) = nullptr;
1033 *(parent->getLayerHandle()) = nullptr;
1034}
1035
Chris Craik161f54b2015-11-05 11:08:52 -08001036static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1037 SkPaint paint;
1038 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
1039 canvas->drawRect(0, 0, 100, 100, paint);
1040}
1041static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
John Reck16c9d6a2015-11-17 15:51:08 -08001042 auto node = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001043 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001044 drawOrderedRect(&canvas, expectedDrawOrder);
1045 });
1046 node->mutateStagingProperties().setTranslationZ(z);
1047 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1048 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1049}
Chris Craik3a5811b2016-03-22 15:03:08 -07001050RENDERTHREAD_TEST(FrameBuilder, zReorder) {
Chris Craikd3daa312015-11-06 10:59:56 -08001051 class ZReorderTestRenderer : public TestRendererBase {
1052 public:
1053 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1054 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1055 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1056 }
1057 };
1058
John Reck16c9d6a2015-11-17 15:51:08 -08001059 auto parent = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001060 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001061 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1062 drawOrderedRect(&canvas, 1);
1063 canvas.insertReorderBarrier(true);
1064 drawOrderedNode(&canvas, 6, 2.0f);
1065 drawOrderedRect(&canvas, 3);
1066 drawOrderedNode(&canvas, 4, 0.0f);
1067 drawOrderedRect(&canvas, 5);
1068 drawOrderedNode(&canvas, 2, -2.0f);
1069 drawOrderedNode(&canvas, 7, 2.0f);
1070 canvas.insertReorderBarrier(false);
1071 drawOrderedRect(&canvas, 8);
1072 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1073 });
Chris Craikf158b492016-01-12 14:45:08 -08001074 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001075 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik161f54b2015-11-05 11:08:52 -08001076 ZReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001077 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik161f54b2015-11-05 11:08:52 -08001078 EXPECT_EQ(10, renderer.getIndex());
1079};
1080
Chris Craik3a5811b2016-03-22 15:03:08 -07001081RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
Chris Craik8d1f2122015-11-24 16:40:09 -08001082 static const int scrollX = 5;
1083 static const int scrollY = 10;
1084 class ProjectionReorderTestRenderer : public TestRendererBase {
1085 public:
1086 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1087 const int index = mIndex++;
1088
1089 Matrix4 expectedMatrix;
1090 switch (index) {
1091 case 0:
1092 EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1093 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1094 expectedMatrix.loadIdentity();
Chris Craik678ff812016-03-01 13:27:54 -08001095 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001096 break;
1097 case 1:
1098 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1099 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
Chris Craik678ff812016-03-01 13:27:54 -08001100 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1101 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1102 EXPECT_EQ(Rect(-35, -30, 45, 50),
1103 Rect(state.computedState.localProjectionPathMask->getBounds()));
Chris Craik8d1f2122015-11-24 16:40:09 -08001104 break;
1105 case 2:
1106 EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1107 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1108 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
Chris Craik678ff812016-03-01 13:27:54 -08001109 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001110 break;
1111 default:
1112 ADD_FAILURE();
1113 }
Chris Craik678ff812016-03-01 13:27:54 -08001114 EXPECT_EQ(expectedMatrix, state.computedState.transform);
Chris Craik8d1f2122015-11-24 16:40:09 -08001115 }
1116 };
1117
1118 /**
1119 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1120 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1121 * draw, but because it is projected backwards, it's drawn in between B and C.
1122 *
1123 * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1124 * (which isn't affected by scroll).
1125 */
1126 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1127 [](RenderProperties& properties, RecordingCanvas& canvas) {
1128 properties.setProjectionReceiver(true);
1129 // scroll doesn't apply to background, so undone via translationX/Y
1130 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1131 properties.setTranslationX(scrollX);
1132 properties.setTranslationY(scrollY);
1133
1134 SkPaint paint;
1135 paint.setColor(SK_ColorWHITE);
1136 canvas.drawRect(0, 0, 100, 100, paint);
1137 });
1138 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1139 [](RenderProperties& properties, RecordingCanvas& canvas) {
1140 properties.setProjectBackwards(true);
1141 properties.setClipToBounds(false);
1142 SkPaint paint;
1143 paint.setColor(SK_ColorDKGRAY);
1144 canvas.drawRect(-10, -10, 60, 60, paint);
1145 });
1146 auto child = TestUtils::createNode(0, 50, 100, 100,
1147 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1148 SkPaint paint;
1149 paint.setColor(SK_ColorBLUE);
1150 canvas.drawRect(0, 0, 100, 50, paint);
1151 canvas.drawRenderNode(projectingRipple.get());
1152 });
1153 auto parent = TestUtils::createNode(0, 0, 100, 100,
1154 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
Chris Craik678ff812016-03-01 13:27:54 -08001155 // Set a rect outline for the projecting ripple to be masked against.
1156 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1157
Florin Malitaeecff562015-12-21 10:43:01 -05001158 canvas.save(SaveFlags::MatrixClip);
Chris Craik8d1f2122015-11-24 16:40:09 -08001159 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1160 canvas.drawRenderNode(receiverBackground.get());
1161 canvas.drawRenderNode(child.get());
1162 canvas.restore();
1163 });
1164
Chris Craikf158b492016-01-12 14:45:08 -08001165 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001166 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik8d1f2122015-11-24 16:40:09 -08001167 ProjectionReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001168 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8d1f2122015-11-24 16:40:09 -08001169 EXPECT_EQ(3, renderer.getIndex());
1170}
1171
Chris Craik678ff812016-03-01 13:27:54 -08001172RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1173 static const int scrollX = 5;
1174 static const int scrollY = 10;
1175 class ProjectionHwLayerTestRenderer : public TestRendererBase {
1176 public:
1177 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1178 EXPECT_EQ(0, mIndex++);
1179 }
1180 void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1181 EXPECT_EQ(1, mIndex++);
1182 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1183 }
1184 void endLayer() override {
1185 EXPECT_EQ(2, mIndex++);
1186 }
1187 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1188 EXPECT_EQ(3, mIndex++);
1189 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1190 }
1191 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1192 EXPECT_EQ(4, mIndex++);
1193 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1194 Matrix4 expected;
1195 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1196 EXPECT_EQ(expected, state.computedState.transform);
1197 EXPECT_EQ(Rect(-85, -80, 295, 300),
1198 Rect(state.computedState.localProjectionPathMask->getBounds()));
1199 }
1200 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1201 EXPECT_EQ(5, mIndex++);
1202 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1203 }
1204 };
1205 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1206 [](RenderProperties& properties, RecordingCanvas& canvas) {
1207 properties.setProjectionReceiver(true);
1208 // scroll doesn't apply to background, so undone via translationX/Y
1209 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1210 properties.setTranslationX(scrollX);
1211 properties.setTranslationY(scrollY);
1212
1213 canvas.drawRect(0, 0, 400, 400, SkPaint());
1214 });
1215 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1216 [](RenderProperties& properties, RecordingCanvas& canvas) {
1217 properties.setProjectBackwards(true);
1218 properties.setClipToBounds(false);
1219 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1220 });
1221 auto child = TestUtils::createNode(100, 100, 300, 300,
1222 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1223 properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1224 canvas.drawRenderNode(projectingRipple.get());
1225 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1226 });
1227 auto parent = TestUtils::createNode(0, 0, 400, 400,
1228 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1229 // Set a rect outline for the projecting ripple to be masked against.
1230 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1231 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1232 canvas.drawRenderNode(receiverBackground.get());
1233 canvas.drawRenderNode(child.get());
1234 });
1235
1236 OffscreenBuffer** layerHandle = child->getLayerHandle();
1237
1238 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1239 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1240 Matrix4 windowTransform;
1241 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1242 layer.setWindowTransform(windowTransform);
1243 *layerHandle = &layer;
1244
1245 auto syncedList = TestUtils::createSyncedNodeList(parent);
1246 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1247 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1248 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001249 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik678ff812016-03-01 13:27:54 -08001250 ProjectionHwLayerTestRenderer renderer;
1251 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1252 EXPECT_EQ(6, renderer.getIndex());
1253
1254 // clean up layer pointer, so we can safely destruct RenderNode
1255 *layerHandle = nullptr;
1256}
1257
Chris Craika748c082016-03-01 18:48:37 -08001258RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1259 static const int scrollX = 500000;
1260 static const int scrollY = 0;
1261 class ProjectionChildScrollTestRenderer : public TestRendererBase {
1262 public:
1263 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1264 EXPECT_EQ(0, mIndex++);
1265 EXPECT_TRUE(state.computedState.transform.isIdentity());
1266 }
1267 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1268 EXPECT_EQ(1, mIndex++);
1269 ASSERT_NE(nullptr, state.computedState.clipState);
1270 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1271 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1272 EXPECT_TRUE(state.computedState.transform.isIdentity());
1273 }
1274 };
1275 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1276 [](RenderProperties& properties, RecordingCanvas& canvas) {
1277 properties.setProjectionReceiver(true);
1278 canvas.drawRect(0, 0, 400, 400, SkPaint());
1279 });
1280 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1281 [](RenderProperties& properties, RecordingCanvas& canvas) {
1282 // scroll doesn't apply to background, so undone via translationX/Y
1283 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1284 properties.setTranslationX(scrollX);
1285 properties.setTranslationY(scrollY);
1286 properties.setProjectBackwards(true);
1287 properties.setClipToBounds(false);
1288 canvas.drawOval(0, 0, 200, 200, SkPaint());
1289 });
1290 auto child = TestUtils::createNode(0, 0, 400, 400,
1291 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1292 // Record time clip will be ignored by projectee
1293 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1294
1295 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1296 canvas.drawRenderNode(projectingRipple.get());
1297 });
1298 auto parent = TestUtils::createNode(0, 0, 400, 400,
1299 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1300 canvas.drawRenderNode(receiverBackground.get());
1301 canvas.drawRenderNode(child.get());
1302 });
1303
1304 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001305 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craika748c082016-03-01 18:48:37 -08001306 ProjectionChildScrollTestRenderer renderer;
1307 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1308 EXPECT_EQ(2, renderer.getIndex());
1309}
1310
Chris Craik98787e62015-11-13 10:55:30 -08001311// creates a 100x100 shadow casting node with provided translationZ
1312static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
John Reck16c9d6a2015-11-17 15:51:08 -08001313 return TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001314 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
John Reck16c9d6a2015-11-17 15:51:08 -08001315 properties.setTranslationZ(translationZ);
1316 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
Chris Craik98787e62015-11-13 10:55:30 -08001317 SkPaint paint;
1318 paint.setColor(SK_ColorWHITE);
1319 canvas.drawRect(0, 0, 100, 100, paint);
Chris Craik98787e62015-11-13 10:55:30 -08001320 });
1321}
1322
Chris Craik6e068c012016-01-15 16:15:30 -08001323RENDERTHREAD_TEST(FrameBuilder, shadow) {
Chris Craikd3daa312015-11-06 10:59:56 -08001324 class ShadowTestRenderer : public TestRendererBase {
1325 public:
1326 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1327 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001328 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
Chris Craik6e068c012016-01-15 16:15:30 -08001329 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1330 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
Chris Craik98787e62015-11-13 10:55:30 -08001331
1332 Matrix4 expectedZ;
1333 expectedZ.loadTranslate(0, 0, 5);
Chris Craik6e068c012016-01-15 16:15:30 -08001334 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
Chris Craikd3daa312015-11-06 10:59:56 -08001335 }
1336 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1337 EXPECT_EQ(1, mIndex++);
1338 }
1339 };
Chris Craik161f54b2015-11-05 11:08:52 -08001340
Chris Craik8d1f2122015-11-24 16:40:09 -08001341 auto parent = TestUtils::createNode(0, 0, 200, 200,
1342 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikd3daa312015-11-06 10:59:56 -08001343 canvas.insertReorderBarrier(true);
Chris Craik98787e62015-11-13 10:55:30 -08001344 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
Chris Craikd3daa312015-11-06 10:59:56 -08001345 });
1346
Chris Craikf158b492016-01-12 14:45:08 -08001347 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001348 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craikd3daa312015-11-06 10:59:56 -08001349 ShadowTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001350 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd3daa312015-11-06 10:59:56 -08001351 EXPECT_EQ(2, renderer.getIndex());
1352}
Chris Craik76caecf2015-11-02 19:17:45 -08001353
Chris Craik6e068c012016-01-15 16:15:30 -08001354RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001355 class ShadowSaveLayerTestRenderer : public TestRendererBase {
1356 public:
1357 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1358 EXPECT_EQ(0, mIndex++);
1359 return nullptr;
1360 }
1361 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1362 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001363 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1364 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
Chris Craik98787e62015-11-13 10:55:30 -08001365 }
1366 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1367 EXPECT_EQ(2, mIndex++);
1368 }
1369 void endLayer() override {
1370 EXPECT_EQ(3, mIndex++);
1371 }
1372 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1373 EXPECT_EQ(4, mIndex++);
1374 }
1375 };
1376
Chris Craik8d1f2122015-11-24 16:40:09 -08001377 auto parent = TestUtils::createNode(0, 0, 200, 200,
1378 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001379 // save/restore outside of reorderBarrier, so they don't get moved out of place
1380 canvas.translate(20, 10);
Florin Malitaeecff562015-12-21 10:43:01 -05001381 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001382 canvas.insertReorderBarrier(true);
1383 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1384 canvas.insertReorderBarrier(false);
1385 canvas.restoreToCount(count);
1386 });
1387
Chris Craikf158b492016-01-12 14:45:08 -08001388 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001389 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001390 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001391 ShadowSaveLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001392 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001393 EXPECT_EQ(5, renderer.getIndex());
1394}
1395
Chris Craikf158b492016-01-12 14:45:08 -08001396RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001397 class ShadowHwLayerTestRenderer : public TestRendererBase {
1398 public:
1399 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1400 EXPECT_EQ(0, mIndex++);
1401 }
1402 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1403 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001404 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1405 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1406 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
Chris Craik98787e62015-11-13 10:55:30 -08001407 }
1408 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1409 EXPECT_EQ(2, mIndex++);
1410 }
1411 void endLayer() override {
1412 EXPECT_EQ(3, mIndex++);
1413 }
1414 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1415 EXPECT_EQ(4, mIndex++);
1416 }
1417 };
1418
Chris Craik8d1f2122015-11-24 16:40:09 -08001419 auto parent = TestUtils::createNode(50, 60, 150, 160,
John Reck16c9d6a2015-11-17 15:51:08 -08001420 [](RenderProperties& props, RecordingCanvas& canvas) {
1421 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001422 canvas.insertReorderBarrier(true);
Florin Malitaeecff562015-12-21 10:43:01 -05001423 canvas.save(SaveFlags::MatrixClip);
Chris Craik98787e62015-11-13 10:55:30 -08001424 canvas.translate(20, 10);
1425 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1426 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001427 });
Chris Craik98787e62015-11-13 10:55:30 -08001428 OffscreenBuffer** layerHandle = parent->getLayerHandle();
1429
1430 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1431 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1432 Matrix4 windowTransform;
1433 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1434 layer.setWindowTransform(windowTransform);
1435 *layerHandle = &layer;
1436
John Reck7db5ffb2016-01-15 13:17:09 -08001437 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik98787e62015-11-13 10:55:30 -08001438 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1439 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
Chris Craikf158b492016-01-12 14:45:08 -08001440 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001441 syncedList,
Chris Craik3a5811b2016-03-22 15:03:08 -07001442 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001443 ShadowHwLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001444 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001445 EXPECT_EQ(5, renderer.getIndex());
1446
1447 // clean up layer pointer, so we can safely destruct RenderNode
1448 *layerHandle = nullptr;
1449}
1450
Chris Craik3a5811b2016-03-22 15:03:08 -07001451RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
Chris Craik98787e62015-11-13 10:55:30 -08001452 class ShadowLayeringTestRenderer : public TestRendererBase {
1453 public:
1454 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1455 int index = mIndex++;
1456 EXPECT_TRUE(index == 0 || index == 1);
1457 }
1458 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1459 int index = mIndex++;
1460 EXPECT_TRUE(index == 2 || index == 3);
1461 }
1462 };
Chris Craik8d1f2122015-11-24 16:40:09 -08001463 auto parent = TestUtils::createNode(0, 0, 200, 200,
1464 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001465 canvas.insertReorderBarrier(true);
1466 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1467 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1468 });
1469
Chris Craikf158b492016-01-12 14:45:08 -08001470 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001471 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001472 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001473 ShadowLayeringTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001474 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001475 EXPECT_EQ(4, renderer.getIndex());
1476}
1477
John Reck16c9d6a2015-11-17 15:51:08 -08001478static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
Chris Craik76caecf2015-11-02 19:17:45 -08001479 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
Chris Craikd3daa312015-11-06 10:59:56 -08001480 class PropertyTestRenderer : public TestRendererBase {
1481 public:
1482 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1483 : mCallback(callback) {}
1484 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1485 EXPECT_EQ(mIndex++, 0);
1486 mCallback(op, state);
1487 }
1488 std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1489 };
1490
John Reck16c9d6a2015-11-17 15:51:08 -08001491 auto node = TestUtils::createNode(0, 0, 100, 100,
1492 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1493 propSetupCallback(props);
Chris Craik76caecf2015-11-02 19:17:45 -08001494 SkPaint paint;
1495 paint.setColor(SK_ColorWHITE);
1496 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001497 });
Chris Craik76caecf2015-11-02 19:17:45 -08001498
Chris Craikf158b492016-01-12 14:45:08 -08001499 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001500 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik76caecf2015-11-02 19:17:45 -08001501 PropertyTestRenderer renderer(opValidateCallback);
Chris Craikf158b492016-01-12 14:45:08 -08001502 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik76caecf2015-11-02 19:17:45 -08001503 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1504}
1505
Chris Craik3a5811b2016-03-22 15:03:08 -07001506RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
Chris Craik76caecf2015-11-02 19:17:45 -08001507 testProperty([](RenderProperties& properties) {
1508 properties.setAlpha(0.5f);
1509 properties.setHasOverlappingRendering(false);
Chris Craik76caecf2015-11-02 19:17:45 -08001510 }, [](const RectOp& op, const BakedOpState& state) {
1511 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1512 });
1513}
1514
Chris Craik3a5811b2016-03-22 15:03:08 -07001515RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
Chris Craik76caecf2015-11-02 19:17:45 -08001516 testProperty([](RenderProperties& properties) {
1517 properties.setClipToBounds(true);
1518 properties.setClipBounds(Rect(10, 20, 300, 400));
Chris Craik76caecf2015-11-02 19:17:45 -08001519 }, [](const RectOp& op, const BakedOpState& state) {
1520 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1521 << "Clip rect should be intersection of node bounds and clip bounds";
1522 });
1523}
1524
Chris Craik3a5811b2016-03-22 15:03:08 -07001525RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001526 testProperty([](RenderProperties& properties) {
1527 properties.mutableRevealClip().set(true, 50, 50, 25);
Chris Craik76caecf2015-11-02 19:17:45 -08001528 }, [](const RectOp& op, const BakedOpState& state) {
1529 ASSERT_NE(nullptr, state.roundRectClipState);
1530 EXPECT_TRUE(state.roundRectClipState->highPriority);
1531 EXPECT_EQ(25, state.roundRectClipState->radius);
1532 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1533 });
1534}
1535
Chris Craik3a5811b2016-03-22 15:03:08 -07001536RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001537 testProperty([](RenderProperties& properties) {
1538 properties.mutableOutline().setShouldClip(true);
1539 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
Chris Craik76caecf2015-11-02 19:17:45 -08001540 }, [](const RectOp& op, const BakedOpState& state) {
1541 ASSERT_NE(nullptr, state.roundRectClipState);
1542 EXPECT_FALSE(state.roundRectClipState->highPriority);
1543 EXPECT_EQ(5, state.roundRectClipState->radius);
1544 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1545 });
1546}
1547
Chris Craik3a5811b2016-03-22 15:03:08 -07001548RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
Chris Craik76caecf2015-11-02 19:17:45 -08001549 testProperty([](RenderProperties& properties) {
1550 properties.setLeftTopRightBottom(10, 10, 110, 110);
1551
1552 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1553 properties.setStaticMatrix(&staticMatrix);
1554
1555 // ignored, since static overrides animation
1556 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1557 properties.setAnimationMatrix(&animationMatrix);
1558
1559 properties.setTranslationX(10);
1560 properties.setTranslationY(20);
1561 properties.setScaleX(0.5f);
1562 properties.setScaleY(0.7f);
Chris Craik76caecf2015-11-02 19:17:45 -08001563 }, [](const RectOp& op, const BakedOpState& state) {
1564 Matrix4 matrix;
1565 matrix.loadTranslate(10, 10, 0); // left, top
1566 matrix.scale(1.2f, 1.2f, 1); // static matrix
1567 // ignore animation matrix, since static overrides it
1568
1569 // translation xy
1570 matrix.translate(10, 20);
1571
1572 // scale xy (from default pivot - center)
1573 matrix.translate(50, 50);
1574 matrix.scale(0.5f, 0.7f, 1);
1575 matrix.translate(-50, -50);
1576 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1577 << "Op draw matrix must match expected combination of transformation properties";
1578 });
1579}
Chris Craik161f54b2015-11-05 11:08:52 -08001580
Chris Craik8ecf41c2015-11-16 10:27:59 -08001581struct SaveLayerAlphaData {
1582 uint32_t layerWidth = 0;
1583 uint32_t layerHeight = 0;
1584 Rect rectClippedBounds;
1585 Matrix4 rectMatrix;
1586};
1587/**
1588 * Constructs a view to hit the temporary layer alpha property implementation:
1589 * a) 0 < alpha < 1
1590 * b) too big for layer (larger than maxTextureSize)
1591 * c) overlapping rendering content
1592 * returning observed data about layer size and content clip/transform.
1593 *
1594 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1595 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1596 */
1597void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
John Reck16c9d6a2015-11-17 15:51:08 -08001598 std::function<void(RenderProperties&)> propSetupCallback) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001599 class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1600 public:
1601 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1602 : mOutData(outData) {}
1603
1604 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1605 EXPECT_EQ(0, mIndex++);
1606 mOutData->layerWidth = width;
1607 mOutData->layerHeight = height;
1608 return nullptr;
1609 }
1610 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1611 EXPECT_EQ(1, mIndex++);
1612
1613 mOutData->rectClippedBounds = state.computedState.clippedBounds;
1614 mOutData->rectMatrix = state.computedState.transform;
1615 }
1616 void endLayer() override {
1617 EXPECT_EQ(2, mIndex++);
1618 }
1619 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1620 EXPECT_EQ(3, mIndex++);
1621 }
1622 private:
1623 SaveLayerAlphaData* mOutData;
1624 };
1625
1626 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1627 << "Node must be bigger than max texture size to exercise saveLayer codepath";
John Reck16c9d6a2015-11-17 15:51:08 -08001628 auto node = TestUtils::createNode(0, 0, 10000, 10000,
1629 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1630 properties.setHasOverlappingRendering(true);
1631 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1632 // apply other properties
1633 propSetupCallback(properties);
1634
Chris Craik8ecf41c2015-11-16 10:27:59 -08001635 SkPaint paint;
1636 paint.setColor(SK_ColorWHITE);
1637 canvas.drawRect(0, 0, 10000, 10000, paint);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001638 });
John Reck7db5ffb2016-01-15 13:17:09 -08001639 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
Chris Craik8ecf41c2015-11-16 10:27:59 -08001640
Chris Craik6e068c012016-01-15 16:15:30 -08001641 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001642 nodes, sLightGeometry, Caches::getInstance());
Chris Craik8ecf41c2015-11-16 10:27:59 -08001643 SaveLayerAlphaClipTestRenderer renderer(outObservedData);
Chris Craikf158b492016-01-12 14:45:08 -08001644 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001645
1646 // assert, since output won't be valid if we haven't seen a save layer triggered
1647 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1648}
1649
Chris Craik3a5811b2016-03-22 15:03:08 -07001650RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001651 SaveLayerAlphaData observedData;
1652 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1653 properties.setTranslationX(10); // offset rendering content
1654 properties.setTranslationY(-2000); // offset rendering content
Chris Craik8ecf41c2015-11-16 10:27:59 -08001655 });
1656 EXPECT_EQ(190u, observedData.layerWidth);
1657 EXPECT_EQ(200u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001658 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
Chris Craik8ecf41c2015-11-16 10:27:59 -08001659 << "expect content to be clipped to screen area";
1660 Matrix4 expected;
1661 expected.loadTranslate(0, -2000, 0);
1662 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1663 << "expect content to be translated as part of being clipped";
1664}
1665
Chris Craik3a5811b2016-03-22 15:03:08 -07001666RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001667 SaveLayerAlphaData observedData;
1668 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1669 // Translate and rotate the view so that the only visible part is the top left corner of
Chris Craik8d1f2122015-11-24 16:40:09 -08001670 // 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 -08001671 // bottom of the viewport.
1672 properties.setTranslationX(100);
1673 properties.setTranslationY(100);
1674 properties.setPivotX(0);
1675 properties.setPivotY(0);
1676 properties.setRotation(45);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001677 });
1678 // ceil(sqrt(2) / 2 * 200) = 142
1679 EXPECT_EQ(142u, observedData.layerWidth);
1680 EXPECT_EQ(142u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001681 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001682 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1683}
1684
Chris Craik3a5811b2016-03-22 15:03:08 -07001685RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001686 SaveLayerAlphaData observedData;
1687 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1688 properties.setPivotX(0);
1689 properties.setPivotY(0);
1690 properties.setScaleX(2);
1691 properties.setScaleY(0.5f);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001692 });
1693 EXPECT_EQ(100u, observedData.layerWidth);
1694 EXPECT_EQ(400u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001695 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001696 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1697}
1698
Chris Craik6fe991e52015-10-20 09:39:42 -07001699} // namespace uirenderer
1700} // namespace android