blob: fafce86740eadc587b8d582c0d85fb4832605915 [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) {
105 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
106 drawOrderedRect(&canvas, 1);
107 canvas.insertReorderBarrier(true);
108 drawOrderedNode(&canvas, 6, 2.0f);
109 drawOrderedRect(&canvas, 3);
110 drawOrderedNode(&canvas, 4, 0.0f);
111 drawOrderedRect(&canvas, 5);
112 drawOrderedNode(&canvas, 2, -2.0f);
113 drawOrderedNode(&canvas, 7, 2.0f);
114 canvas.insertReorderBarrier(false);
115 drawOrderedRect(&canvas, 8);
116 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
Stan Iliev88e08912016-11-22 18:19:29 -0500117 canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op
118 drawOrderedRect(&canvas, 11);
119 drawOrderedNode(&canvas, 10, -1.0f);
120 canvas.insertReorderBarrier(false);
121 canvas.insertReorderBarrier(true); //test with two empty reorder sections
122 canvas.insertReorderBarrier(true);
123 canvas.insertReorderBarrier(false);
124 drawOrderedRect(&canvas, 12);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400125 });
Stan Iliev021693b2016-10-17 16:26:15 -0400126
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400127 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
128 ZReorderCanvas canvas(100, 100);
129 RenderNodeDrawable drawable(parent.get(), &canvas, false);
130 canvas.drawDrawable(&drawable);
Stan Iliev88e08912016-11-22 18:19:29 -0500131 EXPECT_EQ(13, canvas.getIndex());
Stan Iliev021693b2016-10-17 16:26:15 -0400132}
133
134TEST(RenderNodeDrawable, composeOnLayer)
135{
136 auto surface = SkSurface::MakeRasterN32Premul(1, 1);
137 SkCanvas& canvas = *surface->getCanvas();
138 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
139 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
140
Stan Iliev500a0c32016-10-26 10:30:09 -0400141 auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
Stan Iliev021693b2016-10-17 16:26:15 -0400142 [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
143 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
144 });
145
146 //attach a layer to the render node
147 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
148 auto canvas2 = surfaceLayer->getCanvas();
149 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
Stan Iliev500a0c32016-10-26 10:30:09 -0400150 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400151
152 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
153 canvas.drawDrawable(&drawable1);
154 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
155
156 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
157 canvas.drawDrawable(&drawable2);
158 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
159
160 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
161 canvas.drawDrawable(&drawable3);
162 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
163
Stan Iliev500a0c32016-10-26 10:30:09 -0400164 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400165}
166
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500167namespace {
168class ContextFactory : public IContextFactory {
169public:
170 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
171 return new AnimationContext(clock);
172 }
173};
Stan Iliev021693b2016-10-17 16:26:15 -0400174
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500175inline SkRect getBounds(const SkCanvas* canvas) {
176 SkClipStack::BoundsType boundType;
177 SkRect clipBounds;
178 canvas->getClipStack()->getBounds(&clipBounds, &boundType);
179 return clipBounds;
180}
181inline SkRect getLocalBounds(const SkCanvas* canvas) {
182 SkMatrix invertedTotalMatrix;
183 EXPECT_TRUE(canvas->getTotalMatrix().invert(&invertedTotalMatrix));
184 SkRect outlineInDeviceCoord = getBounds(canvas);
185 SkRect outlineInLocalCoord;
186 invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord);
187 return outlineInLocalCoord;
188}
189} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400190
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500191RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
192 static const int SCROLL_X = 5;
193 static const int SCROLL_Y = 10;
194 class ProjectionTestCanvas : public SkCanvas {
195 public:
196 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
197 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
198 const int index = mIndex++;
199 SkMatrix expectedMatrix;;
200 switch (index) {
201 case 0: //this is node "B"
202 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
203 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
204 expectedMatrix.reset();
205 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), getBounds(this));
206 break;
207 case 1: //this is node "P"
208 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
209 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
210 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
211 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), getLocalBounds(this));
212 break;
213 case 2: //this is node "C"
214 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
215 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
216 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
217 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), getBounds(this));
218 break;
219 default:
220 ADD_FAILURE();
221 }
222 EXPECT_EQ(expectedMatrix, getTotalMatrix());
223 }
Stan Iliev021693b2016-10-17 16:26:15 -0400224
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500225 int getIndex() { return mIndex; }
226 protected:
227 int mIndex = 0;
228 };
Stan Iliev021693b2016-10-17 16:26:15 -0400229
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500230 /**
231 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
232 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
233 * draw, but because it is projected backwards, it's drawn in between B and C.
234 *
235 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
236 * (which isn't affected by scroll).
237 */
238 auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
239 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
240 properties.setProjectionReceiver(true);
241 // scroll doesn't apply to background, so undone via translationX/Y
242 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
243 properties.setTranslationX(SCROLL_X);
244 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400245
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500246 SkPaint paint;
247 paint.setColor(SK_ColorWHITE);
248 canvas.drawRect(0, 0, 100, 100, paint);
249 }, "B");
250
251 auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
252 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
253 properties.setProjectBackwards(true);
254 properties.setClipToBounds(false);
255 SkPaint paint;
256 paint.setColor(SK_ColorDKGRAY);
257 canvas.drawRect(-10, -10, 60, 60, paint);
258 }, "P");
259 auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
260 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
261 SkPaint paint;
262 paint.setColor(SK_ColorBLUE);
263 canvas.drawRect(0, 0, 100, 50, paint);
264 canvas.drawRenderNode(projectingRipple.get());
265 }, "C");
266 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
267 [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
268 // Set a rect outline for the projecting ripple to be masked against.
269 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
270
271 canvas.save(SaveFlags::MatrixClip);
272 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
273 canvas.drawRenderNode(receiverBackground.get());
274 canvas.drawRenderNode(child.get());
275 canvas.restore();
276 }, "A");
277 ContextFactory contextFactory;
278 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
279 renderThread, false, parent.get(), &contextFactory));
280 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
281 DamageAccumulator damageAccumulator;
282 info.damageAccumulator = &damageAccumulator;
283 info.observer = nullptr;
284 parent->prepareTree(info);
285
286 //parent(A) -> (receiverBackground, child)
287 //child(C) -> (rect[0, 0, 100, 50], projectingRipple)
288 //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
289 //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
290
291 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
292 ProjectionTestCanvas canvas(100, 100);
293 RenderNodeDrawable drawable(parent.get(), &canvas, true);
294 canvas.drawDrawable(&drawable);
295 EXPECT_EQ(3, canvas.getIndex());
296}
297
298RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
299 /* R is backward projected on B and C is a layer.
300 A
301 / \
302 B C
303 |
304 R
305 */
306 static const int SCROLL_X = 5;
307 static const int SCROLL_Y = 10;
308 static const int CANVAS_WIDTH = 400;
309 static const int CANVAS_HEIGHT = 400;
310 static const int LAYER_WIDTH = 200;
311 static const int LAYER_HEIGHT = 200;
312 class ProjectionTestCanvas : public SkCanvas {
313 public:
314 ProjectionTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
315 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
316 const SkPaint&) override {
317 EXPECT_EQ(0, mIndex++); //part of painting the layer
318 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), getBounds(this));
319 }
320 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
321 EXPECT_EQ(1, mIndex++);
322 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
323 }
324 void onDrawOval(const SkRect&, const SkPaint&) override {
325 EXPECT_EQ(2, mIndex++);
326 SkMatrix expectedMatrix;
327 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
328 EXPECT_EQ(expectedMatrix, getTotalMatrix());
329 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), getLocalBounds(this));
330 }
331 int mIndex = 0;
332 };
333
334 class ProjectionLayer : public SkSurface_Base {
335 public:
336 ProjectionLayer(ProjectionTestCanvas *canvas)
337 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
338 , mCanvas(canvas) {
339 }
340 void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
341 EXPECT_EQ(3, mCanvas->mIndex++);
342 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
343 300 - SCROLL_Y), getBounds(mCanvas));
344 }
345 SkCanvas* onNewCanvas() override {
346 mCanvas->ref();
347 return mCanvas;
348 }
349 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
350 return sk_sp<SkSurface>();
351 }
352 sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
353 return sk_sp<SkImage>();
354 }
355 void onCopyOnWrite(ContentChangeMode) override {}
356 ProjectionTestCanvas* mCanvas;
357 };
358
359 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
360 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
361 properties.setProjectionReceiver(true);
362 // scroll doesn't apply to background, so undone via translationX/Y
363 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
364 properties.setTranslationX(SCROLL_X);
365 properties.setTranslationY(SCROLL_Y);
366
367 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
368 }, "B"); //B
369 auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
370 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
371 properties.setProjectBackwards(true);
372 properties.setClipToBounds(false);
373 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
374 }, "R"); //R
375 auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
376 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
377 canvas.drawRenderNode(projectingRipple.get());
378 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
379 }, "C"); //C
380 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
381 [&receiverBackground, &child](RenderProperties& properties,
382 SkiaRecordingCanvas& canvas) {
383 // Set a rect outline for the projecting ripple to be masked against.
384 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
385 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
386 canvas.drawRenderNode(receiverBackground.get());
387 canvas.drawRenderNode(child.get());
388 }, "A"); //A
389
390 //prepareTree is required to find, which receivers have backward projected nodes
391 ContextFactory contextFactory;
392 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
393 renderThread, false, parent.get(), &contextFactory));
394 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
395 DamageAccumulator damageAccumulator;
396 info.damageAccumulator = &damageAccumulator;
397 info.observer = nullptr;
398 parent->prepareTree(info);
399
400 sk_sp<ProjectionTestCanvas> canvas(new ProjectionTestCanvas());
401 //set a layer after prepareTree to avoid layer logic there
402 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
403 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(canvas.get()));
404 child->setLayerSurface(surfaceLayer1);
405 Matrix4 windowTransform;
406 windowTransform.loadTranslate(100, 100, 0);
407 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
408
409 LayerUpdateQueue layerUpdateQueue;
410 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
411 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
412 SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
413 EXPECT_EQ(1, canvas->mIndex); //assert index 0 is drawn on the layer
414
415 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
416 canvas->drawDrawable(&drawable);
417 EXPECT_EQ(4, canvas->mIndex);
418
419 // clean up layer pointer, so we can safely destruct RenderNode
420 child->setLayerSurface(nullptr);
421}
422
423RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
424 /* R is backward projected on B.
425 A
426 / \
427 B C
428 |
429 R
430 */
431 static const int SCROLL_X = 500000;
432 static const int SCROLL_Y = 0;
433 static const int CANVAS_WIDTH = 400;
434 static const int CANVAS_HEIGHT = 400;
435 class ProjectionChildScrollTestCanvas : public SkCanvas {
436 public:
437 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
438 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
439 EXPECT_EQ(0, mIndex++);
440 EXPECT_TRUE(getTotalMatrix().isIdentity());
441 }
442 void onDrawOval(const SkRect&, const SkPaint&) override {
443 EXPECT_EQ(1, mIndex++);
444 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
445 EXPECT_TRUE(getTotalMatrix().isIdentity());
446 }
447 int mIndex = 0;
448 };
449
450 auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
451 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
452 properties.setProjectionReceiver(true);
453 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
454 }, "B"); //B
455 auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
456 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
457 // scroll doesn't apply to background, so undone via translationX/Y
458 // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
459 properties.setTranslationX(SCROLL_X);
460 properties.setTranslationY(SCROLL_Y);
461 properties.setProjectBackwards(true);
462 properties.setClipToBounds(false);
463 canvas.drawOval(0, 0, 200, 200, SkPaint());
464 }, "R"); //R
465 auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
466 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
467 // Record time clip will be ignored by projectee
468 canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
469
470 canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
471 canvas.drawRenderNode(projectingRipple.get());
472 }, "C"); //C
473 auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
474 [&receiverBackground, &child](RenderProperties& properties,
475 SkiaRecordingCanvas& canvas) {
476 canvas.drawRenderNode(receiverBackground.get());
477 canvas.drawRenderNode(child.get());
478 }, "A"); //A
479
480 //prepareTree is required to find, which receivers have backward projected nodes
481 ContextFactory contextFactory;
482 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
483 renderThread, false, parent.get(), &contextFactory));
484 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
485 DamageAccumulator damageAccumulator;
486 info.damageAccumulator = &damageAccumulator;
487 info.observer = nullptr;
488 parent->prepareTree(info);
489
490 sk_sp<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
491 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
492 canvas->drawDrawable(&drawable);
493 EXPECT_EQ(2, canvas->mIndex);
494}
495
496namespace {
497static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
498{
499 ContextFactory contextFactory;
500 std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
501 renderThread, false, renderNode.get(), &contextFactory));
502 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
503 DamageAccumulator damageAccumulator;
504 info.damageAccumulator = &damageAccumulator;
505 info.observer = nullptr;
506 renderNode->prepareTree(info);
507
508 //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
509 ZReorderCanvas canvas(100, 100);
510 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
511 canvas.drawDrawable(&drawable);
512 return canvas.getIndex();
513}
514}
515
516RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
517 /* R is backward projected on B
518 A
519 / \
520 B C
521 |
522 R
523 */
524 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
525 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
526 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
527 props.setProjectionReceiver(true);
528 } ); //nodeB
529 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
530 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
531 props.setProjectBackwards(true);
532 props.setClipToBounds(false);
533 } ); //nodeR
534 } ); //nodeC
535 }); //nodeA
536 EXPECT_EQ(3, drawNode(renderThread, nodeA));
537}
538
539RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
540 /* R is backward projected on E
541 A
542 / | \
543 / | \
544 B C E
545 |
546 R
547 */
548 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
549 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
550 drawOrderedNode(&canvas, 0, nullptr); //nodeB
551 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
552 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
553 props.setProjectBackwards(true);
554 props.setClipToBounds(false);
555 } ); //nodeR
556 } ); //nodeC
557 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
558 props.setProjectionReceiver(true);
559 } ); //nodeE
560 }); //nodeA
561 EXPECT_EQ(4, drawNode(renderThread, nodeA));
562}
563
564RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
565 /* R is backward projected without receiver
566 A
567 / \
568 B C
569 |
570 R
571 */
572 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
573 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
574 drawOrderedNode(&canvas, 0, nullptr); //nodeB
575 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
576 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
577 //not having a projection receiver is an undefined behavior
578 props.setProjectBackwards(true);
579 props.setClipToBounds(false);
580 } ); //nodeR
581 } ); //nodeC
582 }); //nodeA
583 EXPECT_EQ(2, drawNode(renderThread, nodeA));
584}
585
586RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
587 /* R is backward projected on C
588 A
589 / \
590 B C
591 |
592 R
593 */
594 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
595 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
596 drawOrderedNode(&canvas, 0, nullptr); //nodeB
597 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
598 props.setProjectionReceiver(true);
599 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
600 props.setProjectBackwards(true);
601 props.setClipToBounds(false);
602 } ); //nodeR
603 } ); //nodeC
604 }); //nodeA
605 EXPECT_EQ(3, drawNode(renderThread, nodeA));
606}
607
608RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
609 /* R is backward projected on R
610 A
611 / \
612 B C
613 |
614 R
615 */
616 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
617 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
618 drawOrderedNode(&canvas, 0, nullptr); //nodeB
619 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
620 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
621 //having a node that is projected on itself is an undefined/unexpected behavior
622 props.setProjectionReceiver(true);
623 props.setProjectBackwards(true);
624 props.setClipToBounds(false);
625 } ); //nodeR
626 } ); //nodeC
627 }); //nodeA
628 EXPECT_EQ(2, drawNode(renderThread, nodeA));
629}
630
631//Note: the outcome for this test is different in HWUI
632RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
633 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
634 A
635 /|\
636 / | \
637 B C R
638 */
639 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
640 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
641 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
642 props.setProjectionReceiver(true);
643 } ); //nodeB
644 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
645 } ); //nodeC
646 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
647 props.setProjectBackwards(true);
648 props.setClipToBounds(false);
649 } ); //nodeR
650 }); //nodeA
651 EXPECT_EQ(2, drawNode(renderThread, nodeA));
652}
653
654RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
655 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
656 A
657 |
658 G
659 /|\
660 / | \
661 B C R
662 */
663 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
664 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
665 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
666 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
667 props.setProjectionReceiver(true);
668 } ); //nodeB
669 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
670 } ); //nodeC
671 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
672 props.setProjectBackwards(true);
673 props.setClipToBounds(false);
674 } ); //nodeR
675 } ); //nodeG
676 }); //nodeA
677 EXPECT_EQ(3, drawNode(renderThread, nodeA));
678}
679
680RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
681 /* R is backward projected on B
682 A
683 |
684 B
685 |
686 C
687 |
688 R
689 */
690 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
691 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
692 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
693 props.setProjectionReceiver(true);
694 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
695 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
696 props.setProjectBackwards(true);
697 props.setClipToBounds(false);
698 } ); //nodeR
699 } ); //nodeC
700 } ); //nodeB
701 }); //nodeA
702 EXPECT_EQ(3, drawNode(renderThread, nodeA));
703}
704
705RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
706 /* B and G are receivables, R is backward projected
707 A
708 / \
709 B C
710 / \
711 G R
712 */
713 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
714 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
715 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
716 props.setProjectionReceiver(true);
717 } ); //nodeB
718 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
719 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
720 props.setProjectionReceiver(true);
721 } ); //nodeG
722 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
723 props.setProjectBackwards(true);
724 props.setClipToBounds(false);
725 } ); //nodeR
726 } ); //nodeC
727 }); //nodeA
728 EXPECT_EQ(4, drawNode(renderThread, nodeA));
729}
730
731RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
732 /* B and G are receivables, G is backward projected
733 A
734 / \
735 B C
736 / \
737 G R
738 */
739 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
740 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
741 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
742 props.setProjectionReceiver(true);
743 } ); //nodeB
744 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
745 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
746 props.setProjectionReceiver(true);
747 props.setProjectBackwards(true);
748 props.setClipToBounds(false);
749 } ); //nodeG
750 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
751 } ); //nodeR
752 } ); //nodeC
753 }); //nodeA
754 EXPECT_EQ(4, drawNode(renderThread, nodeA));
755}
756
757RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
758 /* B and G are receivables, R is backward projected
759 A
760 / \
761 B C
762 / \
763 G D
764 |
765 R
766 */
767 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
768 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
769 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
770 props.setProjectionReceiver(true);
771 } ); //nodeB
772 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
773 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
774 props.setProjectionReceiver(true);
775 } ); //nodeG
776 drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
777 drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
778 props.setProjectBackwards(true);
779 props.setClipToBounds(false);
780 } ); //nodeR
781 } ); //nodeD
782 } ); //nodeC
783 }); //nodeA
784 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400785}