blob: 7c149520f909207f6961063931f0195eeddc7fd2 [file] [log] [blame]
Stan Iliev021693b2016-10-17 16:26:15 -04001/*
2 * Copyright (C) 2016 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#include <VectorDrawable.h>
19
20#include "AnimationContext.h"
21#include "DamageAccumulator.h"
22#include "IContextFactory.h"
23#include "pipeline/skia/SkiaDisplayList.h"
Stan Ilievdb45a4b2016-11-08 14:18:31 -050024#include "pipeline/skia/SkiaPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040025#include "pipeline/skia/SkiaRecordingCanvas.h"
26#include "renderthread/CanvasContext.h"
27#include "tests/common/TestUtils.h"
28#include "SkiaCanvas.h"
Stan Ilievdb45a4b2016-11-08 14:18:31 -050029#include <SkSurface_Base.h>
Stan Iliev021693b2016-10-17 16:26:15 -040030#include <SkLiteRecorder.h>
Stan Ilievdb45a4b2016-11-08 14:18:31 -050031#include <SkClipStack.h>
Stan Iliev021693b2016-10-17 16:26:15 -040032#include <string.h>
33
34
35using namespace android;
36using namespace android::uirenderer;
37using namespace android::uirenderer::renderthread;
38using namespace android::uirenderer::skiapipeline;
39
Stan Iliev021693b2016-10-17 16:26:15 -040040TEST(RenderNodeDrawable, create) {
41 auto rootNode = TestUtils::createNode(0, 0, 200, 400,
42 [](RenderProperties& props, Canvas& canvas) {
43 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
44 });
45
46 auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
47 SkLiteRecorder canvas;
48 canvas.reset(skLiteDL.get());
49 canvas.translate(100, 100);
50 RenderNodeDrawable drawable(rootNode.get(), &canvas);
51
52 ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
53 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
54 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
55}
56
Stan Ilievdb45a4b2016-11-08 14:18:31 -050057namespace {
58
Stan Iliev2f06e8a2016-11-02 15:29:03 -040059static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
60 SkPaint paint;
61 // order put in blue channel, transparent so overlapped content doesn't get rejected
62 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
63 canvas->drawRect(0, 0, 100, 100, paint);
Stan Iliev021693b2016-10-17 16:26:15 -040064}
65
Stan Iliev2f06e8a2016-11-02 15:29:03 -040066static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
67 auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
68 [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
69 drawOrderedRect(&canvas, expectedDrawOrder);
70 props.setTranslationZ(z);
71 });
72 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
73}
Stan Iliev021693b2016-10-17 16:26:15 -040074
Stan Ilievdb45a4b2016-11-08 14:18:31 -050075static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder,
76 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
77 auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
78 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
79 drawOrderedRect(&canvas, expectedDrawOrder);
80 if (setup) {
81 setup(props, canvas);
Stan Iliev2f06e8a2016-11-02 15:29:03 -040082 }
Stan Ilievdb45a4b2016-11-08 14:18:31 -050083 });
84 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
85}
86
87class ZReorderCanvas : public SkCanvas {
88public:
89 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
90 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
91 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
92 EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
93 }
94 int getIndex() { return mIndex; }
95protected:
96 int mIndex = 0;
97};
98
99} // end anonymous namespace
100
101TEST(RenderNodeDrawable, zReorder) {
Stan Iliev021693b2016-10-17 16:26:15 -0400102
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400103 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
104 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
Stan Iliev347691f2016-12-01 12:25:07 -0500105 canvas.insertReorderBarrier(true);
106 canvas.insertReorderBarrier(false);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400107 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
108 drawOrderedRect(&canvas, 1);
109 canvas.insertReorderBarrier(true);
110 drawOrderedNode(&canvas, 6, 2.0f);
111 drawOrderedRect(&canvas, 3);
112 drawOrderedNode(&canvas, 4, 0.0f);
113 drawOrderedRect(&canvas, 5);
114 drawOrderedNode(&canvas, 2, -2.0f);
115 drawOrderedNode(&canvas, 7, 2.0f);
116 canvas.insertReorderBarrier(false);
117 drawOrderedRect(&canvas, 8);
118 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
Stan Iliev88e08912016-11-22 18:19:29 -0500119 canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op
120 drawOrderedRect(&canvas, 11);
121 drawOrderedNode(&canvas, 10, -1.0f);
122 canvas.insertReorderBarrier(false);
123 canvas.insertReorderBarrier(true); //test with two empty reorder sections
124 canvas.insertReorderBarrier(true);
125 canvas.insertReorderBarrier(false);
126 drawOrderedRect(&canvas, 12);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400127 });
Stan Iliev021693b2016-10-17 16:26:15 -0400128
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400129 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
130 ZReorderCanvas canvas(100, 100);
131 RenderNodeDrawable drawable(parent.get(), &canvas, false);
132 canvas.drawDrawable(&drawable);
Stan Iliev88e08912016-11-22 18:19:29 -0500133 EXPECT_EQ(13, canvas.getIndex());
Stan Iliev021693b2016-10-17 16:26:15 -0400134}
135
136TEST(RenderNodeDrawable, composeOnLayer)
137{
138 auto surface = SkSurface::MakeRasterN32Premul(1, 1);
139 SkCanvas& canvas = *surface->getCanvas();
140 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
141 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
142
Stan Iliev500a0c32016-10-26 10:30:09 -0400143 auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
Stan Iliev021693b2016-10-17 16:26:15 -0400144 [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
145 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
146 });
147
148 //attach a layer to the render node
149 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
150 auto canvas2 = surfaceLayer->getCanvas();
151 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
Stan Iliev500a0c32016-10-26 10:30:09 -0400152 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400153
154 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
155 canvas.drawDrawable(&drawable1);
156 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
157
158 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
159 canvas.drawDrawable(&drawable2);
160 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
161
162 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
163 canvas.drawDrawable(&drawable3);
164 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
165
Stan Iliev500a0c32016-10-26 10:30:09 -0400166 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400167}
168
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500169namespace {
170class ContextFactory : public IContextFactory {
171public:
172 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
173 return new AnimationContext(clock);
174 }
175};
Stan Iliev021693b2016-10-17 16:26:15 -0400176
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500177inline SkRect getBounds(const SkCanvas* canvas) {
178 SkClipStack::BoundsType boundType;
179 SkRect clipBounds;
180 canvas->getClipStack()->getBounds(&clipBounds, &boundType);
181 return clipBounds;
182}
183inline SkRect getLocalBounds(const SkCanvas* canvas) {
184 SkMatrix invertedTotalMatrix;
185 EXPECT_TRUE(canvas->getTotalMatrix().invert(&invertedTotalMatrix));
186 SkRect outlineInDeviceCoord = getBounds(canvas);
187 SkRect outlineInLocalCoord;
188 invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord);
189 return outlineInLocalCoord;
190}
191} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400192
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500193RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
194 static const int SCROLL_X = 5;
195 static const int SCROLL_Y = 10;
196 class ProjectionTestCanvas : public SkCanvas {
197 public:
198 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
199 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
200 const int index = mIndex++;
201 SkMatrix expectedMatrix;;
202 switch (index) {
203 case 0: //this is node "B"
204 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
205 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
206 expectedMatrix.reset();
207 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), getBounds(this));
208 break;
209 case 1: //this is node "P"
210 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
211 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
212 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
213 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), getLocalBounds(this));
214 break;
215 case 2: //this is node "C"
216 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
217 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
218 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
219 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), getBounds(this));
220 break;
221 default:
222 ADD_FAILURE();
223 }
224 EXPECT_EQ(expectedMatrix, getTotalMatrix());
225 }
Stan Iliev021693b2016-10-17 16:26:15 -0400226
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500227 int getIndex() { return mIndex; }
228 protected:
229 int mIndex = 0;
230 };
Stan Iliev021693b2016-10-17 16:26:15 -0400231
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500232 /**
233 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
234 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
235 * draw, but because it is projected backwards, it's drawn in between B and C.
236 *
237 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
238 * (which isn't affected by scroll).
239 */
240 auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
241 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
242 properties.setProjectionReceiver(true);
243 // scroll doesn't apply to background, so undone via translationX/Y
244 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
245 properties.setTranslationX(SCROLL_X);
246 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400247
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500248 SkPaint paint;
249 paint.setColor(SK_ColorWHITE);
250 canvas.drawRect(0, 0, 100, 100, paint);
251 }, "B");
252
253 auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
254 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
255 properties.setProjectBackwards(true);
256 properties.setClipToBounds(false);
257 SkPaint paint;
258 paint.setColor(SK_ColorDKGRAY);
259 canvas.drawRect(-10, -10, 60, 60, paint);
260 }, "P");
261 auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
262 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
263 SkPaint paint;
264 paint.setColor(SK_ColorBLUE);
265 canvas.drawRect(0, 0, 100, 50, paint);
266 canvas.drawRenderNode(projectingRipple.get());
267 }, "C");
268 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
269 [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
270 // Set a rect outline for the projecting ripple to be masked against.
271 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
272
273 canvas.save(SaveFlags::MatrixClip);
274 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
275 canvas.drawRenderNode(receiverBackground.get());
276 canvas.drawRenderNode(child.get());
277 canvas.restore();
278 }, "A");
279 ContextFactory contextFactory;
280 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
281 renderThread, false, parent.get(), &contextFactory));
282 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
283 DamageAccumulator damageAccumulator;
284 info.damageAccumulator = &damageAccumulator;
285 info.observer = nullptr;
286 parent->prepareTree(info);
287
288 //parent(A) -> (receiverBackground, child)
289 //child(C) -> (rect[0, 0, 100, 50], projectingRipple)
290 //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
291 //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
292
293 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
294 ProjectionTestCanvas canvas(100, 100);
295 RenderNodeDrawable drawable(parent.get(), &canvas, true);
296 canvas.drawDrawable(&drawable);
297 EXPECT_EQ(3, canvas.getIndex());
298}
299
300RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
301 /* R is backward projected on B and C is a layer.
302 A
303 / \
304 B C
305 |
306 R
307 */
308 static const int SCROLL_X = 5;
309 static const int SCROLL_Y = 10;
310 static const int CANVAS_WIDTH = 400;
311 static const int CANVAS_HEIGHT = 400;
312 static const int LAYER_WIDTH = 200;
313 static const int LAYER_HEIGHT = 200;
314 class ProjectionTestCanvas : public SkCanvas {
315 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500316 ProjectionTestCanvas(int* drawCounter)
317 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
318 , mDrawCounter(drawCounter)
319 {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500320 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
321 const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500322 EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500323 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), getBounds(this));
324 }
325 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500326 EXPECT_EQ(1, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500327 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
328 }
329 void onDrawOval(const SkRect&, const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500330 EXPECT_EQ(2, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500331 SkMatrix expectedMatrix;
332 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
333 EXPECT_EQ(expectedMatrix, getTotalMatrix());
334 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), getLocalBounds(this));
335 }
Mike Reed6acfe162016-11-18 17:21:09 -0500336 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500337 };
338
339 class ProjectionLayer : public SkSurface_Base {
340 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500341 ProjectionLayer(int* drawCounter)
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500342 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
Mike Reed6acfe162016-11-18 17:21:09 -0500343 , mDrawCounter(drawCounter) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500344 }
345 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500346 EXPECT_EQ(3, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500347 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
Mike Reed6acfe162016-11-18 17:21:09 -0500348 300 - SCROLL_Y), getBounds(this->getCanvas()));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500349 }
350 SkCanvas* onNewCanvas() override {
Mike Reed6acfe162016-11-18 17:21:09 -0500351 return new ProjectionTestCanvas(mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500352 }
353 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
354 return sk_sp<SkSurface>();
355 }
356 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
357 return sk_sp<SkImage>();
358 }
359 void onCopyOnWrite(ContentChangeMode) override {}
Mike Reed6acfe162016-11-18 17:21:09 -0500360 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500361 };
362
363 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
364 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
365 properties.setProjectionReceiver(true);
366 // scroll doesn't apply to background, so undone via translationX/Y
367 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
368 properties.setTranslationX(SCROLL_X);
369 properties.setTranslationY(SCROLL_Y);
370
371 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
372 }, "B"); //B
373 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
374 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
375 properties.setProjectBackwards(true);
376 properties.setClipToBounds(false);
377 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
378 }, "R"); //R
379 auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
380 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
381 canvas.drawRenderNode(projectingRipple.get());
382 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
383 }, "C"); //C
384 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
385 [&receiverBackground, &child](RenderProperties& properties,
386 SkiaRecordingCanvas& canvas) {
387 // Set a rect outline for the projecting ripple to be masked against.
388 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
389 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
390 canvas.drawRenderNode(receiverBackground.get());
391 canvas.drawRenderNode(child.get());
392 }, "A"); //A
393
394 //prepareTree is required to find, which receivers have backward projected nodes
395 ContextFactory contextFactory;
396 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
397 renderThread, false, parent.get(), &contextFactory));
398 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
399 DamageAccumulator damageAccumulator;
400 info.damageAccumulator = &damageAccumulator;
401 info.observer = nullptr;
402 parent->prepareTree(info);
403
Mike Reed6acfe162016-11-18 17:21:09 -0500404 int drawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500405 //set a layer after prepareTree to avoid layer logic there
406 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500407 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500408 child->setLayerSurface(surfaceLayer1);
409 Matrix4 windowTransform;
410 windowTransform.loadTranslate(100, 100, 0);
411 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
412
413 LayerUpdateQueue layerUpdateQueue;
414 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
415 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
416 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
Mike Reed6acfe162016-11-18 17:21:09 -0500417 EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500418
Mike Reed6acfe162016-11-18 17:21:09 -0500419 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
420 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
421 EXPECT_EQ(4, drawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500422
423 // clean up layer pointer, so we can safely destruct RenderNode
424 child->setLayerSurface(nullptr);
425}
426
427RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
428 /* R is backward projected on B.
429 A
430 / \
431 B C
432 |
433 R
434 */
435 static const int SCROLL_X = 500000;
436 static const int SCROLL_Y = 0;
437 static const int CANVAS_WIDTH = 400;
438 static const int CANVAS_HEIGHT = 400;
439 class ProjectionChildScrollTestCanvas : public SkCanvas {
440 public:
441 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
442 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
443 EXPECT_EQ(0, mIndex++);
444 EXPECT_TRUE(getTotalMatrix().isIdentity());
445 }
446 void onDrawOval(const SkRect&, const SkPaint&) override {
447 EXPECT_EQ(1, mIndex++);
448 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
449 EXPECT_TRUE(getTotalMatrix().isIdentity());
450 }
451 int mIndex = 0;
452 };
453
454 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
455 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
456 properties.setProjectionReceiver(true);
457 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
458 }, "B"); //B
459 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
460 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
461 // scroll doesn't apply to background, so undone via translationX/Y
462 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
463 properties.setTranslationX(SCROLL_X);
464 properties.setTranslationY(SCROLL_Y);
465 properties.setProjectBackwards(true);
466 properties.setClipToBounds(false);
467 canvas.drawOval(0, 0, 200, 200, SkPaint());
468 }, "R"); //R
469 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
470 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
471 // Record time clip will be ignored by projectee
Mike Reed6e49c9f2016-12-02 15:36:59 -0500472 canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500473
474 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
475 canvas.drawRenderNode(projectingRipple.get());
476 }, "C"); //C
477 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
478 [&receiverBackground, &child](RenderProperties& properties,
479 SkiaRecordingCanvas& canvas) {
480 canvas.drawRenderNode(receiverBackground.get());
481 canvas.drawRenderNode(child.get());
482 }, "A"); //A
483
484 //prepareTree is required to find, which receivers have backward projected nodes
485 ContextFactory contextFactory;
486 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
487 renderThread, false, parent.get(), &contextFactory));
488 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
489 DamageAccumulator damageAccumulator;
490 info.damageAccumulator = &damageAccumulator;
491 info.observer = nullptr;
492 parent->prepareTree(info);
493
Mike Reed6acfe162016-11-18 17:21:09 -0500494 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500495 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
496 canvas->drawDrawable(&drawable);
497 EXPECT_EQ(2, canvas->mIndex);
498}
499
500namespace {
501static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
502{
503 ContextFactory contextFactory;
504 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
505 renderThread, false, renderNode.get(), &contextFactory));
506 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
507 DamageAccumulator damageAccumulator;
508 info.damageAccumulator = &damageAccumulator;
509 info.observer = nullptr;
510 renderNode->prepareTree(info);
511
512 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
513 ZReorderCanvas canvas(100, 100);
514 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
515 canvas.drawDrawable(&drawable);
516 return canvas.getIndex();
517}
518}
519
520RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
521 /* R is backward projected on B
522 A
523 / \
524 B C
525 |
526 R
527 */
528 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
529 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
530 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
531 props.setProjectionReceiver(true);
532 } ); //nodeB
533 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
534 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
535 props.setProjectBackwards(true);
536 props.setClipToBounds(false);
537 } ); //nodeR
538 } ); //nodeC
539 }); //nodeA
540 EXPECT_EQ(3, drawNode(renderThread, nodeA));
541}
542
543RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
544 /* R is backward projected on E
545 A
546 / | \
547 / | \
548 B C E
549 |
550 R
551 */
552 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
553 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
554 drawOrderedNode(&canvas, 0, nullptr); //nodeB
555 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
556 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
557 props.setProjectBackwards(true);
558 props.setClipToBounds(false);
559 } ); //nodeR
560 } ); //nodeC
561 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
562 props.setProjectionReceiver(true);
563 } ); //nodeE
564 }); //nodeA
565 EXPECT_EQ(4, drawNode(renderThread, nodeA));
566}
567
568RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
569 /* R is backward projected without receiver
570 A
571 / \
572 B C
573 |
574 R
575 */
576 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
577 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
578 drawOrderedNode(&canvas, 0, nullptr); //nodeB
579 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
580 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
581 //not having a projection receiver is an undefined behavior
582 props.setProjectBackwards(true);
583 props.setClipToBounds(false);
584 } ); //nodeR
585 } ); //nodeC
586 }); //nodeA
587 EXPECT_EQ(2, drawNode(renderThread, nodeA));
588}
589
590RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
591 /* R is backward projected on C
592 A
593 / \
594 B C
595 |
596 R
597 */
598 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
599 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
600 drawOrderedNode(&canvas, 0, nullptr); //nodeB
601 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
602 props.setProjectionReceiver(true);
603 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
604 props.setProjectBackwards(true);
605 props.setClipToBounds(false);
606 } ); //nodeR
607 } ); //nodeC
608 }); //nodeA
609 EXPECT_EQ(3, drawNode(renderThread, nodeA));
610}
611
612RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
613 /* R is backward projected on R
614 A
615 / \
616 B C
617 |
618 R
619 */
620 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
621 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
622 drawOrderedNode(&canvas, 0, nullptr); //nodeB
623 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
624 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
625 //having a node that is projected on itself is an undefined/unexpected behavior
626 props.setProjectionReceiver(true);
627 props.setProjectBackwards(true);
628 props.setClipToBounds(false);
629 } ); //nodeR
630 } ); //nodeC
631 }); //nodeA
632 EXPECT_EQ(2, drawNode(renderThread, nodeA));
633}
634
635//Note: the outcome for this test is different in HWUI
636RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
637 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
638 A
639 /|\
640 / | \
641 B C R
642 */
643 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
644 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
645 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
646 props.setProjectionReceiver(true);
647 } ); //nodeB
648 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
649 } ); //nodeC
650 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
651 props.setProjectBackwards(true);
652 props.setClipToBounds(false);
653 } ); //nodeR
654 }); //nodeA
655 EXPECT_EQ(2, drawNode(renderThread, nodeA));
656}
657
658RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
659 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
660 A
661 |
662 G
663 /|\
664 / | \
665 B C R
666 */
667 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
668 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
669 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
670 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
671 props.setProjectionReceiver(true);
672 } ); //nodeB
673 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
674 } ); //nodeC
675 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
676 props.setProjectBackwards(true);
677 props.setClipToBounds(false);
678 } ); //nodeR
679 } ); //nodeG
680 }); //nodeA
681 EXPECT_EQ(3, drawNode(renderThread, nodeA));
682}
683
684RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
685 /* R is backward projected on B
686 A
687 |
688 B
689 |
690 C
691 |
692 R
693 */
694 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
695 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
696 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
697 props.setProjectionReceiver(true);
698 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
699 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
700 props.setProjectBackwards(true);
701 props.setClipToBounds(false);
702 } ); //nodeR
703 } ); //nodeC
704 } ); //nodeB
705 }); //nodeA
706 EXPECT_EQ(3, drawNode(renderThread, nodeA));
707}
708
709RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
710 /* B and G are receivables, R is backward projected
711 A
712 / \
713 B C
714 / \
715 G R
716 */
717 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
718 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
719 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
720 props.setProjectionReceiver(true);
721 } ); //nodeB
722 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
723 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
724 props.setProjectionReceiver(true);
725 } ); //nodeG
726 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
727 props.setProjectBackwards(true);
728 props.setClipToBounds(false);
729 } ); //nodeR
730 } ); //nodeC
731 }); //nodeA
732 EXPECT_EQ(4, drawNode(renderThread, nodeA));
733}
734
735RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
736 /* B and G are receivables, G is backward projected
737 A
738 / \
739 B C
740 / \
741 G R
742 */
743 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
744 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
745 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
746 props.setProjectionReceiver(true);
747 } ); //nodeB
748 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
749 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
750 props.setProjectionReceiver(true);
751 props.setProjectBackwards(true);
752 props.setClipToBounds(false);
753 } ); //nodeG
754 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
755 } ); //nodeR
756 } ); //nodeC
757 }); //nodeA
758 EXPECT_EQ(4, drawNode(renderThread, nodeA));
759}
760
761RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
762 /* B and G are receivables, R is backward projected
763 A
764 / \
765 B C
766 / \
767 G D
768 |
769 R
770 */
771 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
772 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
773 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
774 props.setProjectionReceiver(true);
775 } ); //nodeB
776 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
777 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
778 props.setProjectionReceiver(true);
779 } ); //nodeG
780 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
781 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
782 props.setProjectBackwards(true);
783 props.setClipToBounds(false);
784 } ); //nodeR
785 } ); //nodeD
786 } ); //nodeC
787 }); //nodeA
788 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400789}