blob: 66dccb4b0d4fad66028fde25c372d4f159df2269 [file] [log] [blame]
Chris Craikb565df12015-10-05 13:00:52 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
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 Craik8ecf41c2015-11-16 10:27:59 -080021#include <LayerUpdateQueue.h>
Chris Craikb565df12015-10-05 13:00:52 -070022#include <OpReorderer.h>
23#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;
33const Vector3 sLightCenter = {100, 100, 100};
Chris Craik98787e62015-11-13 10:55:30 -080034
35static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
36 TestUtils::syncHierarchyPropertiesAndDisplayList(node);
37 std::vector<sp<RenderNode>> vec;
38 vec.emplace_back(node);
39 return vec;
40}
Chris Craik5854b342015-10-26 15:49:56 -070041
Chris Craik6fe991e52015-10-20 09:39:42 -070042/**
Chris Craik5854b342015-10-26 15:49:56 -070043 * Virtual class implemented by each test to redirect static operation / state transitions to
44 * virtual methods.
Chris Craik6fe991e52015-10-20 09:39:42 -070045 *
Chris Craik5854b342015-10-26 15:49:56 -070046 * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
47 * and allows Renderer vs Dispatching behavior to be merged.
Chris Craik6fe991e52015-10-20 09:39:42 -070048 *
49 * onXXXOp methods fail by default - tests should override ops they expect
Chris Craikd3daa312015-11-06 10:59:56 -080050 * startRepaintLayer fails by default - tests should override if expected
Chris Craik6fe991e52015-10-20 09:39:42 -070051 * startFrame/endFrame do nothing by default - tests should override to intercept
52 */
Chris Craik5854b342015-10-26 15:49:56 -070053class TestRendererBase {
Chris Craik6fe991e52015-10-20 09:39:42 -070054public:
Chris Craik5854b342015-10-26 15:49:56 -070055 virtual ~TestRendererBase() {}
Chris Craikd3daa312015-11-06 10:59:56 -080056 virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
Chris Craika6ac95e2015-11-02 18:06:59 -080057 ADD_FAILURE() << "Layer creation not expected in this test";
58 return nullptr;
59 }
Chris Craik98787e62015-11-13 10:55:30 -080060 virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
Chris Craika6ac95e2015-11-02 18:06:59 -080061 ADD_FAILURE() << "Layer repaint not expected in this test";
62 }
63 virtual void endLayer() {
64 ADD_FAILURE() << "Layer updates not expected in this test";
65 }
Chris Craik98787e62015-11-13 10:55:30 -080066 virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
Chris Craike4db79d2015-12-22 16:32:23 -080067 virtual void endFrame(const Rect& repaintRect) {}
Chris Craik6fe991e52015-10-20 09:39:42 -070068
Chris Craik15c3f192015-12-03 12:16:56 -080069 // define virtual defaults for single draw methods
70#define X(Type) \
Chris Craika6ac95e2015-11-02 18:06:59 -080071 virtual void on##Type(const Type&, const BakedOpState&) { \
72 ADD_FAILURE() << #Type " not expected in this test"; \
73 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080074 MAP_RENDERABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080075#undef X
76
77 // define virtual defaults for merged draw methods
78#define X(Type) \
79 virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
80 ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
81 }
Chris Craik7cbf63d2016-01-06 13:46:52 -080082 MAP_MERGEABLE_OPS(X)
Chris Craik15c3f192015-12-03 12:16:56 -080083#undef X
84
Chris Craik5854b342015-10-26 15:49:56 -070085 int getIndex() { return mIndex; }
Chris Craik6fe991e52015-10-20 09:39:42 -070086
Chris Craik5854b342015-10-26 15:49:56 -070087protected:
88 int mIndex = 0;
89};
90
91/**
92 * Dispatches all static methods to similar formed methods on renderer, which fail by default but
Chris Craik8ecf41c2015-11-16 10:27:59 -080093 * are overridden by subclasses per test.
Chris Craik5854b342015-10-26 15:49:56 -070094 */
95class TestDispatcher {
96public:
Chris Craik15c3f192015-12-03 12:16:56 -080097 // define single op methods, which redirect to TestRendererBase
98#define X(Type) \
Chris Craik5854b342015-10-26 15:49:56 -070099 static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
100 renderer.on##Type(op, state); \
Chris Craik6fe991e52015-10-20 09:39:42 -0700101 }
Chris Craik7cbf63d2016-01-06 13:46:52 -0800102 MAP_RENDERABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -0800103#undef X
104
105 // define merged op methods, which redirect to TestRendererBase
106#define X(Type) \
107 static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
108 renderer.onMerged##Type##s(opList); \
109 }
Chris Craik7cbf63d2016-01-06 13:46:52 -0800110 MAP_MERGEABLE_OPS(X);
Chris Craik15c3f192015-12-03 12:16:56 -0800111#undef X
Chris Craik6fe991e52015-10-20 09:39:42 -0700112};
Chris Craikb565df12015-10-05 13:00:52 -0700113
Chris Craik5854b342015-10-26 15:49:56 -0700114class FailRenderer : public TestRendererBase {};
Chris Craik6fe991e52015-10-20 09:39:42 -0700115
Chris Craikb565df12015-10-05 13:00:52 -0700116TEST(OpReorderer, simple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800117 class SimpleTestRenderer : public TestRendererBase {
118 public:
Chris Craik98787e62015-11-13 10:55:30 -0800119 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800120 EXPECT_EQ(0, mIndex++);
121 EXPECT_EQ(100u, width);
122 EXPECT_EQ(200u, height);
123 }
124 void onRectOp(const RectOp& op, const BakedOpState& state) override {
125 EXPECT_EQ(1, mIndex++);
126 }
127 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
128 EXPECT_EQ(2, mIndex++);
129 }
Chris Craike4db79d2015-12-22 16:32:23 -0800130 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800131 EXPECT_EQ(3, mIndex++);
132 }
133 };
134
Chris Craik8d1f2122015-11-24 16:40:09 -0800135 auto node = TestUtils::createNode(0, 0, 100, 200,
136 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700137 SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
Chris Craikb565df12015-10-05 13:00:52 -0700138 canvas.drawRect(0, 0, 100, 200, SkPaint());
139 canvas.drawBitmap(bitmap, 10, 10, nullptr);
140 });
Chris Craik8d1f2122015-11-24 16:40:09 -0800141 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
142 createSyncedNodeList(node), sLightCenter);
Chris Craik5854b342015-10-26 15:49:56 -0700143 SimpleTestRenderer renderer;
144 reorderer.replayBakedOps<TestDispatcher>(renderer);
145 EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
Chris Craik6fe991e52015-10-20 09:39:42 -0700146}
147
Chris Craik386aa032015-12-07 17:08:25 -0800148TEST(OpReorderer, simpleStroke) {
149 class SimpleStrokeTestRenderer : public TestRendererBase {
150 public:
151 void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
152 EXPECT_EQ(0, mIndex++);
153 // even though initial bounds are empty...
154 EXPECT_TRUE(op.unmappedBounds.isEmpty())
155 << "initial bounds should be empty, since they're unstroked";
156 EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
157 << "final bounds should account for stroke";
158 }
159 };
160
161 auto node = TestUtils::createNode(0, 0, 100, 200,
162 [](RenderProperties& props, RecordingCanvas& canvas) {
163 SkPaint strokedPaint;
164 strokedPaint.setStrokeWidth(10);
165 canvas.drawPoint(50, 50, strokedPaint);
166 });
167 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
168 createSyncedNodeList(node), sLightCenter);
169 SimpleStrokeTestRenderer renderer;
170 reorderer.replayBakedOps<TestDispatcher>(renderer);
171 EXPECT_EQ(1, renderer.getIndex());
172}
173
Chris Craik6fe991e52015-10-20 09:39:42 -0700174TEST(OpReorderer, simpleRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800175 auto node = TestUtils::createNode(0, 0, 200, 200,
176 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700177 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
178 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
179 canvas.drawRect(0, 0, 400, 400, SkPaint());
180 canvas.restore();
181 });
Chris Craik8d1f2122015-11-24 16:40:09 -0800182 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
183 createSyncedNodeList(node), sLightCenter);
Chris Craik6fe991e52015-10-20 09:39:42 -0700184
Chris Craik5854b342015-10-26 15:49:56 -0700185 FailRenderer renderer;
186 reorderer.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb565df12015-10-05 13:00:52 -0700187}
188
Chris Craikb565df12015-10-05 13:00:52 -0700189TEST(OpReorderer, simpleBatching) {
Chris Craika1717272015-11-19 13:02:43 -0800190 const int LOOPS = 5;
Chris Craikd3daa312015-11-06 10:59:56 -0800191 class SimpleBatchingTestRenderer : public TestRendererBase {
192 public:
193 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800194 EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
Chris Craikd3daa312015-11-06 10:59:56 -0800195 }
196 void onRectOp(const RectOp& op, const BakedOpState& state) override {
Chris Craika1717272015-11-19 13:02:43 -0800197 EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
Chris Craikd3daa312015-11-06 10:59:56 -0800198 }
199 };
200
Chris Craik8d1f2122015-11-24 16:40:09 -0800201 auto node = TestUtils::createNode(0, 0, 200, 200,
202 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik15c3f192015-12-03 12:16:56 -0800203 SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
204 kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
Chris Craikb565df12015-10-05 13:00:52 -0700205
206 // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
207 // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
208 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
Chris Craika1717272015-11-19 13:02:43 -0800209 for (int i = 0; i < LOOPS; i++) {
Chris Craikb565df12015-10-05 13:00:52 -0700210 canvas.translate(0, 10);
211 canvas.drawRect(0, 0, 10, 10, SkPaint());
212 canvas.drawBitmap(bitmap, 5, 0, nullptr);
213 }
214 canvas.restore();
215 });
216
Chris Craik8d1f2122015-11-24 16:40:09 -0800217 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
218 createSyncedNodeList(node), sLightCenter);
Chris Craik5854b342015-10-26 15:49:56 -0700219 SimpleBatchingTestRenderer renderer;
220 reorderer.replayBakedOps<TestDispatcher>(renderer);
Chris Craika1717272015-11-19 13:02:43 -0800221 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craik15c3f192015-12-03 12:16:56 -0800222 << "Expect number of ops = 2 * loop count";
Chris Craika1717272015-11-19 13:02:43 -0800223}
224
Chris Craik93e53e02015-12-17 18:42:44 -0800225TEST(OpReorderer, clippedMerging) {
226 class ClippedMergingTestRenderer : public TestRendererBase {
227 public:
228 void onMergedBitmapOps(const MergedBakedOpList& opList) override {
229 EXPECT_EQ(0, mIndex);
230 mIndex += opList.count;
231 EXPECT_EQ(4u, opList.count);
232 EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
233 EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
234 opList.clipSideFlags);
235 }
236 };
237 auto node = TestUtils::createNode(0, 0, 100, 100,
238 [](RenderProperties& props, TestCanvas& canvas) {
239 SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
240
241 // left side clipped (to inset left half)
242 canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
243 canvas.drawBitmap(bitmap, 0, 40, nullptr);
244
245 // top side clipped (to inset top half)
246 canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
247 canvas.drawBitmap(bitmap, 40, 0, nullptr);
248
249 // right side clipped (to inset right half)
250 canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
251 canvas.drawBitmap(bitmap, 80, 40, nullptr);
252
253 // bottom not clipped, just abutting (inset bottom half)
254 canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
255 canvas.drawBitmap(bitmap, 40, 70, nullptr);
256 });
257
258 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
259 createSyncedNodeList(node), sLightCenter);
260 ClippedMergingTestRenderer renderer;
261 reorderer.replayBakedOps<TestDispatcher>(renderer);
262 EXPECT_EQ(4, renderer.getIndex());
263}
264
Chris Craikd7448e62015-12-15 10:34:36 -0800265TEST(OpReorderer, textMerging) {
266 class TextMergingTestRenderer : public TestRendererBase {
267 public:
268 void onMergedTextOps(const MergedBakedOpList& opList) override {
269 EXPECT_EQ(0, mIndex);
270 mIndex += opList.count;
271 EXPECT_EQ(2u, opList.count);
272 EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
273 EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
274 EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
275 }
276 };
277 auto node = TestUtils::createNode(0, 0, 400, 400,
278 [](RenderProperties& props, TestCanvas& canvas) {
279 SkPaint paint;
280 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
281 paint.setAntiAlias(true);
282 paint.setTextSize(50);
283 TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
284 TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
285 });
286 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
287 createSyncedNodeList(node), sLightCenter);
288 TextMergingTestRenderer renderer;
289 reorderer.replayBakedOps<TestDispatcher>(renderer);
290 EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
291}
292
293TEST(OpReorderer, textStrikethrough) {
Chris Craika1717272015-11-19 13:02:43 -0800294 const int LOOPS = 5;
295 class TextStrikethroughTestRenderer : public TestRendererBase {
296 public:
297 void onRectOp(const RectOp& op, const BakedOpState& state) override {
298 EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
299 }
Chris Craik15c3f192015-12-03 12:16:56 -0800300 void onMergedTextOps(const MergedBakedOpList& opList) override {
301 EXPECT_EQ(0, mIndex);
302 mIndex += opList.count;
303 EXPECT_EQ(5u, opList.count);
Chris Craika1717272015-11-19 13:02:43 -0800304 }
305 };
Chris Craik8d1f2122015-11-24 16:40:09 -0800306 auto node = TestUtils::createNode(0, 0, 200, 2000,
307 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craika1717272015-11-19 13:02:43 -0800308 SkPaint textPaint;
309 textPaint.setAntiAlias(true);
310 textPaint.setTextSize(20);
311 textPaint.setStrikeThruText(true);
Chris Craik42a54072015-11-24 11:41:54 -0800312 textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
Chris Craika1717272015-11-19 13:02:43 -0800313 for (int i = 0; i < LOOPS; i++) {
314 TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
315 }
316 });
Chris Craik8d1f2122015-11-24 16:40:09 -0800317 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
318 createSyncedNodeList(node), sLightCenter);
Chris Craika1717272015-11-19 13:02:43 -0800319 TextStrikethroughTestRenderer renderer;
320 reorderer.replayBakedOps<TestDispatcher>(renderer);
321 EXPECT_EQ(2 * LOOPS, renderer.getIndex())
Chris Craikd7448e62015-12-15 10:34:36 -0800322 << "Expect number of ops = 2 * loop count";
Chris Craikb565df12015-10-05 13:00:52 -0700323}
324
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800325RENDERTHREAD_TEST(OpReorderer, textureLayer) {
326 class TextureLayerTestRenderer : public TestRendererBase {
327 public:
328 void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
329 EXPECT_EQ(0, mIndex++);
Chris Craike4db79d2015-12-22 16:32:23 -0800330 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800331 EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
332
333 Matrix4 expected;
334 expected.loadTranslate(5, 5, 0);
335 EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
336 }
337 };
338
339 auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
340 [](Matrix4* transform) {
341 transform->loadTranslate(5, 5, 0);
342 });
343
344 auto node = TestUtils::createNode(0, 0, 200, 200,
345 [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
346 canvas.save(SkCanvas::kMatrixClip_SaveFlag);
347 canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
348 canvas.drawLayer(layerUpdater.get());
349 canvas.restore();
350 });
351 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
352 createSyncedNodeList(node), sLightCenter);
353 TextureLayerTestRenderer renderer;
354 reorderer.replayBakedOps<TestDispatcher>(renderer);
355 EXPECT_EQ(1, renderer.getIndex());
356}
357
Chris Craikb565df12015-10-05 13:00:52 -0700358TEST(OpReorderer, renderNode) {
Chris Craikd3daa312015-11-06 10:59:56 -0800359 class RenderNodeTestRenderer : public TestRendererBase {
360 public:
361 void onRectOp(const RectOp& op, const BakedOpState& state) override {
362 switch(mIndex++) {
363 case 0:
Chris Craik5430ab22015-12-10 16:28:16 -0800364 EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
Chris Craikd3daa312015-11-06 10:59:56 -0800365 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
366 break;
367 case 1:
368 EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
369 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
370 break;
371 default:
372 ADD_FAILURE();
373 }
374 }
375 };
376
Chris Craik8d1f2122015-11-24 16:40:09 -0800377 auto child = TestUtils::createNode(10, 10, 110, 110,
378 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikb565df12015-10-05 13:00:52 -0700379 SkPaint paint;
380 paint.setColor(SK_ColorWHITE);
381 canvas.drawRect(0, 0, 100, 100, paint);
382 });
383
Chris Craik8d1f2122015-11-24 16:40:09 -0800384 auto parent = TestUtils::createNode(0, 0, 200, 200,
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800385 [&child](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700386 SkPaint paint;
387 paint.setColor(SK_ColorDKGRAY);
388 canvas.drawRect(0, 0, 200, 200, paint);
Chris Craikb565df12015-10-05 13:00:52 -0700389
Chris Craikddf22152015-10-14 17:42:47 -0700390 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
391 canvas.translate(40, 40);
Chris Craikd2dfd8f2015-12-16 14:27:20 -0800392 canvas.drawRenderNode(child.get());
Chris Craikddf22152015-10-14 17:42:47 -0700393 canvas.restore();
Chris Craikb565df12015-10-05 13:00:52 -0700394 });
395
Chris Craik98787e62015-11-13 10:55:30 -0800396 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
397 createSyncedNodeList(parent), sLightCenter);
Chris Craik5854b342015-10-26 15:49:56 -0700398 RenderNodeTestRenderer renderer;
399 reorderer.replayBakedOps<TestDispatcher>(renderer);
Chris Craikb565df12015-10-05 13:00:52 -0700400}
401
Chris Craikddf22152015-10-14 17:42:47 -0700402TEST(OpReorderer, clipped) {
Chris Craikd3daa312015-11-06 10:59:56 -0800403 class ClippedTestRenderer : public TestRendererBase {
404 public:
405 void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
406 EXPECT_EQ(0, mIndex++);
407 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800408 EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800409 EXPECT_TRUE(state.computedState.transform.isIdentity());
410 }
411 };
412
Chris Craik8d1f2122015-11-24 16:40:09 -0800413 auto node = TestUtils::createNode(0, 0, 200, 200,
414 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikddf22152015-10-14 17:42:47 -0700415 SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
416 canvas.drawBitmap(bitmap, 0, 0, nullptr);
417 });
Chris Craikddf22152015-10-14 17:42:47 -0700418
Chris Craik0b7e8242015-10-28 16:50:44 -0700419 OpReorderer reorderer(sEmptyLayerUpdateQueue,
420 SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
Chris Craik98787e62015-11-13 10:55:30 -0800421 200, 200, createSyncedNodeList(node), sLightCenter);
Chris Craik5854b342015-10-26 15:49:56 -0700422 ClippedTestRenderer renderer;
423 reorderer.replayBakedOps<TestDispatcher>(renderer);
Chris Craikddf22152015-10-14 17:42:47 -0700424}
425
Chris Craik6fe991e52015-10-20 09:39:42 -0700426TEST(OpReorderer, saveLayerSimple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800427 class SaveLayerSimpleTestRenderer : public TestRendererBase {
428 public:
429 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
430 EXPECT_EQ(0, mIndex++);
431 EXPECT_EQ(180u, width);
432 EXPECT_EQ(180u, height);
433 return nullptr;
434 }
435 void endLayer() override {
436 EXPECT_EQ(2, mIndex++);
437 }
438 void onRectOp(const RectOp& op, const BakedOpState& state) override {
439 EXPECT_EQ(1, mIndex++);
440 EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
Chris Craik5430ab22015-12-10 16:28:16 -0800441 EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800442 EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800443
444 Matrix4 expectedTransform;
445 expectedTransform.loadTranslate(-10, -10, 0);
446 EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
447 }
448 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
449 EXPECT_EQ(3, mIndex++);
450 EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
Chris Craike4db79d2015-12-22 16:32:23 -0800451 EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
Chris Craikd3daa312015-11-06 10:59:56 -0800452 EXPECT_TRUE(state.computedState.transform.isIdentity());
453 }
454 };
455
Chris Craik8d1f2122015-11-24 16:40:09 -0800456 auto node = TestUtils::createNode(0, 0, 200, 200,
457 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700458 canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
459 canvas.drawRect(10, 10, 190, 190, SkPaint());
460 canvas.restore();
461 });
Chris Craik8d1f2122015-11-24 16:40:09 -0800462 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
463 createSyncedNodeList(node), sLightCenter);
Chris Craik5854b342015-10-26 15:49:56 -0700464 SaveLayerSimpleTestRenderer renderer;
465 reorderer.replayBakedOps<TestDispatcher>(renderer);
466 EXPECT_EQ(4, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700467}
Chris Craik6fe991e52015-10-20 09:39:42 -0700468
Chris Craik6fe991e52015-10-20 09:39:42 -0700469TEST(OpReorderer, saveLayerNested) {
Chris Craikd3daa312015-11-06 10:59:56 -0800470 /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
471 * - startTemporaryLayer2, rect2 endLayer2
472 * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
473 * - startFrame, layerOp1, endFrame
474 */
475 class SaveLayerNestedTestRenderer : public TestRendererBase {
476 public:
477 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
478 const int index = mIndex++;
479 if (index == 0) {
480 EXPECT_EQ(400u, width);
481 EXPECT_EQ(400u, height);
482 return (OffscreenBuffer*) 0x400;
483 } else if (index == 3) {
484 EXPECT_EQ(800u, width);
485 EXPECT_EQ(800u, height);
486 return (OffscreenBuffer*) 0x800;
487 } else { ADD_FAILURE(); }
488 return (OffscreenBuffer*) nullptr;
489 }
490 void endLayer() override {
491 int index = mIndex++;
492 EXPECT_TRUE(index == 2 || index == 6);
493 }
Chris Craik98787e62015-11-13 10:55:30 -0800494 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800495 EXPECT_EQ(7, mIndex++);
496 }
Chris Craike4db79d2015-12-22 16:32:23 -0800497 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800498 EXPECT_EQ(9, mIndex++);
499 }
500 void onRectOp(const RectOp& op, const BakedOpState& state) override {
501 const int index = mIndex++;
502 if (index == 1) {
Chris Craik5430ab22015-12-10 16:28:16 -0800503 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
Chris Craikd3daa312015-11-06 10:59:56 -0800504 } else if (index == 4) {
Chris Craik5430ab22015-12-10 16:28:16 -0800505 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
Chris Craikd3daa312015-11-06 10:59:56 -0800506 } else { ADD_FAILURE(); }
507 }
508 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
509 const int index = mIndex++;
510 if (index == 5) {
511 EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800512 EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
Chris Craikd3daa312015-11-06 10:59:56 -0800513 } else if (index == 8) {
514 EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
Chris Craik5430ab22015-12-10 16:28:16 -0800515 EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
Chris Craikd3daa312015-11-06 10:59:56 -0800516 } else { ADD_FAILURE(); }
517 }
518 };
519
Chris Craik8d1f2122015-11-24 16:40:09 -0800520 auto node = TestUtils::createNode(0, 0, 800, 800,
521 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700522 canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
523 {
524 canvas.drawRect(0, 0, 800, 800, SkPaint());
525 canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
526 {
527 canvas.drawRect(0, 0, 400, 400, SkPaint());
528 }
529 canvas.restore();
530 }
531 canvas.restore();
532 });
533
Chris Craik8d1f2122015-11-24 16:40:09 -0800534 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
535 createSyncedNodeList(node), sLightCenter);
Chris Craik5854b342015-10-26 15:49:56 -0700536 SaveLayerNestedTestRenderer renderer;
537 reorderer.replayBakedOps<TestDispatcher>(renderer);
538 EXPECT_EQ(10, renderer.getIndex());
Chris Craikb565df12015-10-05 13:00:52 -0700539}
Chris Craik6fe991e52015-10-20 09:39:42 -0700540
541TEST(OpReorderer, saveLayerContentRejection) {
Chris Craik8d1f2122015-11-24 16:40:09 -0800542 auto node = TestUtils::createNode(0, 0, 200, 200,
543 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik6fe991e52015-10-20 09:39:42 -0700544 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
545 canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
546 canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
547
548 // draw within save layer may still be recorded, but shouldn't be drawn
549 canvas.drawRect(200, 200, 400, 400, SkPaint());
550
551 canvas.restore();
552 canvas.restore();
553 });
Chris Craik8d1f2122015-11-24 16:40:09 -0800554 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
555 createSyncedNodeList(node), sLightCenter);
Chris Craik6fe991e52015-10-20 09:39:42 -0700556
Chris Craik5854b342015-10-26 15:49:56 -0700557 FailRenderer renderer;
Chris Craik6fe991e52015-10-20 09:39:42 -0700558 // should see no ops, even within the layer, since the layer should be rejected
Chris Craik5854b342015-10-26 15:49:56 -0700559 reorderer.replayBakedOps<TestDispatcher>(renderer);
Chris Craik6fe991e52015-10-20 09:39:42 -0700560}
561
Chris Craik98787e62015-11-13 10:55:30 -0800562RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) {
Chris Craikd3daa312015-11-06 10:59:56 -0800563 class HwLayerSimpleTestRenderer : public TestRendererBase {
564 public:
Chris Craik98787e62015-11-13 10:55:30 -0800565 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800566 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -0800567 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
568 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
569 EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
Chris Craikd3daa312015-11-06 10:59:56 -0800570 }
571 void onRectOp(const RectOp& op, const BakedOpState& state) override {
572 EXPECT_EQ(1, mIndex++);
573
574 EXPECT_TRUE(state.computedState.transform.isIdentity())
575 << "Transform should be reset within layer";
576
Chris Craike4db79d2015-12-22 16:32:23 -0800577 EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
Chris Craikd3daa312015-11-06 10:59:56 -0800578 << "Damage rect should be used to clip layer content";
579 }
580 void endLayer() override {
581 EXPECT_EQ(2, mIndex++);
582 }
Chris Craik98787e62015-11-13 10:55:30 -0800583 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800584 EXPECT_EQ(3, mIndex++);
585 }
586 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
587 EXPECT_EQ(4, mIndex++);
588 }
Chris Craike4db79d2015-12-22 16:32:23 -0800589 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800590 EXPECT_EQ(5, mIndex++);
591 }
592 };
593
Chris Craik8d1f2122015-11-24 16:40:09 -0800594 auto node = TestUtils::createNode(10, 10, 110, 110,
John Reck16c9d6a2015-11-17 15:51:08 -0800595 [](RenderProperties& props, RecordingCanvas& canvas) {
596 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700597 SkPaint paint;
598 paint.setColor(SK_ColorWHITE);
599 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -0800600 });
Chris Craik98787e62015-11-13 10:55:30 -0800601 OffscreenBuffer** layerHandle = node->getLayerHandle();
Chris Craik0b7e8242015-10-28 16:50:44 -0700602
Chris Craik98787e62015-11-13 10:55:30 -0800603 // create RenderNode's layer here in same way prepareTree would
604 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
605 *layerHandle = &layer;
Chris Craik0b7e8242015-10-28 16:50:44 -0700606
Chris Craik98787e62015-11-13 10:55:30 -0800607 auto syncedNodeList = createSyncedNodeList(node);
Chris Craik0b7e8242015-10-28 16:50:44 -0700608
609 // only enqueue partial damage
Chris Craik98787e62015-11-13 10:55:30 -0800610 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -0700611 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
612
Chris Craik98787e62015-11-13 10:55:30 -0800613 OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
614 syncedNodeList, sLightCenter);
Chris Craik0b7e8242015-10-28 16:50:44 -0700615 HwLayerSimpleTestRenderer renderer;
616 reorderer.replayBakedOps<TestDispatcher>(renderer);
617 EXPECT_EQ(6, renderer.getIndex());
618
619 // clean up layer pointer, so we can safely destruct RenderNode
Chris Craik98787e62015-11-13 10:55:30 -0800620 *layerHandle = nullptr;
Chris Craik0b7e8242015-10-28 16:50:44 -0700621}
622
Chris Craik98787e62015-11-13 10:55:30 -0800623RENDERTHREAD_TEST(OpReorderer, hwLayerComplex) {
Chris Craikd3daa312015-11-06 10:59:56 -0800624 /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
625 * - startRepaintLayer(child), rect(grey), endLayer
626 * - startTemporaryLayer, drawLayer(child), endLayer
627 * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
628 * - startFrame, drawLayer(parent), endLayerb
629 */
630 class HwLayerComplexTestRenderer : public TestRendererBase {
631 public:
632 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
633 EXPECT_EQ(3, mIndex++); // savelayer first
634 return (OffscreenBuffer*)0xabcd;
635 }
Chris Craik98787e62015-11-13 10:55:30 -0800636 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800637 int index = mIndex++;
638 if (index == 0) {
639 // starting inner layer
Chris Craik98787e62015-11-13 10:55:30 -0800640 EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
641 EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800642 } else if (index == 6) {
643 // starting outer layer
Chris Craik98787e62015-11-13 10:55:30 -0800644 EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
645 EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800646 } else { ADD_FAILURE(); }
647 }
648 void onRectOp(const RectOp& op, const BakedOpState& state) override {
649 int index = mIndex++;
650 if (index == 1) {
651 // inner layer's rect (white)
652 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
653 } else if (index == 7) {
654 // outer layer's rect (grey)
655 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
656 } else { ADD_FAILURE(); }
657 }
658 void endLayer() override {
659 int index = mIndex++;
660 EXPECT_TRUE(index == 2 || index == 5 || index == 9);
661 }
Chris Craik98787e62015-11-13 10:55:30 -0800662 void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800663 EXPECT_EQ(10, mIndex++);
664 }
665 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
Chris Craik98787e62015-11-13 10:55:30 -0800666 OffscreenBuffer* layer = *op.layerHandle;
Chris Craikd3daa312015-11-06 10:59:56 -0800667 int index = mIndex++;
668 if (index == 4) {
Chris Craik98787e62015-11-13 10:55:30 -0800669 EXPECT_EQ(100u, layer->viewportWidth);
670 EXPECT_EQ(100u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800671 } else if (index == 8) {
672 EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
673 } else if (index == 11) {
Chris Craik98787e62015-11-13 10:55:30 -0800674 EXPECT_EQ(200u, layer->viewportWidth);
675 EXPECT_EQ(200u, layer->viewportHeight);
Chris Craikd3daa312015-11-06 10:59:56 -0800676 } else { ADD_FAILURE(); }
677 }
Chris Craike4db79d2015-12-22 16:32:23 -0800678 void endFrame(const Rect& repaintRect) override {
Chris Craikd3daa312015-11-06 10:59:56 -0800679 EXPECT_EQ(12, mIndex++);
680 }
681 };
682
John Reck16c9d6a2015-11-17 15:51:08 -0800683 auto child = TestUtils::createNode(50, 50, 150, 150,
684 [](RenderProperties& props, RecordingCanvas& canvas) {
685 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700686 SkPaint paint;
687 paint.setColor(SK_ColorWHITE);
688 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -0800689 });
Chris Craik98787e62015-11-13 10:55:30 -0800690 OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
691 *(child->getLayerHandle()) = &childLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -0700692
693 RenderNode* childPtr = child.get();
John Reck16c9d6a2015-11-17 15:51:08 -0800694 auto parent = TestUtils::createNode(0, 0, 200, 200,
695 [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
696 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik0b7e8242015-10-28 16:50:44 -0700697 SkPaint paint;
698 paint.setColor(SK_ColorDKGRAY);
699 canvas.drawRect(0, 0, 200, 200, paint);
700
701 canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
702 canvas.drawRenderNode(childPtr);
703 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -0800704 });
Chris Craik98787e62015-11-13 10:55:30 -0800705 OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
706 *(parent->getLayerHandle()) = &parentLayer;
Chris Craik0b7e8242015-10-28 16:50:44 -0700707
Chris Craik98787e62015-11-13 10:55:30 -0800708 auto syncedList = createSyncedNodeList(parent);
Chris Craik0b7e8242015-10-28 16:50:44 -0700709
Chris Craik98787e62015-11-13 10:55:30 -0800710 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
Chris Craik0b7e8242015-10-28 16:50:44 -0700711 layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
712 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
713
Chris Craik98787e62015-11-13 10:55:30 -0800714 OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
715 syncedList, sLightCenter);
Chris Craik0b7e8242015-10-28 16:50:44 -0700716 HwLayerComplexTestRenderer renderer;
717 reorderer.replayBakedOps<TestDispatcher>(renderer);
718 EXPECT_EQ(13, renderer.getIndex());
719
720 // clean up layer pointers, so we can safely destruct RenderNodes
721 *(child->getLayerHandle()) = nullptr;
722 *(parent->getLayerHandle()) = nullptr;
723}
724
Chris Craik161f54b2015-11-05 11:08:52 -0800725static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
726 SkPaint paint;
727 paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
728 canvas->drawRect(0, 0, 100, 100, paint);
729}
730static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
John Reck16c9d6a2015-11-17 15:51:08 -0800731 auto node = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -0800732 [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -0800733 drawOrderedRect(&canvas, expectedDrawOrder);
734 });
735 node->mutateStagingProperties().setTranslationZ(z);
736 node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
737 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
738}
739TEST(OpReorderer, zReorder) {
Chris Craikd3daa312015-11-06 10:59:56 -0800740 class ZReorderTestRenderer : public TestRendererBase {
741 public:
742 void onRectOp(const RectOp& op, const BakedOpState& state) override {
743 int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
744 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
745 }
746 };
747
John Reck16c9d6a2015-11-17 15:51:08 -0800748 auto parent = TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -0800749 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik161f54b2015-11-05 11:08:52 -0800750 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
751 drawOrderedRect(&canvas, 1);
752 canvas.insertReorderBarrier(true);
753 drawOrderedNode(&canvas, 6, 2.0f);
754 drawOrderedRect(&canvas, 3);
755 drawOrderedNode(&canvas, 4, 0.0f);
756 drawOrderedRect(&canvas, 5);
757 drawOrderedNode(&canvas, 2, -2.0f);
758 drawOrderedNode(&canvas, 7, 2.0f);
759 canvas.insertReorderBarrier(false);
760 drawOrderedRect(&canvas, 8);
761 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
762 });
Chris Craik98787e62015-11-13 10:55:30 -0800763 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
764 createSyncedNodeList(parent), sLightCenter);
Chris Craik161f54b2015-11-05 11:08:52 -0800765 ZReorderTestRenderer renderer;
766 reorderer.replayBakedOps<TestDispatcher>(renderer);
767 EXPECT_EQ(10, renderer.getIndex());
768};
769
Chris Craik8d1f2122015-11-24 16:40:09 -0800770TEST(OpReorderer, projectionReorder) {
771 static const int scrollX = 5;
772 static const int scrollY = 10;
773 class ProjectionReorderTestRenderer : public TestRendererBase {
774 public:
775 void onRectOp(const RectOp& op, const BakedOpState& state) override {
776 const int index = mIndex++;
777
778 Matrix4 expectedMatrix;
779 switch (index) {
780 case 0:
781 EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
782 EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
783 expectedMatrix.loadIdentity();
784 break;
785 case 1:
786 EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
787 EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
788 expectedMatrix.loadTranslate(50, 50, 0); // TODO: should scroll be respected here?
789 break;
790 case 2:
791 EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
792 EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
793 expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
794 break;
795 default:
796 ADD_FAILURE();
797 }
798 EXPECT_MATRIX_APPROX_EQ(expectedMatrix, state.computedState.transform);
799 }
800 };
801
802 /**
803 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
804 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
805 * draw, but because it is projected backwards, it's drawn in between B and C.
806 *
807 * The parent is scrolled by scrollX/scrollY, but this does not affect the background
808 * (which isn't affected by scroll).
809 */
810 auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
811 [](RenderProperties& properties, RecordingCanvas& canvas) {
812 properties.setProjectionReceiver(true);
813 // scroll doesn't apply to background, so undone via translationX/Y
814 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
815 properties.setTranslationX(scrollX);
816 properties.setTranslationY(scrollY);
817
818 SkPaint paint;
819 paint.setColor(SK_ColorWHITE);
820 canvas.drawRect(0, 0, 100, 100, paint);
821 });
822 auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
823 [](RenderProperties& properties, RecordingCanvas& canvas) {
824 properties.setProjectBackwards(true);
825 properties.setClipToBounds(false);
826 SkPaint paint;
827 paint.setColor(SK_ColorDKGRAY);
828 canvas.drawRect(-10, -10, 60, 60, paint);
829 });
830 auto child = TestUtils::createNode(0, 50, 100, 100,
831 [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
832 SkPaint paint;
833 paint.setColor(SK_ColorBLUE);
834 canvas.drawRect(0, 0, 100, 50, paint);
835 canvas.drawRenderNode(projectingRipple.get());
836 });
837 auto parent = TestUtils::createNode(0, 0, 100, 100,
838 [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
839 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
840 canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
841 canvas.drawRenderNode(receiverBackground.get());
842 canvas.drawRenderNode(child.get());
843 canvas.restore();
844 });
845
846 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
847 createSyncedNodeList(parent), sLightCenter);
848 ProjectionReorderTestRenderer renderer;
849 reorderer.replayBakedOps<TestDispatcher>(renderer);
850 EXPECT_EQ(3, renderer.getIndex());
851}
852
Chris Craik98787e62015-11-13 10:55:30 -0800853// creates a 100x100 shadow casting node with provided translationZ
854static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
John Reck16c9d6a2015-11-17 15:51:08 -0800855 return TestUtils::createNode(0, 0, 100, 100,
Chris Craik8d1f2122015-11-24 16:40:09 -0800856 [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
John Reck16c9d6a2015-11-17 15:51:08 -0800857 properties.setTranslationZ(translationZ);
858 properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
Chris Craik98787e62015-11-13 10:55:30 -0800859 SkPaint paint;
860 paint.setColor(SK_ColorWHITE);
861 canvas.drawRect(0, 0, 100, 100, paint);
Chris Craik98787e62015-11-13 10:55:30 -0800862 });
863}
864
Chris Craikd3daa312015-11-06 10:59:56 -0800865TEST(OpReorderer, shadow) {
866 class ShadowTestRenderer : public TestRendererBase {
867 public:
868 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
869 EXPECT_EQ(0, mIndex++);
Chris Craik98787e62015-11-13 10:55:30 -0800870 EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
871 EXPECT_TRUE(op.casterPath->isRect(nullptr));
872 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowMatrixXY);
873
874 Matrix4 expectedZ;
875 expectedZ.loadTranslate(0, 0, 5);
876 EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowMatrixZ);
Chris Craikd3daa312015-11-06 10:59:56 -0800877 }
878 void onRectOp(const RectOp& op, const BakedOpState& state) override {
879 EXPECT_EQ(1, mIndex++);
880 }
881 };
Chris Craik161f54b2015-11-05 11:08:52 -0800882
Chris Craik8d1f2122015-11-24 16:40:09 -0800883 auto parent = TestUtils::createNode(0, 0, 200, 200,
884 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craikd3daa312015-11-06 10:59:56 -0800885 canvas.insertReorderBarrier(true);
Chris Craik98787e62015-11-13 10:55:30 -0800886 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
Chris Craikd3daa312015-11-06 10:59:56 -0800887 });
888
Chris Craik98787e62015-11-13 10:55:30 -0800889 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
890 createSyncedNodeList(parent), sLightCenter);
Chris Craikd3daa312015-11-06 10:59:56 -0800891 ShadowTestRenderer renderer;
892 reorderer.replayBakedOps<TestDispatcher>(renderer);
893 EXPECT_EQ(2, renderer.getIndex());
894}
Chris Craik76caecf2015-11-02 19:17:45 -0800895
Chris Craik98787e62015-11-13 10:55:30 -0800896TEST(OpReorderer, shadowSaveLayer) {
897 class ShadowSaveLayerTestRenderer : public TestRendererBase {
898 public:
899 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
900 EXPECT_EQ(0, mIndex++);
901 return nullptr;
902 }
903 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
904 EXPECT_EQ(1, mIndex++);
905 EXPECT_FLOAT_EQ(50, op.lightCenter.x);
906 EXPECT_FLOAT_EQ(40, op.lightCenter.y);
907 }
908 void onRectOp(const RectOp& op, const BakedOpState& state) override {
909 EXPECT_EQ(2, mIndex++);
910 }
911 void endLayer() override {
912 EXPECT_EQ(3, mIndex++);
913 }
914 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
915 EXPECT_EQ(4, mIndex++);
916 }
917 };
918
Chris Craik8d1f2122015-11-24 16:40:09 -0800919 auto parent = TestUtils::createNode(0, 0, 200, 200,
920 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -0800921 // save/restore outside of reorderBarrier, so they don't get moved out of place
922 canvas.translate(20, 10);
923 int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
924 canvas.insertReorderBarrier(true);
925 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
926 canvas.insertReorderBarrier(false);
927 canvas.restoreToCount(count);
928 });
929
930 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
931 createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
Chris Craik98787e62015-11-13 10:55:30 -0800932 ShadowSaveLayerTestRenderer renderer;
933 reorderer.replayBakedOps<TestDispatcher>(renderer);
934 EXPECT_EQ(5, renderer.getIndex());
935}
936
937RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
938 class ShadowHwLayerTestRenderer : public TestRendererBase {
939 public:
940 void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
941 EXPECT_EQ(0, mIndex++);
942 }
943 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
944 EXPECT_EQ(1, mIndex++);
945 EXPECT_FLOAT_EQ(50, op.lightCenter.x);
946 EXPECT_FLOAT_EQ(40, op.lightCenter.y);
947 }
948 void onRectOp(const RectOp& op, const BakedOpState& state) override {
949 EXPECT_EQ(2, mIndex++);
950 }
951 void endLayer() override {
952 EXPECT_EQ(3, mIndex++);
953 }
954 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
955 EXPECT_EQ(4, mIndex++);
956 }
957 };
958
Chris Craik8d1f2122015-11-24 16:40:09 -0800959 auto parent = TestUtils::createNode(50, 60, 150, 160,
John Reck16c9d6a2015-11-17 15:51:08 -0800960 [](RenderProperties& props, RecordingCanvas& canvas) {
961 props.mutateLayerProperties().setType(LayerType::RenderLayer);
Chris Craik98787e62015-11-13 10:55:30 -0800962 canvas.insertReorderBarrier(true);
963 canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
964 canvas.translate(20, 10);
965 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
966 canvas.restore();
John Reck16c9d6a2015-11-17 15:51:08 -0800967 });
Chris Craik98787e62015-11-13 10:55:30 -0800968 OffscreenBuffer** layerHandle = parent->getLayerHandle();
969
970 // create RenderNode's layer here in same way prepareTree would, setting windowTransform
971 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
972 Matrix4 windowTransform;
973 windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
974 layer.setWindowTransform(windowTransform);
975 *layerHandle = &layer;
976
977 auto syncedList = createSyncedNodeList(parent);
978 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
979 layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
980 OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
981 syncedList, (Vector3) { 100, 100, 100 });
Chris Craik98787e62015-11-13 10:55:30 -0800982 ShadowHwLayerTestRenderer renderer;
983 reorderer.replayBakedOps<TestDispatcher>(renderer);
984 EXPECT_EQ(5, renderer.getIndex());
985
986 // clean up layer pointer, so we can safely destruct RenderNode
987 *layerHandle = nullptr;
988}
989
990TEST(OpReorderer, shadowLayering) {
991 class ShadowLayeringTestRenderer : public TestRendererBase {
992 public:
993 void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
994 int index = mIndex++;
995 EXPECT_TRUE(index == 0 || index == 1);
996 }
997 void onRectOp(const RectOp& op, const BakedOpState& state) override {
998 int index = mIndex++;
999 EXPECT_TRUE(index == 2 || index == 3);
1000 }
1001 };
Chris Craik8d1f2122015-11-24 16:40:09 -08001002 auto parent = TestUtils::createNode(0, 0, 200, 200,
1003 [](RenderProperties& props, RecordingCanvas& canvas) {
Chris Craik98787e62015-11-13 10:55:30 -08001004 canvas.insertReorderBarrier(true);
1005 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
1006 canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
1007 });
1008
1009 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
1010 createSyncedNodeList(parent), sLightCenter);
Chris Craik98787e62015-11-13 10:55:30 -08001011 ShadowLayeringTestRenderer renderer;
1012 reorderer.replayBakedOps<TestDispatcher>(renderer);
1013 EXPECT_EQ(4, renderer.getIndex());
1014}
1015
John Reck16c9d6a2015-11-17 15:51:08 -08001016static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
Chris Craik76caecf2015-11-02 19:17:45 -08001017 std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
Chris Craikd3daa312015-11-06 10:59:56 -08001018 class PropertyTestRenderer : public TestRendererBase {
1019 public:
1020 PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
1021 : mCallback(callback) {}
1022 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1023 EXPECT_EQ(mIndex++, 0);
1024 mCallback(op, state);
1025 }
1026 std::function<void(const RectOp&, const BakedOpState&)> mCallback;
1027 };
1028
John Reck16c9d6a2015-11-17 15:51:08 -08001029 auto node = TestUtils::createNode(0, 0, 100, 100,
1030 [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
1031 propSetupCallback(props);
Chris Craik76caecf2015-11-02 19:17:45 -08001032 SkPaint paint;
1033 paint.setColor(SK_ColorWHITE);
1034 canvas.drawRect(0, 0, 100, 100, paint);
John Reck16c9d6a2015-11-17 15:51:08 -08001035 });
Chris Craik76caecf2015-11-02 19:17:45 -08001036
Chris Craik98787e62015-11-13 10:55:30 -08001037 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
1038 createSyncedNodeList(node), sLightCenter);
Chris Craik76caecf2015-11-02 19:17:45 -08001039 PropertyTestRenderer renderer(opValidateCallback);
1040 reorderer.replayBakedOps<TestDispatcher>(renderer);
1041 EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
1042}
1043
1044TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
1045 testProperty([](RenderProperties& properties) {
1046 properties.setAlpha(0.5f);
1047 properties.setHasOverlappingRendering(false);
Chris Craik76caecf2015-11-02 19:17:45 -08001048 }, [](const RectOp& op, const BakedOpState& state) {
1049 EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
1050 });
1051}
1052
1053TEST(OpReorderer, renderPropClipping) {
1054 testProperty([](RenderProperties& properties) {
1055 properties.setClipToBounds(true);
1056 properties.setClipBounds(Rect(10, 20, 300, 400));
Chris Craik76caecf2015-11-02 19:17:45 -08001057 }, [](const RectOp& op, const BakedOpState& state) {
1058 EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
1059 << "Clip rect should be intersection of node bounds and clip bounds";
1060 });
1061}
1062
1063TEST(OpReorderer, renderPropRevealClip) {
1064 testProperty([](RenderProperties& properties) {
1065 properties.mutableRevealClip().set(true, 50, 50, 25);
Chris Craik76caecf2015-11-02 19:17:45 -08001066 }, [](const RectOp& op, const BakedOpState& state) {
1067 ASSERT_NE(nullptr, state.roundRectClipState);
1068 EXPECT_TRUE(state.roundRectClipState->highPriority);
1069 EXPECT_EQ(25, state.roundRectClipState->radius);
1070 EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
1071 });
1072}
1073
1074TEST(OpReorderer, renderPropOutlineClip) {
1075 testProperty([](RenderProperties& properties) {
1076 properties.mutableOutline().setShouldClip(true);
1077 properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
Chris Craik76caecf2015-11-02 19:17:45 -08001078 }, [](const RectOp& op, const BakedOpState& state) {
1079 ASSERT_NE(nullptr, state.roundRectClipState);
1080 EXPECT_FALSE(state.roundRectClipState->highPriority);
1081 EXPECT_EQ(5, state.roundRectClipState->radius);
1082 EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
1083 });
1084}
1085
1086TEST(OpReorderer, renderPropTransform) {
1087 testProperty([](RenderProperties& properties) {
1088 properties.setLeftTopRightBottom(10, 10, 110, 110);
1089
1090 SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
1091 properties.setStaticMatrix(&staticMatrix);
1092
1093 // ignored, since static overrides animation
1094 SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
1095 properties.setAnimationMatrix(&animationMatrix);
1096
1097 properties.setTranslationX(10);
1098 properties.setTranslationY(20);
1099 properties.setScaleX(0.5f);
1100 properties.setScaleY(0.7f);
Chris Craik76caecf2015-11-02 19:17:45 -08001101 }, [](const RectOp& op, const BakedOpState& state) {
1102 Matrix4 matrix;
1103 matrix.loadTranslate(10, 10, 0); // left, top
1104 matrix.scale(1.2f, 1.2f, 1); // static matrix
1105 // ignore animation matrix, since static overrides it
1106
1107 // translation xy
1108 matrix.translate(10, 20);
1109
1110 // scale xy (from default pivot - center)
1111 matrix.translate(50, 50);
1112 matrix.scale(0.5f, 0.7f, 1);
1113 matrix.translate(-50, -50);
1114 EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
1115 << "Op draw matrix must match expected combination of transformation properties";
1116 });
1117}
Chris Craik161f54b2015-11-05 11:08:52 -08001118
Chris Craik8ecf41c2015-11-16 10:27:59 -08001119struct SaveLayerAlphaData {
1120 uint32_t layerWidth = 0;
1121 uint32_t layerHeight = 0;
1122 Rect rectClippedBounds;
1123 Matrix4 rectMatrix;
1124};
1125/**
1126 * Constructs a view to hit the temporary layer alpha property implementation:
1127 * a) 0 < alpha < 1
1128 * b) too big for layer (larger than maxTextureSize)
1129 * c) overlapping rendering content
1130 * returning observed data about layer size and content clip/transform.
1131 *
1132 * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
1133 * (for efficiency, and to fit in layer size constraints) based on parent clip.
1134 */
1135void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
John Reck16c9d6a2015-11-17 15:51:08 -08001136 std::function<void(RenderProperties&)> propSetupCallback) {
Chris Craik8ecf41c2015-11-16 10:27:59 -08001137 class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
1138 public:
1139 SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
1140 : mOutData(outData) {}
1141
1142 OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
1143 EXPECT_EQ(0, mIndex++);
1144 mOutData->layerWidth = width;
1145 mOutData->layerHeight = height;
1146 return nullptr;
1147 }
1148 void onRectOp(const RectOp& op, const BakedOpState& state) override {
1149 EXPECT_EQ(1, mIndex++);
1150
1151 mOutData->rectClippedBounds = state.computedState.clippedBounds;
1152 mOutData->rectMatrix = state.computedState.transform;
1153 }
1154 void endLayer() override {
1155 EXPECT_EQ(2, mIndex++);
1156 }
1157 void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
1158 EXPECT_EQ(3, mIndex++);
1159 }
1160 private:
1161 SaveLayerAlphaData* mOutData;
1162 };
1163
1164 ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
1165 << "Node must be bigger than max texture size to exercise saveLayer codepath";
John Reck16c9d6a2015-11-17 15:51:08 -08001166 auto node = TestUtils::createNode(0, 0, 10000, 10000,
1167 [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
1168 properties.setHasOverlappingRendering(true);
1169 properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
1170 // apply other properties
1171 propSetupCallback(properties);
1172
Chris Craik8ecf41c2015-11-16 10:27:59 -08001173 SkPaint paint;
1174 paint.setColor(SK_ColorWHITE);
1175 canvas.drawRect(0, 0, 10000, 10000, paint);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001176 });
1177 auto nodes = createSyncedNodeList(node); // sync before querying height
1178
1179 OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
1180 SaveLayerAlphaClipTestRenderer renderer(outObservedData);
1181 reorderer.replayBakedOps<TestDispatcher>(renderer);
1182
1183 // assert, since output won't be valid if we haven't seen a save layer triggered
1184 ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
1185}
1186
1187TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {
1188 SaveLayerAlphaData observedData;
1189 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1190 properties.setTranslationX(10); // offset rendering content
1191 properties.setTranslationY(-2000); // offset rendering content
Chris Craik8ecf41c2015-11-16 10:27:59 -08001192 });
1193 EXPECT_EQ(190u, observedData.layerWidth);
1194 EXPECT_EQ(200u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001195 EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
Chris Craik8ecf41c2015-11-16 10:27:59 -08001196 << "expect content to be clipped to screen area";
1197 Matrix4 expected;
1198 expected.loadTranslate(0, -2000, 0);
1199 EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
1200 << "expect content to be translated as part of being clipped";
1201}
1202
1203TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
1204 SaveLayerAlphaData observedData;
1205 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1206 // Translate and rotate the view so that the only visible part is the top left corner of
Chris Craik8d1f2122015-11-24 16:40:09 -08001207 // 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 -08001208 // bottom of the viewport.
1209 properties.setTranslationX(100);
1210 properties.setTranslationY(100);
1211 properties.setPivotX(0);
1212 properties.setPivotY(0);
1213 properties.setRotation(45);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001214 });
1215 // ceil(sqrt(2) / 2 * 200) = 142
1216 EXPECT_EQ(142u, observedData.layerWidth);
1217 EXPECT_EQ(142u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001218 EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001219 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1220}
1221
1222TEST(OpReorderer, renderPropSaveLayerAlphaScale) {
1223 SaveLayerAlphaData observedData;
1224 testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
1225 properties.setPivotX(0);
1226 properties.setPivotY(0);
1227 properties.setScaleX(2);
1228 properties.setScaleY(0.5f);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001229 });
1230 EXPECT_EQ(100u, observedData.layerWidth);
1231 EXPECT_EQ(400u, observedData.layerHeight);
Chris Craik5430ab22015-12-10 16:28:16 -08001232 EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
Chris Craik8ecf41c2015-11-16 10:27:59 -08001233 EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
1234}
1235
Chris Craik6fe991e52015-10-20 09:39:42 -07001236} // namespace uirenderer
1237} // namespace android