blob: 99400c6d48df9781b1b4b424efd9c078ff4c2c47 [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,
390 [](Matrix4* transform) {
391 transform->loadTranslate(5, 5, 0);
392 });
393
394 auto node = TestUtils::createNode(0, 0, 200, 200,
395 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500396 canvas.save(SaveFlags::MatrixClip);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800397 canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
398 canvas.drawLayer(layerUpdater.get());
399 canvas.restore();
400 });
Chris Craikf158b492016-01-12 14:45:08 -0800401 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700402 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800403 TextureLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800404 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800405 EXPECT_EQ(1, renderer.getIndex());
406}
407
Chris Craik3a5811b2016-03-22 15:03:08 -0700408RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
Chris Craik223e3b62016-03-10 10:27:38 -0800409 class FunctorTestRenderer : public TestRendererBase {
410 public:
411 void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
412 EXPECT_EQ(0, mIndex++);
413 }
414 };
415 Functor noopFunctor;
416
417 // 1 million pixel tall view, scrolled down 80%
418 auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
419 [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
420 canvas.translate(0, -800000);
421 canvas.callDrawGLFunction(&noopFunctor);
422 });
423
424 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700425 TestUtils::createSyncedNodeList(scrolledFunctorView),
426 sLightGeometry, Caches::getInstance());
Chris Craik223e3b62016-03-10 10:27:38 -0800427 FunctorTestRenderer renderer;
428 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
429 EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
430}
431
Chris Craik3a5811b2016-03-22 15:03:08 -0700432RENDERTHREAD_TEST(FrameBuilder, renderNode) {
Chris Craikd3daa312015-11-06 10:59:56 -0800433 class RenderNodeTestRenderer : public TestRendererBase {
434 public:
435 void onRectOp(const RectOp& op, const BakedOpState& state) override {
436 switch(mIndex++) {
437 case 0:
Chris Craik5430ab22015-12-10 16:28:16 -0800438 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
Chris Craikd3daa312015-11-06 10:59:56 -0800439 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
440 break;
441 case 1:
442 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
443 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
444 break;
445 default:
446 ADD_FAILURE();
447 }
448 }
449 };
450
Chris Craik8d1f2122015-11-24 16:40:09 -0800451 auto child = TestUtils::createNode(10, 10, 110, 110,
452 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700453 SkPaint paint;
454 paint.setColor(SK_ColorWHITE);
455 canvas.drawRect(0, 0, 100, 100, paint);
456 });
457
Chris Craik8d1f2122015-11-24 16:40:09 -0800458 auto parent = TestUtils::createNode(0, 0, 200, 200,
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800459 [&child](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700460 SkPaint paint;
461 paint.setColor(SK_ColorDKGRAY);
462 canvas.drawRect(0, 0, 200, 200, paint);
Chris Craikb565df12015-10-05 13:00:52 -0700463
Florin Malitaeecff562015-12-21 10:43:01 -0500464 canvas.save(SaveFlags::MatrixClip);
Chris Craikddf22152015-10-14 17:42:47 -0700465 canvas.translate(40, 40);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800466 canvas.drawRenderNode(child.get());
Chris Craikddf22152015-10-14 17:42:47 -0700467 canvas.restore();
Chris Craikb565df12015-10-05 13:00:52 -0700468 });
469
Chris Craikf158b492016-01-12 14:45:08 -0800470 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700471 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700472 RenderNodeTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800473 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik223e3b62016-03-10 10:27:38 -0800474 EXPECT_EQ(2, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700475}
476
Chris Craik3a5811b2016-03-22 15:03:08 -0700477RENDERTHREAD_TEST(FrameBuilder, clipped) {
Chris Craikd3daa312015-11-06 10:59:56 -0800478 class ClippedTestRenderer : public TestRendererBase {
479 public:
480 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
481 EXPECT_EQ(0, mIndex++);
482 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800483 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800484 EXPECT_TRUE(state.computedState.transform.isIdentity());
485 }
486 };
487
Chris Craik8d1f2122015-11-24 16:40:09 -0800488 auto node = TestUtils::createNode(0, 0, 200, 200,
489 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700490 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
491 canvas.drawBitmap(bitmap, 0, 0, nullptr);
492 });
Chris Craikddf22152015-10-14 17:42:47 -0700493
Chris Craikf158b492016-01-12 14:45:08 -0800494 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
Chris Craik0b7e8242015-10-28 16:50:44 -0700495 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
Chris Craik3a5811b2016-03-22 15:03:08 -0700496 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700497 ClippedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800498 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikddf22152015-10-14 17:42:47 -0700499}
500
Chris Craik3a5811b2016-03-22 15:03:08 -0700501RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800502 class SaveLayerSimpleTestRenderer : public TestRendererBase {
503 public:
504 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
505 EXPECT_EQ(0, mIndex++);
506 EXPECT_EQ(180u, width);
507 EXPECT_EQ(180u, height);
508 return nullptr;
509 }
510 void endLayer() override {
511 EXPECT_EQ(2, mIndex++);
512 }
513 void onRectOp(const RectOp& op, const BakedOpState& state) override {
514 EXPECT_EQ(1, mIndex++);
515 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
Chris Craik5430ab22015-12-10 16:28:16 -0800516 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800517 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800518
519 Matrix4 expectedTransform;
520 expectedTransform.loadTranslate(-10, -10, 0);
521 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
522 }
523 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
524 EXPECT_EQ(3, mIndex++);
525 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800526 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800527 EXPECT_TRUE(state.computedState.transform.isIdentity());
528 }
529 };
530
Chris Craik8d1f2122015-11-24 16:40:09 -0800531 auto node = TestUtils::createNode(0, 0, 200, 200,
532 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500533 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700534 canvas.drawRect(10, 10, 190, 190, SkPaint());
535 canvas.restore();
536 });
Chris Craikf158b492016-01-12 14:45:08 -0800537 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700538 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700539 SaveLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800540 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700541 EXPECT_EQ(4, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700542}
Chris Craik6fe991e52015-10-20 09:39:42 -0700543
Chris Craik3a5811b2016-03-22 15:03:08 -0700544RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
Chris Craikd3daa312015-11-06 10:59:56 -0800545 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
546 * - startTemporaryLayer2, rect2 endLayer2
547 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
548 * - startFrame, layerOp1, endFrame
549 */
550 class SaveLayerNestedTestRenderer : public TestRendererBase {
551 public:
552 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
553 const int index = mIndex++;
554 if (index == 0) {
555 EXPECT_EQ(400u, width);
556 EXPECT_EQ(400u, height);
557 return (OffscreenBuffer*) 0x400;
558 } else if (index == 3) {
559 EXPECT_EQ(800u, width);
560 EXPECT_EQ(800u, height);
561 return (OffscreenBuffer*) 0x800;
562 } else { ADD_FAILURE(); }
563 return (OffscreenBuffer*) nullptr;
564 }
565 void endLayer() override {
566 int index = mIndex++;
567 EXPECT_TRUE(index == 2 || index == 6);
568 }
Chris Craik98787e62015-11-13 10:55:30 -0800569 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800570 EXPECT_EQ(7, mIndex++);
571 }
Chris Craike4db79d2015-12-22 16:32:23 -0800572 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800573 EXPECT_EQ(9, mIndex++);
574 }
575 void onRectOp(const RectOp& op, const BakedOpState& state) override {
576 const int index = mIndex++;
577 if (index == 1) {
Chris Craik5430ab22015-12-10 16:28:16 -0800578 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
Chris Craikd3daa312015-11-06 10:59:56 -0800579 } else if (index == 4) {
Chris Craik5430ab22015-12-10 16:28:16 -0800580 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
Chris Craikd3daa312015-11-06 10:59:56 -0800581 } else { ADD_FAILURE(); }
582 }
583 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
584 const int index = mIndex++;
585 if (index == 5) {
586 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800587 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
Chris Craikd3daa312015-11-06 10:59:56 -0800588 } else if (index == 8) {
589 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800590 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
Chris Craikd3daa312015-11-06 10:59:56 -0800591 } else { ADD_FAILURE(); }
592 }
593 };
594
Chris Craik8d1f2122015-11-24 16:40:09 -0800595 auto node = TestUtils::createNode(0, 0, 800, 800,
596 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500597 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700598 {
599 canvas.drawRect(0, 0, 800, 800, SkPaint());
Florin Malitaeecff562015-12-21 10:43:01 -0500600 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700601 {
602 canvas.drawRect(0, 0, 400, 400, SkPaint());
603 }
604 canvas.restore();
605 }
606 canvas.restore();
607 });
608
Chris Craikf158b492016-01-12 14:45:08 -0800609 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
Chris Craik3a5811b2016-03-22 15:03:08 -0700610 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik5854b342015-10-26 15:49:56 -0700611 SaveLayerNestedTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800612 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik5854b342015-10-26 15:49:56 -0700613 EXPECT_EQ(10, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700614}
Chris Craik6fe991e52015-10-20 09:39:42 -0700615
Chris Craik3a5811b2016-03-22 15:03:08 -0700616RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800617 auto node = TestUtils::createNode(0, 0, 200, 200,
618 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500619 canvas.save(SaveFlags::MatrixClip);
Chris Craik6fe991e52015-10-20 09:39:42 -0700620 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
Florin Malitaeecff562015-12-21 10:43:01 -0500621 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700622
623 // draw within save layer may still be recorded, but shouldn't be drawn
624 canvas.drawRect(200, 200, 400, 400, SkPaint());
625
626 canvas.restore();
627 canvas.restore();
628 });
Chris Craikf158b492016-01-12 14:45:08 -0800629 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700630 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik6fe991e52015-10-20 09:39:42 -0700631
Chris Craik5854b342015-10-26 15:49:56 -0700632 FailRenderer renderer;
Chris Craik6fe991e52015-10-20 09:39:42 -0700633 // should see no ops, even within the layer, since the layer should be rejected
Chris Craikf158b492016-01-12 14:45:08 -0800634 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700635}
636
Chris Craik3a5811b2016-03-22 15:03:08 -0700637RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800638 class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
639 public:
640 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
641 EXPECT_EQ(0, mIndex++);
642 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800643 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800644 EXPECT_TRUE(state.computedState.transform.isIdentity());
645 }
646 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
647 EXPECT_EQ(1, mIndex++);
648 ASSERT_NE(nullptr, op.paint);
649 ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
650 }
651 void onRectOp(const RectOp& op, const BakedOpState& state) override {
652 EXPECT_EQ(2, mIndex++);
653 EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
654 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
655 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
656 EXPECT_TRUE(state.computedState.transform.isIdentity());
657 }
658 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
659 EXPECT_EQ(3, mIndex++);
660 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craik7435eb12016-01-07 17:41:40 -0800661 EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
Chris Craikb87eadd2016-01-06 09:16:05 -0800662 EXPECT_TRUE(state.computedState.transform.isIdentity());
663 }
664 };
665
666 auto node = TestUtils::createNode(0, 0, 200, 200,
667 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500668 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
Chris Craikb87eadd2016-01-06 09:16:05 -0800669 canvas.drawRect(0, 0, 200, 200, SkPaint());
670 canvas.restore();
671 });
Chris Craikf158b492016-01-12 14:45:08 -0800672 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700673 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800674 SaveLayerUnclippedSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800675 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800676 EXPECT_EQ(4, renderer.getIndex());
677}
678
Chris Craik3a5811b2016-03-22 15:03:08 -0700679RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800680 class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
681 public:
682 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
683 int index = mIndex++;
684 EXPECT_GT(4, index);
685 EXPECT_EQ(5, op.unmappedBounds.getWidth());
686 EXPECT_EQ(5, op.unmappedBounds.getHeight());
687 if (index == 0) {
688 EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
689 } else if (index == 1) {
690 EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
691 } else if (index == 2) {
692 EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
693 } else if (index == 3) {
694 EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
695 }
696 }
697 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
698 EXPECT_EQ(4, mIndex++);
699 ASSERT_EQ(op.vertexCount, 16u);
700 for (size_t i = 0; i < op.vertexCount; i++) {
701 auto v = op.vertices[i];
702 EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
703 EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
704 }
705 }
706 void onRectOp(const RectOp& op, const BakedOpState& state) override {
707 EXPECT_EQ(5, mIndex++);
708 }
709 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
710 EXPECT_LT(5, mIndex++);
711 }
712 };
713
714 auto node = TestUtils::createNode(0, 0, 200, 200,
715 [](RenderProperties& props, RecordingCanvas& canvas) {
716
Florin Malitaeecff562015-12-21 10:43:01 -0500717 int restoreTo = canvas.save(SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800718 canvas.scale(2, 2);
Florin Malitaeecff562015-12-21 10:43:01 -0500719 canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
720 canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
721 canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
722 canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
Chris Craikb87eadd2016-01-06 09:16:05 -0800723 canvas.drawRect(0, 0, 100, 100, SkPaint());
724 canvas.restoreToCount(restoreTo);
725 });
Chris Craikf158b492016-01-12 14:45:08 -0800726 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700727 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800728 SaveLayerUnclippedMergedClearsTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800729 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800730 EXPECT_EQ(10, renderer.getIndex())
731 << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
732}
733
Chris Craik3a5811b2016-03-22 15:03:08 -0700734RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
Chris Craik4876de12016-02-25 16:54:08 -0800735 class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
736 public:
737 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
738 EXPECT_EQ(0, mIndex++);
739 }
740 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
741 EXPECT_EQ(1, mIndex++);
742 ASSERT_NE(nullptr, op.paint);
743 EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
744 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
745 << "Expect dirty rect as clip";
746 ASSERT_NE(nullptr, state.computedState.clipState);
747 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
748 EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
749 }
750 void onRectOp(const RectOp& op, const BakedOpState& state) override {
751 EXPECT_EQ(2, mIndex++);
752 }
753 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
754 EXPECT_EQ(3, mIndex++);
755 }
756 };
757
758 auto node = TestUtils::createNode(0, 0, 200, 200,
759 [](RenderProperties& props, RecordingCanvas& canvas) {
760 // save smaller than clip, so we get unclipped behavior
761 canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
762 canvas.drawRect(0, 0, 200, 200, SkPaint());
763 canvas.restore();
764 });
765
766 // draw with partial screen dirty, and assert we see that rect later
767 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700768 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800769 SaveLayerUnclippedClearClipTestRenderer renderer;
770 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
771 EXPECT_EQ(4, renderer.getIndex());
772}
773
Chris Craik3a5811b2016-03-22 15:03:08 -0700774RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
Chris Craik4876de12016-02-25 16:54:08 -0800775 auto node = TestUtils::createNode(0, 0, 200, 200,
776 [](RenderProperties& props, RecordingCanvas& canvas) {
777 // unclipped savelayer + rect both in area that won't intersect with dirty
778 canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
779 canvas.drawRect(100, 100, 200, 200, SkPaint());
780 canvas.restore();
781 });
782
783 // draw with partial screen dirty that doesn't intersect with savelayer
784 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700785 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik4876de12016-02-25 16:54:08 -0800786 FailRenderer renderer;
787 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
788}
789
Chris Craikb87eadd2016-01-06 09:16:05 -0800790/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
791 * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
792 * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
793 */
Chris Craik3a5811b2016-03-22 15:03:08 -0700794RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
Chris Craikb87eadd2016-01-06 09:16:05 -0800795 class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
796 public:
797 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
798 EXPECT_EQ(0, mIndex++); // savelayer first
799 return (OffscreenBuffer*)0xabcd;
800 }
801 void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
802 int index = mIndex++;
803 EXPECT_TRUE(index == 1 || index == 7);
804 }
805 void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
806 int index = mIndex++;
807 EXPECT_TRUE(index == 2 || index == 8);
808 }
809 void onRectOp(const RectOp& op, const BakedOpState& state) override {
810 EXPECT_EQ(3, mIndex++);
811 Matrix4 expected;
812 expected.loadTranslate(-100, -100, 0);
813 EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
814 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
815 }
816 void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
817 int index = mIndex++;
818 EXPECT_TRUE(index == 4 || index == 10);
819 }
820 void endLayer() override {
821 EXPECT_EQ(5, mIndex++);
822 }
823 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
824 EXPECT_EQ(6, mIndex++);
825 }
826 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
827 EXPECT_EQ(9, mIndex++);
828 }
829 void endFrame(const Rect& repaintRect) override {
830 EXPECT_EQ(11, mIndex++);
831 }
832 };
833
834 auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
835 [](RenderProperties& props, RecordingCanvas& canvas) {
Florin Malitaeecff562015-12-21 10:43:01 -0500836 canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
837 canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
838 canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
Chris Craikb87eadd2016-01-06 09:16:05 -0800839 canvas.drawRect(200, 200, 300, 300, SkPaint());
840 canvas.restore();
841 canvas.restore();
842 canvas.restore();
843 });
Chris Craikf158b492016-01-12 14:45:08 -0800844 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
Chris Craik3a5811b2016-03-22 15:03:08 -0700845 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craikb87eadd2016-01-06 09:16:05 -0800846 SaveLayerUnclippedComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800847 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb87eadd2016-01-06 09:16:05 -0800848 EXPECT_EQ(12, renderer.getIndex());
849}
850
Chris Craikf158b492016-01-12 14:45:08 -0800851RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800852 class HwLayerSimpleTestRenderer : public TestRendererBase {
853 public:
Chris Craik98787e62015-11-13 10:55:30 -0800854 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800855 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -0800856 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
857 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
858 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
Chris Craikd3daa312015-11-06 10:59:56 -0800859 }
860 void onRectOp(const RectOp& op, const BakedOpState& state) override {
861 EXPECT_EQ(1, mIndex++);
862
863 EXPECT_TRUE(state.computedState.transform.isIdentity())
864 << "Transform should be reset within layer";
865
Chris Craike4db79d2015-12-22 16:32:23 -0800866 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
Chris Craikd3daa312015-11-06 10:59:56 -0800867 << "Damage rect should be used to clip layer content";
868 }
869 void endLayer() override {
870 EXPECT_EQ(2, mIndex++);
871 }
Chris Craik98787e62015-11-13 10:55:30 -0800872 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800873 EXPECT_EQ(3, mIndex++);
874 }
875 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
876 EXPECT_EQ(4, mIndex++);
877 }
Chris Craike4db79d2015-12-22 16:32:23 -0800878 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800879 EXPECT_EQ(5, mIndex++);
880 }
881 };
882
Chris Craik8d1f2122015-11-24 16:40:09 -0800883 auto node = TestUtils::createNode(10, 10, 110, 110,
John Reck16c9d6a2015-11-17 15:51:08 -0800884 [](RenderProperties& props, RecordingCanvas& canvas) {
885 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700886 SkPaint paint;
887 paint.setColor(SK_ColorWHITE);
888 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -0800889 });
Chris Craik98787e62015-11-13 10:55:30 -0800890 OffscreenBuffer** layerHandle = node->getLayerHandle();
Chris Craik0b7e8242015-10-28 16:50:44 -0700891
Chris Craik98787e62015-11-13 10:55:30 -0800892 // create RenderNode's layer here in same way prepareTree would
893 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
894 *layerHandle = &layer;
Chris Craik0b7e8242015-10-28 16:50:44 -0700895
John Reck7db5ffb2016-01-15 13:17:09 -0800896 auto syncedNodeList = TestUtils::createSyncedNodeList(node);
Chris Craik0b7e8242015-10-28 16:50:44 -0700897
898 // only enqueue partial damage
Chris Craik98787e62015-11-13 10:55:30 -0800899 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -0700900 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
901
Chris Craikf158b492016-01-12 14:45:08 -0800902 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -0700903 syncedNodeList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -0700904 HwLayerSimpleTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -0800905 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700906 EXPECT_EQ(6, renderer.getIndex());
907
908 // clean up layer pointer, so we can safely destruct RenderNode
Chris Craik98787e62015-11-13 10:55:30 -0800909 *layerHandle = nullptr;
Chris Craik0b7e8242015-10-28 16:50:44 -0700910}
911
Chris Craikf158b492016-01-12 14:45:08 -0800912RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
Chris Craikd3daa312015-11-06 10:59:56 -0800913 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
914 * - startRepaintLayer(child), rect(grey), endLayer
915 * - startTemporaryLayer, drawLayer(child), endLayer
916 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
917 * - startFrame, drawLayer(parent), endLayerb
918 */
919 class HwLayerComplexTestRenderer : public TestRendererBase {
920 public:
921 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
922 EXPECT_EQ(3, mIndex++); // savelayer first
923 return (OffscreenBuffer*)0xabcd;
924 }
Chris Craik98787e62015-11-13 10:55:30 -0800925 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800926 int index = mIndex++;
927 if (index == 0) {
928 // starting inner layer
Chris Craik98787e62015-11-13 10:55:30 -0800929 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
930 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800931 } else if (index == 6) {
932 // starting outer layer
Chris Craik98787e62015-11-13 10:55:30 -0800933 EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
934 EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800935 } else { ADD_FAILURE(); }
936 }
937 void onRectOp(const RectOp& op, const BakedOpState& state) override {
938 int index = mIndex++;
939 if (index == 1) {
940 // inner layer's rect (white)
941 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
942 } else if (index == 7) {
943 // outer layer's rect (grey)
944 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
945 } else { ADD_FAILURE(); }
946 }
947 void endLayer() override {
948 int index = mIndex++;
949 EXPECT_TRUE(index == 2 || index == 5 || index == 9);
950 }
Chris Craik98787e62015-11-13 10:55:30 -0800951 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800952 EXPECT_EQ(10, mIndex++);
953 }
954 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
Chris Craik98787e62015-11-13 10:55:30 -0800955 OffscreenBuffer* layer = *op.layerHandle;
Chris Craikd3daa312015-11-06 10:59:56 -0800956 int index = mIndex++;
957 if (index == 4) {
Chris Craik98787e62015-11-13 10:55:30 -0800958 EXPECT_EQ(100u, layer->viewportWidth);
959 EXPECT_EQ(100u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800960 } else if (index == 8) {
961 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
962 } else if (index == 11) {
Chris Craik98787e62015-11-13 10:55:30 -0800963 EXPECT_EQ(200u, layer->viewportWidth);
964 EXPECT_EQ(200u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800965 } else { ADD_FAILURE(); }
966 }
Chris Craike4db79d2015-12-22 16:32:23 -0800967 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800968 EXPECT_EQ(12, mIndex++);
969 }
970 };
971
John Reck16c9d6a2015-11-17 15:51:08 -0800972 auto child = TestUtils::createNode(50, 50, 150, 150,
973 [](RenderProperties& props, RecordingCanvas& canvas) {
974 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700975 SkPaint paint;
976 paint.setColor(SK_ColorWHITE);
977 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -0800978 });
Chris Craik98787e62015-11-13 10:55:30 -0800979 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
980 *(child->getLayerHandle()) = &childLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -0700981
982 RenderNode* childPtr = child.get();
John Reck16c9d6a2015-11-17 15:51:08 -0800983 auto parent = TestUtils::createNode(0, 0, 200, 200,
984 [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
985 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700986 SkPaint paint;
987 paint.setColor(SK_ColorDKGRAY);
988 canvas.drawRect(0, 0, 200, 200, paint);
989
Florin Malitaeecff562015-12-21 10:43:01 -0500990 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700991 canvas.drawRenderNode(childPtr);
992 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -0800993 });
Chris Craik98787e62015-11-13 10:55:30 -0800994 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
995 *(parent->getLayerHandle()) = &parentLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -0700996
John Reck7db5ffb2016-01-15 13:17:09 -0800997 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik0b7e8242015-10-28 16:50:44 -0700998
Chris Craik98787e62015-11-13 10:55:30 -0800999 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -07001000 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
1001 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
1002
Chris Craikf158b492016-01-12 14:45:08 -08001003 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001004 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik0b7e8242015-10-28 16:50:44 -07001005 HwLayerComplexTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001006 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik0b7e8242015-10-28 16:50:44 -07001007 EXPECT_EQ(13, renderer.getIndex());
1008
1009 // clean up layer pointers, so we can safely destruct RenderNodes
1010 *(child->getLayerHandle()) = nullptr;
1011 *(parent->getLayerHandle()) = nullptr;
1012}
1013
Chris Craik161f54b2015-11-05 11:08:52 -08001014static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
1015 SkPaint paint;
1016 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
1017 canvas->drawRect(0, 0, 100, 100, paint);
1018}
1019static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
John Reck16c9d6a2015-11-17 15:51:08 -08001020 auto node = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001021 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001022 drawOrderedRect(&canvas, expectedDrawOrder);
1023 });
1024 node->mutateStagingProperties().setTranslationZ(z);
1025 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
1026 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
1027}
Chris Craik3a5811b2016-03-22 15:03:08 -07001028RENDERTHREAD_TEST(FrameBuilder, zReorder) {
Chris Craikd3daa312015-11-06 10:59:56 -08001029 class ZReorderTestRenderer : public TestRendererBase {
1030 public:
1031 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1032 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
1033 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
1034 }
1035 };
1036
John Reck16c9d6a2015-11-17 15:51:08 -08001037 auto parent = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001038 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -08001039 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
1040 drawOrderedRect(&canvas, 1);
1041 canvas.insertReorderBarrier(true);
1042 drawOrderedNode(&canvas, 6, 2.0f);
1043 drawOrderedRect(&canvas, 3);
1044 drawOrderedNode(&canvas, 4, 0.0f);
1045 drawOrderedRect(&canvas, 5);
1046 drawOrderedNode(&canvas, 2, -2.0f);
1047 drawOrderedNode(&canvas, 7, 2.0f);
1048 canvas.insertReorderBarrier(false);
1049 drawOrderedRect(&canvas, 8);
1050 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
1051 });
Chris Craikf158b492016-01-12 14:45:08 -08001052 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001053 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik161f54b2015-11-05 11:08:52 -08001054 ZReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001055 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik161f54b2015-11-05 11:08:52 -08001056 EXPECT_EQ(10, renderer.getIndex());
1057};
1058
Chris Craik3a5811b2016-03-22 15:03:08 -07001059RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
Chris Craik8d1f2122015-11-24 16:40:09 -08001060 static const int scrollX = 5;
1061 static const int scrollY = 10;
1062 class ProjectionReorderTestRenderer : public TestRendererBase {
1063 public:
1064 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1065 const int index = mIndex++;
1066
1067 Matrix4 expectedMatrix;
1068 switch (index) {
1069 case 0:
1070 EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
1071 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
1072 expectedMatrix.loadIdentity();
Chris Craik678ff812016-03-01 13:27:54 -08001073 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001074 break;
1075 case 1:
1076 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
1077 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
Chris Craik678ff812016-03-01 13:27:54 -08001078 expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
1079 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1080 EXPECT_EQ(Rect(-35, -30, 45, 50),
1081 Rect(state.computedState.localProjectionPathMask->getBounds()));
Chris Craik8d1f2122015-11-24 16:40:09 -08001082 break;
1083 case 2:
1084 EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
1085 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
1086 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
Chris Craik678ff812016-03-01 13:27:54 -08001087 EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
Chris Craik8d1f2122015-11-24 16:40:09 -08001088 break;
1089 default:
1090 ADD_FAILURE();
1091 }
Chris Craik678ff812016-03-01 13:27:54 -08001092 EXPECT_EQ(expectedMatrix, state.computedState.transform);
Chris Craik8d1f2122015-11-24 16:40:09 -08001093 }
1094 };
1095
1096 /**
1097 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
1098 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
1099 * draw, but because it is projected backwards, it's drawn in between B and C.
1100 *
1101 * The parent is scrolled by scrollX/scrollY, but this does not affect the background
1102 * (which isn't affected by scroll).
1103 */
1104 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
1105 [](RenderProperties& properties, RecordingCanvas& canvas) {
1106 properties.setProjectionReceiver(true);
1107 // scroll doesn't apply to background, so undone via translationX/Y
1108 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1109 properties.setTranslationX(scrollX);
1110 properties.setTranslationY(scrollY);
1111
1112 SkPaint paint;
1113 paint.setColor(SK_ColorWHITE);
1114 canvas.drawRect(0, 0, 100, 100, paint);
1115 });
1116 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
1117 [](RenderProperties& properties, RecordingCanvas& canvas) {
1118 properties.setProjectBackwards(true);
1119 properties.setClipToBounds(false);
1120 SkPaint paint;
1121 paint.setColor(SK_ColorDKGRAY);
1122 canvas.drawRect(-10, -10, 60, 60, paint);
1123 });
1124 auto child = TestUtils::createNode(0, 50, 100, 100,
1125 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1126 SkPaint paint;
1127 paint.setColor(SK_ColorBLUE);
1128 canvas.drawRect(0, 0, 100, 50, paint);
1129 canvas.drawRenderNode(projectingRipple.get());
1130 });
1131 auto parent = TestUtils::createNode(0, 0, 100, 100,
1132 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
Chris Craik678ff812016-03-01 13:27:54 -08001133 // Set a rect outline for the projecting ripple to be masked against.
1134 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
1135
Florin Malitaeecff562015-12-21 10:43:01 -05001136 canvas.save(SaveFlags::MatrixClip);
Chris Craik8d1f2122015-11-24 16:40:09 -08001137 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1138 canvas.drawRenderNode(receiverBackground.get());
1139 canvas.drawRenderNode(child.get());
1140 canvas.restore();
1141 });
1142
Chris Craikf158b492016-01-12 14:45:08 -08001143 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
Chris Craik3a5811b2016-03-22 15:03:08 -07001144 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craik8d1f2122015-11-24 16:40:09 -08001145 ProjectionReorderTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001146 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8d1f2122015-11-24 16:40:09 -08001147 EXPECT_EQ(3, renderer.getIndex());
1148}
1149
Chris Craik678ff812016-03-01 13:27:54 -08001150RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
1151 static const int scrollX = 5;
1152 static const int scrollY = 10;
1153 class ProjectionHwLayerTestRenderer : public TestRendererBase {
1154 public:
1155 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1156 EXPECT_EQ(0, mIndex++);
1157 }
1158 void onArcOp(const ArcOp& op, const BakedOpState& state) override {
1159 EXPECT_EQ(1, mIndex++);
1160 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1161 }
1162 void endLayer() override {
1163 EXPECT_EQ(2, mIndex++);
1164 }
1165 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1166 EXPECT_EQ(3, mIndex++);
1167 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1168 }
1169 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1170 EXPECT_EQ(4, mIndex++);
1171 ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
1172 Matrix4 expected;
1173 expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
1174 EXPECT_EQ(expected, state.computedState.transform);
1175 EXPECT_EQ(Rect(-85, -80, 295, 300),
1176 Rect(state.computedState.localProjectionPathMask->getBounds()));
1177 }
1178 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1179 EXPECT_EQ(5, mIndex++);
1180 ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
1181 }
1182 };
1183 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1184 [](RenderProperties& properties, RecordingCanvas& canvas) {
1185 properties.setProjectionReceiver(true);
1186 // scroll doesn't apply to background, so undone via translationX/Y
1187 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1188 properties.setTranslationX(scrollX);
1189 properties.setTranslationY(scrollY);
1190
1191 canvas.drawRect(0, 0, 400, 400, SkPaint());
1192 });
1193 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1194 [](RenderProperties& properties, RecordingCanvas& canvas) {
1195 properties.setProjectBackwards(true);
1196 properties.setClipToBounds(false);
1197 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
1198 });
1199 auto child = TestUtils::createNode(100, 100, 300, 300,
1200 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1201 properties.mutateLayerProperties().setType(LayerType::RenderLayer);
1202 canvas.drawRenderNode(projectingRipple.get());
1203 canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
1204 });
1205 auto parent = TestUtils::createNode(0, 0, 400, 400,
1206 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1207 // Set a rect outline for the projecting ripple to be masked against.
1208 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
1209 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1210 canvas.drawRenderNode(receiverBackground.get());
1211 canvas.drawRenderNode(child.get());
1212 });
1213
1214 OffscreenBuffer** layerHandle = child->getLayerHandle();
1215
1216 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1217 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
1218 Matrix4 windowTransform;
1219 windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
1220 layer.setWindowTransform(windowTransform);
1221 *layerHandle = &layer;
1222
1223 auto syncedList = TestUtils::createSyncedNodeList(parent);
1224 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1225 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
1226 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001227 syncedList, sLightGeometry, Caches::getInstance());
Chris Craik678ff812016-03-01 13:27:54 -08001228 ProjectionHwLayerTestRenderer renderer;
1229 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1230 EXPECT_EQ(6, renderer.getIndex());
1231
1232 // clean up layer pointer, so we can safely destruct RenderNode
1233 *layerHandle = nullptr;
1234}
1235
Chris Craika748c082016-03-01 18:48:37 -08001236RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
1237 static const int scrollX = 500000;
1238 static const int scrollY = 0;
1239 class ProjectionChildScrollTestRenderer : public TestRendererBase {
1240 public:
1241 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1242 EXPECT_EQ(0, mIndex++);
1243 EXPECT_TRUE(state.computedState.transform.isIdentity());
1244 }
1245 void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
1246 EXPECT_EQ(1, mIndex++);
1247 ASSERT_NE(nullptr, state.computedState.clipState);
1248 ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
1249 ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
1250 EXPECT_TRUE(state.computedState.transform.isIdentity());
1251 }
1252 };
1253 auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
1254 [](RenderProperties& properties, RecordingCanvas& canvas) {
1255 properties.setProjectionReceiver(true);
1256 canvas.drawRect(0, 0, 400, 400, SkPaint());
1257 });
1258 auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
1259 [](RenderProperties& properties, RecordingCanvas& canvas) {
1260 // scroll doesn't apply to background, so undone via translationX/Y
1261 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
1262 properties.setTranslationX(scrollX);
1263 properties.setTranslationY(scrollY);
1264 properties.setProjectBackwards(true);
1265 properties.setClipToBounds(false);
1266 canvas.drawOval(0, 0, 200, 200, SkPaint());
1267 });
1268 auto child = TestUtils::createNode(0, 0, 400, 400,
1269 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
1270 // Record time clip will be ignored by projectee
1271 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
1272
1273 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
1274 canvas.drawRenderNode(projectingRipple.get());
1275 });
1276 auto parent = TestUtils::createNode(0, 0, 400, 400,
1277 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
1278 canvas.drawRenderNode(receiverBackground.get());
1279 canvas.drawRenderNode(child.get());
1280 });
1281
1282 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
Chris Craik3a5811b2016-03-22 15:03:08 -07001283 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craika748c082016-03-01 18:48:37 -08001284 ProjectionChildScrollTestRenderer renderer;
1285 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
1286 EXPECT_EQ(2, renderer.getIndex());
1287}
1288
Chris Craik98787e62015-11-13 10:55:30 -08001289// creates a 100x100 shadow casting node with provided translationZ
1290static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
John Reck16c9d6a2015-11-17 15:51:08 -08001291 return TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -08001292 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
John Reck16c9d6a2015-11-17 15:51:08 -08001293 properties.setTranslationZ(translationZ);
1294 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
Chris Craik98787e62015-11-13 10:55:30 -08001295 SkPaint paint;
1296 paint.setColor(SK_ColorWHITE);
1297 canvas.drawRect(0, 0, 100, 100, paint);
Chris Craik98787e62015-11-13 10:55:30 -08001298 });
1299}
1300
Chris Craik6e068c012016-01-15 16:15:30 -08001301RENDERTHREAD_TEST(FrameBuilder, shadow) {
Chris Craikd3daa312015-11-06 10:59:56 -08001302 class ShadowTestRenderer : public TestRendererBase {
1303 public:
1304 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1305 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -08001306 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
Chris Craik6e068c012016-01-15 16:15:30 -08001307 EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
1308 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
Chris Craik98787e62015-11-13 10:55:30 -08001309
1310 Matrix4 expectedZ;
1311 expectedZ.loadTranslate(0, 0, 5);
Chris Craik6e068c012016-01-15 16:15:30 -08001312 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
Chris Craikd3daa312015-11-06 10:59:56 -08001313 }
1314 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1315 EXPECT_EQ(1, mIndex++);
1316 }
1317 };
Chris Craik161f54b2015-11-05 11:08:52 -08001318
Chris Craik8d1f2122015-11-24 16:40:09 -08001319 auto parent = TestUtils::createNode(0, 0, 200, 200,
1320 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikd3daa312015-11-06 10:59:56 -08001321 canvas.insertReorderBarrier(true);
Chris Craik98787e62015-11-13 10:55:30 -08001322 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
Chris Craikd3daa312015-11-06 10:59:56 -08001323 });
1324
Chris Craikf158b492016-01-12 14:45:08 -08001325 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001326 TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
Chris Craikd3daa312015-11-06 10:59:56 -08001327 ShadowTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001328 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craikd3daa312015-11-06 10:59:56 -08001329 EXPECT_EQ(2, renderer.getIndex());
1330}
Chris Craik76caecf2015-11-02 19:17:45 -08001331
Chris Craik6e068c012016-01-15 16:15:30 -08001332RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001333 class ShadowSaveLayerTestRenderer : public TestRendererBase {
1334 public:
1335 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1336 EXPECT_EQ(0, mIndex++);
1337 return nullptr;
1338 }
1339 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1340 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001341 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1342 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
Chris Craik98787e62015-11-13 10:55:30 -08001343 }
1344 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1345 EXPECT_EQ(2, mIndex++);
1346 }
1347 void endLayer() override {
1348 EXPECT_EQ(3, mIndex++);
1349 }
1350 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1351 EXPECT_EQ(4, mIndex++);
1352 }
1353 };
1354
Chris Craik8d1f2122015-11-24 16:40:09 -08001355 auto parent = TestUtils::createNode(0, 0, 200, 200,
1356 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001357 // save/restore outside of reorderBarrier, so they don't get moved out of place
1358 canvas.translate(20, 10);
Florin Malitaeecff562015-12-21 10:43:01 -05001359 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001360 canvas.insertReorderBarrier(true);
1361 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1362 canvas.insertReorderBarrier(false);
1363 canvas.restoreToCount(count);
1364 });
1365
Chris Craikf158b492016-01-12 14:45:08 -08001366 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001367 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001368 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001369 ShadowSaveLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001370 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001371 EXPECT_EQ(5, renderer.getIndex());
1372}
1373
Chris Craikf158b492016-01-12 14:45:08 -08001374RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
Chris Craik98787e62015-11-13 10:55:30 -08001375 class ShadowHwLayerTestRenderer : public TestRendererBase {
1376 public:
1377 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
1378 EXPECT_EQ(0, mIndex++);
1379 }
1380 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1381 EXPECT_EQ(1, mIndex++);
Chris Craik6e068c012016-01-15 16:15:30 -08001382 EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
1383 EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
1384 EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
Chris Craik98787e62015-11-13 10:55:30 -08001385 }
1386 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1387 EXPECT_EQ(2, mIndex++);
1388 }
1389 void endLayer() override {
1390 EXPECT_EQ(3, mIndex++);
1391 }
1392 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1393 EXPECT_EQ(4, mIndex++);
1394 }
1395 };
1396
Chris Craik8d1f2122015-11-24 16:40:09 -08001397 auto parent = TestUtils::createNode(50, 60, 150, 160,
John Reck16c9d6a2015-11-17 15:51:08 -08001398 [](RenderProperties& props, RecordingCanvas& canvas) {
1399 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik98787e62015-11-13 10:55:30 -08001400 canvas.insertReorderBarrier(true);
Florin Malitaeecff562015-12-21 10:43:01 -05001401 canvas.save(SaveFlags::MatrixClip);
Chris Craik98787e62015-11-13 10:55:30 -08001402 canvas.translate(20, 10);
1403 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1404 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -08001405 });
Chris Craik98787e62015-11-13 10:55:30 -08001406 OffscreenBuffer** layerHandle = parent->getLayerHandle();
1407
1408 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
1409 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
1410 Matrix4 windowTransform;
1411 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
1412 layer.setWindowTransform(windowTransform);
1413 *layerHandle = &layer;
1414
John Reck7db5ffb2016-01-15 13:17:09 -08001415 auto syncedList = TestUtils::createSyncedNodeList(parent);
Chris Craik98787e62015-11-13 10:55:30 -08001416 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
1417 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
Chris Craikf158b492016-01-12 14:45:08 -08001418 FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001419 syncedList,
Chris Craik3a5811b2016-03-22 15:03:08 -07001420 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001421 ShadowHwLayerTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001422 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001423 EXPECT_EQ(5, renderer.getIndex());
1424
1425 // clean up layer pointer, so we can safely destruct RenderNode
1426 *layerHandle = nullptr;
1427}
1428
Chris Craik3a5811b2016-03-22 15:03:08 -07001429RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
Chris Craik98787e62015-11-13 10:55:30 -08001430 class ShadowLayeringTestRenderer : public TestRendererBase {
1431 public:
1432 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
1433 int index = mIndex++;
1434 EXPECT_TRUE(index == 0 || index == 1);
1435 }
1436 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1437 int index = mIndex++;
1438 EXPECT_TRUE(index == 2 || index == 3);
1439 }
1440 };
Chris Craik8d1f2122015-11-24 16:40:09 -08001441 auto parent = TestUtils::createNode(0, 0, 200, 200,
1442 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001443 canvas.insertReorderBarrier(true);
1444 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1445 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1446 });
1447
Chris Craikf158b492016-01-12 14:45:08 -08001448 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik6e068c012016-01-15 16:15:30 -08001449 TestUtils::createSyncedNodeList(parent),
Chris Craik3a5811b2016-03-22 15:03:08 -07001450 (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
Chris Craik98787e62015-11-13 10:55:30 -08001451 ShadowLayeringTestRenderer renderer;
Chris Craikf158b492016-01-12 14:45:08 -08001452 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik98787e62015-11-13 10:55:30 -08001453 EXPECT_EQ(4, renderer.getIndex());
1454}
1455
John Reck16c9d6a2015-11-17 15:51:08 -08001456static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
Chris Craik76caecf2015-11-02 19:17:45 -08001457 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
Chris Craikd3daa312015-11-06 10:59:56 -08001458 class PropertyTestRenderer : public TestRendererBase {
1459 public:
1460 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1461 : mCallback(callback) {}
1462 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1463 EXPECT_EQ(mIndex++, 0);
1464 mCallback(op, state);
1465 }
1466 std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1467 };
1468
John Reck16c9d6a2015-11-17 15:51:08 -08001469 auto node = TestUtils::createNode(0, 0, 100, 100,
1470 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1471 propSetupCallback(props);
Chris Craik76caecf2015-11-02 19:17:45 -08001472 SkPaint paint;
1473 paint.setColor(SK_ColorWHITE);
1474 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001475 });
Chris Craik76caecf2015-11-02 19:17:45 -08001476
Chris Craikf158b492016-01-12 14:45:08 -08001477 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001478 TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
Chris Craik76caecf2015-11-02 19:17:45 -08001479 PropertyTestRenderer renderer(opValidateCallback);
Chris Craikf158b492016-01-12 14:45:08 -08001480 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik76caecf2015-11-02 19:17:45 -08001481 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1482}
1483
Chris Craik3a5811b2016-03-22 15:03:08 -07001484RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
Chris Craik76caecf2015-11-02 19:17:45 -08001485 testProperty([](RenderProperties& properties) {
1486 properties.setAlpha(0.5f);
1487 properties.setHasOverlappingRendering(false);
Chris Craik76caecf2015-11-02 19:17:45 -08001488 }, [](const RectOp& op, const BakedOpState& state) {
1489 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1490 });
1491}
1492
Chris Craik3a5811b2016-03-22 15:03:08 -07001493RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
Chris Craik76caecf2015-11-02 19:17:45 -08001494 testProperty([](RenderProperties& properties) {
1495 properties.setClipToBounds(true);
1496 properties.setClipBounds(Rect(10, 20, 300, 400));
Chris Craik76caecf2015-11-02 19:17:45 -08001497 }, [](const RectOp& op, const BakedOpState& state) {
1498 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1499 << "Clip rect should be intersection of node bounds and clip bounds";
1500 });
1501}
1502
Chris Craik3a5811b2016-03-22 15:03:08 -07001503RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001504 testProperty([](RenderProperties& properties) {
1505 properties.mutableRevealClip().set(true, 50, 50, 25);
Chris Craik76caecf2015-11-02 19:17:45 -08001506 }, [](const RectOp& op, const BakedOpState& state) {
1507 ASSERT_NE(nullptr, state.roundRectClipState);
1508 EXPECT_TRUE(state.roundRectClipState->highPriority);
1509 EXPECT_EQ(25, state.roundRectClipState->radius);
1510 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1511 });
1512}
1513
Chris Craik3a5811b2016-03-22 15:03:08 -07001514RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
Chris Craik76caecf2015-11-02 19:17:45 -08001515 testProperty([](RenderProperties& properties) {
1516 properties.mutableOutline().setShouldClip(true);
1517 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
Chris Craik76caecf2015-11-02 19:17:45 -08001518 }, [](const RectOp& op, const BakedOpState& state) {
1519 ASSERT_NE(nullptr, state.roundRectClipState);
1520 EXPECT_FALSE(state.roundRectClipState->highPriority);
1521 EXPECT_EQ(5, state.roundRectClipState->radius);
1522 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1523 });
1524}
1525
Chris Craik3a5811b2016-03-22 15:03:08 -07001526RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
Chris Craik76caecf2015-11-02 19:17:45 -08001527 testProperty([](RenderProperties& properties) {
1528 properties.setLeftTopRightBottom(10, 10, 110, 110);
1529
1530 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1531 properties.setStaticMatrix(&staticMatrix);
1532
1533 // ignored, since static overrides animation
1534 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1535 properties.setAnimationMatrix(&animationMatrix);
1536
1537 properties.setTranslationX(10);
1538 properties.setTranslationY(20);
1539 properties.setScaleX(0.5f);
1540 properties.setScaleY(0.7f);
Chris Craik76caecf2015-11-02 19:17:45 -08001541 }, [](const RectOp& op, const BakedOpState& state) {
1542 Matrix4 matrix;
1543 matrix.loadTranslate(10, 10, 0); // left, top
1544 matrix.scale(1.2f, 1.2f, 1); // static matrix
1545 // ignore animation matrix, since static overrides it
1546
1547 // translation xy
1548 matrix.translate(10, 20);
1549
1550 // scale xy (from default pivot - center)
1551 matrix.translate(50, 50);
1552 matrix.scale(0.5f, 0.7f, 1);
1553 matrix.translate(-50, -50);
1554 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1555 << "Op draw matrix must match expected combination of transformation properties";
1556 });
1557}
Chris Craik161f54b2015-11-05 11:08:52 -08001558
Chris Craik8ecf41c2015-11-16 10:27:59 -08001559struct SaveLayerAlphaData {
1560 uint32_t layerWidth = 0;
1561 uint32_t layerHeight = 0;
1562 Rect rectClippedBounds;
1563 Matrix4 rectMatrix;
1564};
1565/**
1566 * Constructs a view to hit the temporary layer alpha property implementation:
1567 * a) 0 < alpha < 1
1568 * b) too big for layer (larger than maxTextureSize)
1569 * c) overlapping rendering content
1570 * returning observed data about layer size and content clip/transform.
1571 *
1572 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1573 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1574 */
1575void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
John Reck16c9d6a2015-11-17 15:51:08 -08001576 std::function<void(RenderProperties&)> propSetupCallback) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001577 class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1578 public:
1579 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1580 : mOutData(outData) {}
1581
1582 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1583 EXPECT_EQ(0, mIndex++);
1584 mOutData->layerWidth = width;
1585 mOutData->layerHeight = height;
1586 return nullptr;
1587 }
1588 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1589 EXPECT_EQ(1, mIndex++);
1590
1591 mOutData->rectClippedBounds = state.computedState.clippedBounds;
1592 mOutData->rectMatrix = state.computedState.transform;
1593 }
1594 void endLayer() override {
1595 EXPECT_EQ(2, mIndex++);
1596 }
1597 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1598 EXPECT_EQ(3, mIndex++);
1599 }
1600 private:
1601 SaveLayerAlphaData* mOutData;
1602 };
1603
1604 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1605 << "Node must be bigger than max texture size to exercise saveLayer codepath";
John Reck16c9d6a2015-11-17 15:51:08 -08001606 auto node = TestUtils::createNode(0, 0, 10000, 10000,
1607 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1608 properties.setHasOverlappingRendering(true);
1609 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1610 // apply other properties
1611 propSetupCallback(properties);
1612
Chris Craik8ecf41c2015-11-16 10:27:59 -08001613 SkPaint paint;
1614 paint.setColor(SK_ColorWHITE);
1615 canvas.drawRect(0, 0, 10000, 10000, paint);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001616 });
John Reck7db5ffb2016-01-15 13:17:09 -08001617 auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
Chris Craik8ecf41c2015-11-16 10:27:59 -08001618
Chris Craik6e068c012016-01-15 16:15:30 -08001619 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
Chris Craik3a5811b2016-03-22 15:03:08 -07001620 nodes, sLightGeometry, Caches::getInstance());
Chris Craik8ecf41c2015-11-16 10:27:59 -08001621 SaveLayerAlphaClipTestRenderer renderer(outObservedData);
Chris Craikf158b492016-01-12 14:45:08 -08001622 frameBuilder.replayBakedOps<TestDispatcher>(renderer);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001623
1624 // assert, since output won't be valid if we haven't seen a save layer triggered
1625 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1626}
1627
Chris Craik3a5811b2016-03-22 15:03:08 -07001628RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001629 SaveLayerAlphaData observedData;
1630 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1631 properties.setTranslationX(10); // offset rendering content
1632 properties.setTranslationY(-2000); // offset rendering content
Chris Craik8ecf41c2015-11-16 10:27:59 -08001633 });
1634 EXPECT_EQ(190u, observedData.layerWidth);
1635 EXPECT_EQ(200u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001636 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
Chris Craik8ecf41c2015-11-16 10:27:59 -08001637 << "expect content to be clipped to screen area";
1638 Matrix4 expected;
1639 expected.loadTranslate(0, -2000, 0);
1640 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1641 << "expect content to be translated as part of being clipped";
1642}
1643
Chris Craik3a5811b2016-03-22 15:03:08 -07001644RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001645 SaveLayerAlphaData observedData;
1646 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1647 // Translate and rotate the view so that the only visible part is the top left corner of
Chris Craik8d1f2122015-11-24 16:40:09 -08001648 // 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 -08001649 // bottom of the viewport.
1650 properties.setTranslationX(100);
1651 properties.setTranslationY(100);
1652 properties.setPivotX(0);
1653 properties.setPivotY(0);
1654 properties.setRotation(45);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001655 });
1656 // ceil(sqrt(2) / 2 * 200) = 142
1657 EXPECT_EQ(142u, observedData.layerWidth);
1658 EXPECT_EQ(142u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001659 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001660 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1661}
1662
Chris Craik3a5811b2016-03-22 15:03:08 -07001663RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001664 SaveLayerAlphaData observedData;
1665 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1666 properties.setPivotX(0);
1667 properties.setPivotY(0);
1668 properties.setScaleX(2);
1669 properties.setScaleY(0.5f);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001670 });
1671 EXPECT_EQ(100u, observedData.layerWidth);
1672 EXPECT_EQ(400u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001673 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001674 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1675}
1676
Chris Craik6fe991e52015-10-20 09:39:42 -07001677} // namespace uirenderer
1678} // namespace android