blob: c7f57fee1d5acd556dbf99e926b44acfe0be1f93 [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
Stan Iliev021693b2016-10-17 16:26:15 -040017#include <VectorDrawable.h>
John Reck1bcacfd2017-11-03 10:12:19 -070018#include <gtest/gtest.h>
Stan Iliev021693b2016-10-17 16:26:15 -040019
John Reck1bcacfd2017-11-03 10:12:19 -070020#include <SkClipStack.h>
21#include <SkLiteRecorder.h>
22#include <SkSurface_Base.h>
23#include <string.h>
Stan Iliev021693b2016-10-17 16:26:15 -040024#include "AnimationContext.h"
25#include "DamageAccumulator.h"
John Reck1bcacfd2017-11-03 10:12:19 -070026#include "FatalTestCanvas.h"
Stan Iliev021693b2016-10-17 16:26:15 -040027#include "IContextFactory.h"
John Reck1bcacfd2017-11-03 10:12:19 -070028#include "SkiaCanvas.h"
Stan Iliev021693b2016-10-17 16:26:15 -040029#include "pipeline/skia/SkiaDisplayList.h"
Stan Ilieve9d00122017-09-19 12:07:10 -040030#include "pipeline/skia/SkiaOpenGLPipeline.h"
John Reck1bcacfd2017-11-03 10:12:19 -070031#include "pipeline/skia/SkiaPipeline.h"
Stan Iliev021693b2016-10-17 16:26:15 -040032#include "pipeline/skia/SkiaRecordingCanvas.h"
33#include "renderthread/CanvasContext.h"
34#include "tests/common/TestUtils.h"
Stan Iliev021693b2016-10-17 16:26:15 -040035
36using namespace android;
37using namespace android::uirenderer;
38using namespace android::uirenderer::renderthread;
39using namespace android::uirenderer::skiapipeline;
40
Stan Iliev021693b2016-10-17 16:26:15 -040041TEST(RenderNodeDrawable, create) {
John Reck1bcacfd2017-11-03 10:12:19 -070042 auto rootNode =
43 TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) {
Stan Iliev021693b2016-10-17 16:26:15 -040044 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
45 });
46
Derek Sollenbergerea1fe9b2017-03-01 13:02:43 -050047 SkLiteDL skLiteDL;
Stan Iliev021693b2016-10-17 16:26:15 -040048 SkLiteRecorder canvas;
Derek Sollenbergerea1fe9b2017-03-01 13:02:43 -050049 canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
Stan Iliev021693b2016-10-17 16:26:15 -040050 canvas.translate(100, 100);
51 RenderNodeDrawable drawable(rootNode.get(), &canvas);
52
53 ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
54 ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
55 ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
56}
57
Stan Ilievdb45a4b2016-11-08 14:18:31 -050058namespace {
59
Stan Iliev2f06e8a2016-11-02 15:29:03 -040060static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
61 SkPaint paint;
62 // order put in blue channel, transparent so overlapped content doesn't get rejected
63 paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
64 canvas->drawRect(0, 0, 100, 100, paint);
Stan Iliev021693b2016-10-17 16:26:15 -040065}
66
Stan Iliev2f06e8a2016-11-02 15:29:03 -040067static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
John Reck1bcacfd2017-11-03 10:12:19 -070068 auto node = TestUtils::createSkiaNode(
69 0, 0, 100, 100,
Stan Iliev2f06e8a2016-11-02 15:29:03 -040070 [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -070071 drawOrderedRect(&canvas, expectedDrawOrder);
72 props.setTranslationZ(z);
73 });
74 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
Stan Iliev2f06e8a2016-11-02 15:29:03 -040075}
Stan Iliev021693b2016-10-17 16:26:15 -040076
John Reck1bcacfd2017-11-03 10:12:19 -070077static void drawOrderedNode(
78 Canvas* canvas, uint8_t expectedDrawOrder,
Stan Ilievdb45a4b2016-11-08 14:18:31 -050079 std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
John Reck1bcacfd2017-11-03 10:12:19 -070080 auto node = TestUtils::createSkiaNode(
81 0, 0, 100, 100,
Stan Ilievdb45a4b2016-11-08 14:18:31 -050082 [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -070083 drawOrderedRect(&canvas, expectedDrawOrder);
84 if (setup) {
85 setup(props, canvas);
86 }
87 });
88 canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
Stan Ilievdb45a4b2016-11-08 14:18:31 -050089}
90
91class ZReorderCanvas : public SkCanvas {
92public:
93 ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
94 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
John Reck1bcacfd2017-11-03 10:12:19 -070095 int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
Stan Iliev52771272016-11-17 09:54:38 -050096 EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
Stan Ilievdb45a4b2016-11-08 14:18:31 -050097 }
Stan Iliev52771272016-11-17 09:54:38 -050098 int getIndex() { return mDrawCounter; }
John Reck1bcacfd2017-11-03 10:12:19 -070099
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500100protected:
Stan Iliev52771272016-11-17 09:54:38 -0500101 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500102};
103
John Reck1bcacfd2017-11-03 10:12:19 -0700104} // end anonymous namespace
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500105
106TEST(RenderNodeDrawable, zReorder) {
John Reck1bcacfd2017-11-03 10:12:19 -0700107 auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
108 SkiaRecordingCanvas& canvas) {
Stan Iliev347691f2016-12-01 12:25:07 -0500109 canvas.insertReorderBarrier(true);
110 canvas.insertReorderBarrier(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700111 drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400112 drawOrderedRect(&canvas, 1);
113 canvas.insertReorderBarrier(true);
114 drawOrderedNode(&canvas, 6, 2.0f);
115 drawOrderedRect(&canvas, 3);
116 drawOrderedNode(&canvas, 4, 0.0f);
117 drawOrderedRect(&canvas, 5);
118 drawOrderedNode(&canvas, 2, -2.0f);
119 drawOrderedNode(&canvas, 7, 2.0f);
120 canvas.insertReorderBarrier(false);
121 drawOrderedRect(&canvas, 8);
John Reck1bcacfd2017-11-03 10:12:19 -0700122 drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
123 canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op
Stan Iliev88e08912016-11-22 18:19:29 -0500124 drawOrderedRect(&canvas, 11);
125 drawOrderedNode(&canvas, 10, -1.0f);
126 canvas.insertReorderBarrier(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700127 canvas.insertReorderBarrier(true); // test with two empty reorder sections
Stan Iliev88e08912016-11-22 18:19:29 -0500128 canvas.insertReorderBarrier(true);
129 canvas.insertReorderBarrier(false);
130 drawOrderedRect(&canvas, 12);
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400131 });
Stan Iliev021693b2016-10-17 16:26:15 -0400132
John Reck1bcacfd2017-11-03 10:12:19 -0700133 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Iliev2f06e8a2016-11-02 15:29:03 -0400134 ZReorderCanvas canvas(100, 100);
135 RenderNodeDrawable drawable(parent.get(), &canvas, false);
136 canvas.drawDrawable(&drawable);
Stan Iliev88e08912016-11-22 18:19:29 -0500137 EXPECT_EQ(13, canvas.getIndex());
Stan Iliev021693b2016-10-17 16:26:15 -0400138}
139
John Reck1bcacfd2017-11-03 10:12:19 -0700140TEST(RenderNodeDrawable, composeOnLayer) {
Stan Iliev021693b2016-10-17 16:26:15 -0400141 auto surface = SkSurface::MakeRasterN32Premul(1, 1);
142 SkCanvas& canvas = *surface->getCanvas();
143 canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
144 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
145
John Reck1bcacfd2017-11-03 10:12:19 -0700146 auto rootNode = TestUtils::createSkiaNode(
147 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
148 recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
149 });
Stan Iliev021693b2016-10-17 16:26:15 -0400150
John Reck1bcacfd2017-11-03 10:12:19 -0700151 // attach a layer to the render node
Stan Iliev021693b2016-10-17 16:26:15 -0400152 auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
153 auto canvas2 = surfaceLayer->getCanvas();
154 canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
Stan Iliev500a0c32016-10-26 10:30:09 -0400155 rootNode->setLayerSurface(surfaceLayer);
Stan Iliev021693b2016-10-17 16:26:15 -0400156
157 RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
158 canvas.drawDrawable(&drawable1);
159 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
160
161 RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
162 canvas.drawDrawable(&drawable2);
163 ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
164
165 RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
166 canvas.drawDrawable(&drawable3);
167 ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
168
Stan Iliev500a0c32016-10-26 10:30:09 -0400169 rootNode->setLayerSurface(sk_sp<SkSurface>());
Stan Iliev021693b2016-10-17 16:26:15 -0400170}
171
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500172namespace {
Stan Iliev68885e32016-12-14 11:18:34 -0500173static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
174 SkRect clipBounds;
175 recorder.getClipBounds(&clipBounds);
176 return clipBounds;
177}
178
179static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
180 SkMatrix matrix;
181 recorder.getMatrix(&matrix);
182 return matrix;
183}
184}
185
John Reck1bcacfd2017-11-03 10:12:19 -0700186TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) {
Stan Iliev68885e32016-12-14 11:18:34 -0500187 auto surface = SkSurface::MakeRasterN32Premul(400, 800);
188 SkCanvas& canvas = *surface->getCanvas();
189 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
190 ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
191
John Reck1bcacfd2017-11-03 10:12:19 -0700192 auto rootNode = TestUtils::createSkiaNode(
193 0, 0, 400, 800, [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
194 SkPaint layerPaint;
195 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
196 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
Stan Iliev68885e32016-12-14 11:18:34 -0500197
John Reck1bcacfd2017-11-03 10:12:19 -0700198 // note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
199 recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer);
200 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
201 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
Stan Iliev68885e32016-12-14 11:18:34 -0500202
John Reck1bcacfd2017-11-03 10:12:19 -0700203 recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
204 ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder));
Stan Iliev68885e32016-12-14 11:18:34 -0500205
John Reck1bcacfd2017-11-03 10:12:19 -0700206 recorder.translate(300.0f, 400.0f);
207 EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder));
Stan Iliev68885e32016-12-14 11:18:34 -0500208
John Reck1bcacfd2017-11-03 10:12:19 -0700209 recorder.restore();
210 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
211 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
Stan Iliev68885e32016-12-14 11:18:34 -0500212
John Reck1bcacfd2017-11-03 10:12:19 -0700213 SkPaint paint;
214 paint.setAntiAlias(true);
215 paint.setColor(SK_ColorGREEN);
216 recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
217 });
Stan Iliev68885e32016-12-14 11:18:34 -0500218
219 RenderNodeDrawable drawable(rootNode.get(), &canvas, true);
220 canvas.drawDrawable(&drawable);
221 ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600));
222}
223
224namespace {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500225class ContextFactory : public IContextFactory {
226public:
227 virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
228 return new AnimationContext(clock);
229 }
230};
John Reck1bcacfd2017-11-03 10:12:19 -0700231} // end anonymous namespace
Stan Iliev021693b2016-10-17 16:26:15 -0400232
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500233RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
234 static const int SCROLL_X = 5;
235 static const int SCROLL_Y = 10;
236 class ProjectionTestCanvas : public SkCanvas {
237 public:
238 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
239 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500240 const int index = mDrawCounter++;
John Reck1bcacfd2017-11-03 10:12:19 -0700241 SkMatrix expectedMatrix;
242 ;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500243 switch (index) {
John Reck1bcacfd2017-11-03 10:12:19 -0700244 case 0: // this is node "B"
245 EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
246 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
247 expectedMatrix.reset();
248 EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
249 break;
250 case 1: // this is node "P"
251 EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
252 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
253 expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
254 EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50),
255 TestUtils::getLocalClipBounds(this));
256 break;
257 case 2: // this is node "C"
258 EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
259 EXPECT_EQ(SK_ColorBLUE, paint.getColor());
260 expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
261 EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
262 break;
263 default:
264 ADD_FAILURE();
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500265 }
266 EXPECT_EQ(expectedMatrix, getTotalMatrix());
267 }
Stan Iliev021693b2016-10-17 16:26:15 -0400268
Stan Iliev52771272016-11-17 09:54:38 -0500269 int getIndex() { return mDrawCounter; }
John Reck1bcacfd2017-11-03 10:12:19 -0700270
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500271 protected:
Stan Iliev52771272016-11-17 09:54:38 -0500272 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500273 };
Stan Iliev021693b2016-10-17 16:26:15 -0400274
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500275 /**
276 * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
277 * with a projecting child (P) of its own. P would normally draw between B and C's "background"
278 * draw, but because it is projected backwards, it's drawn in between B and C.
279 *
280 * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
281 * (which isn't affected by scroll).
282 */
John Reck1bcacfd2017-11-03 10:12:19 -0700283 auto receiverBackground = TestUtils::createSkiaNode(
284 0, 0, 100, 100,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500285 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700286 properties.setProjectionReceiver(true);
287 // scroll doesn't apply to background, so undone via translationX/Y
288 // NOTE: translationX/Y only! no other transform properties may be set for a proj
289 // receiver!
290 properties.setTranslationX(SCROLL_X);
291 properties.setTranslationY(SCROLL_Y);
Stan Iliev021693b2016-10-17 16:26:15 -0400292
John Reck1bcacfd2017-11-03 10:12:19 -0700293 SkPaint paint;
294 paint.setColor(SK_ColorWHITE);
295 canvas.drawRect(0, 0, 100, 100, paint);
296 },
297 "B");
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500298
John Reck1bcacfd2017-11-03 10:12:19 -0700299 auto projectingRipple = TestUtils::createSkiaNode(
300 50, 0, 100, 50,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500301 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700302 properties.setProjectBackwards(true);
303 properties.setClipToBounds(false);
304 SkPaint paint;
305 paint.setColor(SK_ColorDKGRAY);
306 canvas.drawRect(-10, -10, 60, 60, paint);
307 },
308 "P");
309 auto child = TestUtils::createSkiaNode(
310 0, 50, 100, 100,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500311 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700312 SkPaint paint;
313 paint.setColor(SK_ColorBLUE);
314 canvas.drawRect(0, 0, 100, 50, paint);
315 canvas.drawRenderNode(projectingRipple.get());
316 },
317 "C");
318 auto parent = TestUtils::createSkiaNode(
319 0, 0, 100, 100,
320 [&receiverBackground, &child](RenderProperties& properties,
321 SkiaRecordingCanvas& canvas) {
322 // Set a rect outline for the projecting ripple to be masked against.
323 properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500324
John Reck1bcacfd2017-11-03 10:12:19 -0700325 canvas.save(SaveFlags::MatrixClip);
326 canvas.translate(-SCROLL_X,
327 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
328 canvas.drawRenderNode(receiverBackground.get());
329 canvas.drawRenderNode(child.get());
330 canvas.restore();
331 },
332 "A");
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500333 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700334 std::unique_ptr<CanvasContext> canvasContext(
335 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500336 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
337 DamageAccumulator damageAccumulator;
338 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500339 parent->prepareTree(info);
340
John Reck1bcacfd2017-11-03 10:12:19 -0700341 // parent(A) -> (receiverBackground, child)
342 // child(C) -> (rect[0, 0, 100, 50], projectingRipple)
343 // projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
344 // receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500345
John Reck1bcacfd2017-11-03 10:12:19 -0700346 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500347 ProjectionTestCanvas canvas(100, 100);
348 RenderNodeDrawable drawable(parent.get(), &canvas, true);
349 canvas.drawDrawable(&drawable);
350 EXPECT_EQ(3, canvas.getIndex());
351}
352
Yuqian Li70910fd2017-11-29 13:38:40 -0500353RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
354 class ProjectionTestCanvas : public SkCanvas {
355 public:
356 ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
357 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
358 mDrawCounter++;
359 }
360
361 int getDrawCounter() { return mDrawCounter; }
362
363 private:
364 int mDrawCounter = 0;
365 };
366
367 auto receiverBackground = TestUtils::createSkiaNode(
368 0, 0, 100, 100,
369 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
370 properties.setProjectionReceiver(true);
371 },
372 "B"); // a receiver with an empty display list
373
374 auto projectingRipple = TestUtils::createSkiaNode(
375 0, 0, 100, 100,
376 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
377 properties.setProjectBackwards(true);
378 properties.setClipToBounds(false);
379 SkPaint paint;
380 canvas.drawRect(0, 0, 100, 100, paint);
381 },
382 "P");
383 auto child = TestUtils::createSkiaNode(
384 0, 0, 100, 100,
385 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
386 SkPaint paint;
387 canvas.drawRect(0, 0, 100, 100, paint);
388 canvas.drawRenderNode(projectingRipple.get());
389 },
390 "C");
391 auto parent = TestUtils::createSkiaNode(
392 0, 0, 100, 100,
393 [&receiverBackground, &child](RenderProperties& properties,
394 SkiaRecordingCanvas& canvas) {
395 canvas.drawRenderNode(receiverBackground.get());
396 canvas.drawRenderNode(child.get());
397 },
398 "A");
399 ContextFactory contextFactory;
400 std::unique_ptr<CanvasContext> canvasContext(
401 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
402 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
403 DamageAccumulator damageAccumulator;
404 info.damageAccumulator = &damageAccumulator;
405 parent->prepareTree(info);
406
407 // parent(A) -> (receiverBackground, child)
408 // child(C) -> (rect[0, 0, 100, 100], projectingRipple)
409 // projectingRipple(P) -> (rect[0, 0, 100, 100]) -> projects backwards
410 // receiverBackground(B) -> (empty) -> projection receiver
411
412 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
413 ProjectionTestCanvas canvas(100, 100);
414 RenderNodeDrawable drawable(parent.get(), &canvas, true);
415 canvas.drawDrawable(&drawable);
416 EXPECT_EQ(2, canvas.getDrawCounter());
417}
418
Stan Ilieve9d00122017-09-19 12:07:10 -0400419RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500420 /* R is backward projected on B and C is a layer.
421 A
422 / \
423 B C
424 |
425 R
426 */
427 static const int SCROLL_X = 5;
428 static const int SCROLL_Y = 10;
429 static const int CANVAS_WIDTH = 400;
430 static const int CANVAS_HEIGHT = 400;
431 static const int LAYER_WIDTH = 200;
432 static const int LAYER_HEIGHT = 200;
433 class ProjectionTestCanvas : public SkCanvas {
434 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500435 ProjectionTestCanvas(int* drawCounter)
John Reck1bcacfd2017-11-03 10:12:19 -0700436 : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT), mDrawCounter(drawCounter) {}
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500437 void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
John Reck1bcacfd2017-11-03 10:12:19 -0700438 const SkPaint&) override {
439 EXPECT_EQ(0, (*mDrawCounter)++); // part of painting the layer
440 EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT),
441 TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500442 }
443 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500444 EXPECT_EQ(1, (*mDrawCounter)++);
John Reck1bcacfd2017-11-03 10:12:19 -0700445 EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT),
446 TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500447 }
448 void onDrawOval(const SkRect&, const SkPaint&) override {
Mike Reed6acfe162016-11-18 17:21:09 -0500449 EXPECT_EQ(2, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500450 SkMatrix expectedMatrix;
451 expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
452 EXPECT_EQ(expectedMatrix, getTotalMatrix());
Stan Iliev52771272016-11-17 09:54:38 -0500453 EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500454 }
Mike Reed6acfe162016-11-18 17:21:09 -0500455 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500456 };
457
458 class ProjectionLayer : public SkSurface_Base {
459 public:
Mike Reed6acfe162016-11-18 17:21:09 -0500460 ProjectionLayer(int* drawCounter)
John Reck1bcacfd2017-11-03 10:12:19 -0700461 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
462 , mDrawCounter(drawCounter) {}
Derek Sollenberger03e6cff72017-12-04 15:07:08 -0500463 virtual sk_sp<SkImage> onNewImageSnapshot() override {
Mike Reed6acfe162016-11-18 17:21:09 -0500464 EXPECT_EQ(3, (*mDrawCounter)++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500465 EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
John Reck1bcacfd2017-11-03 10:12:19 -0700466 300 - SCROLL_Y),
467 TestUtils::getClipBounds(this->getCanvas()));
Derek Sollenberger03e6cff72017-12-04 15:07:08 -0500468 return nullptr;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500469 }
John Reck1bcacfd2017-11-03 10:12:19 -0700470 SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); }
471 sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500472 void onCopyOnWrite(ContentChangeMode) override {}
Mike Reed6acfe162016-11-18 17:21:09 -0500473 int* mDrawCounter;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500474 };
475
John Reck1bcacfd2017-11-03 10:12:19 -0700476 auto receiverBackground = TestUtils::createSkiaNode(
477 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500478 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700479 properties.setProjectionReceiver(true);
480 // scroll doesn't apply to background, so undone via translationX/Y
481 // NOTE: translationX/Y only! no other transform properties may be set for a proj
482 // receiver!
483 properties.setTranslationX(SCROLL_X);
484 properties.setTranslationY(SCROLL_Y);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500485
John Reck1bcacfd2017-11-03 10:12:19 -0700486 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
487 },
488 "B"); // B
489 auto projectingRipple = TestUtils::createSkiaNode(
490 0, 0, LAYER_WIDTH, LAYER_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500491 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700492 properties.setProjectBackwards(true);
493 properties.setClipToBounds(false);
494 canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
495 },
496 "R"); // R
497 auto child = TestUtils::createSkiaNode(
498 100, 100, 300, 300,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500499 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700500 canvas.drawRenderNode(projectingRipple.get());
501 canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
502 },
503 "C"); // C
504 auto parent = TestUtils::createSkiaNode(
505 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500506 [&receiverBackground, &child](RenderProperties& properties,
John Reck1bcacfd2017-11-03 10:12:19 -0700507 SkiaRecordingCanvas& canvas) {
508 // Set a rect outline for the projecting ripple to be masked against.
509 properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
510 canvas.translate(-SCROLL_X,
511 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
512 canvas.drawRenderNode(receiverBackground.get());
513 canvas.drawRenderNode(child.get());
514 },
515 "A"); // A
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500516
John Reck1bcacfd2017-11-03 10:12:19 -0700517 // prepareTree is required to find, which receivers have backward projected nodes
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500518 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700519 std::unique_ptr<CanvasContext> canvasContext(
520 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500521 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
522 DamageAccumulator damageAccumulator;
523 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500524 parent->prepareTree(info);
525
Mike Reed6acfe162016-11-18 17:21:09 -0500526 int drawCounter = 0;
John Reck1bcacfd2017-11-03 10:12:19 -0700527 // set a layer after prepareTree to avoid layer logic there
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500528 child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
Mike Reed6acfe162016-11-18 17:21:09 -0500529 sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500530 child->setLayerSurface(surfaceLayer1);
531 Matrix4 windowTransform;
532 windowTransform.loadTranslate(100, 100, 0);
533 child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
534
535 LayerUpdateQueue layerUpdateQueue;
536 layerUpdateQueue.enqueueLayerWithDamage(child.get(),
John Reck1bcacfd2017-11-03 10:12:19 -0700537 android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
Stan Ilieve9d00122017-09-19 12:07:10 -0400538 auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
539 pipeline->renderLayersImpl(layerUpdateQueue, true, false);
John Reck1bcacfd2017-11-03 10:12:19 -0700540 EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500541
Mike Reed6acfe162016-11-18 17:21:09 -0500542 RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
543 surfaceLayer1->getCanvas()->drawDrawable(&drawable);
544 EXPECT_EQ(4, drawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500545
546 // clean up layer pointer, so we can safely destruct RenderNode
547 child->setLayerSurface(nullptr);
548}
549
550RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
551 /* R is backward projected on B.
552 A
553 / \
554 B C
555 |
556 R
557 */
558 static const int SCROLL_X = 500000;
559 static const int SCROLL_Y = 0;
560 static const int CANVAS_WIDTH = 400;
561 static const int CANVAS_HEIGHT = 400;
562 class ProjectionChildScrollTestCanvas : public SkCanvas {
563 public:
564 ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
565 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
Stan Iliev52771272016-11-17 09:54:38 -0500566 EXPECT_EQ(0, mDrawCounter++);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500567 EXPECT_TRUE(getTotalMatrix().isIdentity());
568 }
569 void onDrawOval(const SkRect&, const SkPaint&) override {
Stan Iliev52771272016-11-17 09:54:38 -0500570 EXPECT_EQ(1, mDrawCounter++);
571 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500572 EXPECT_TRUE(getTotalMatrix().isIdentity());
573 }
Stan Iliev52771272016-11-17 09:54:38 -0500574 int mDrawCounter = 0;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500575 };
576
John Reck1bcacfd2017-11-03 10:12:19 -0700577 auto receiverBackground = TestUtils::createSkiaNode(
578 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500579 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700580 properties.setProjectionReceiver(true);
581 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
582 },
583 "B"); // B
584 auto projectingRipple = TestUtils::createSkiaNode(
585 0, 0, 200, 200,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500586 [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700587 // scroll doesn't apply to background, so undone via translationX/Y
588 // NOTE: translationX/Y only! no other transform properties may be set for a proj
589 // receiver!
590 properties.setTranslationX(SCROLL_X);
591 properties.setTranslationY(SCROLL_Y);
592 properties.setProjectBackwards(true);
593 properties.setClipToBounds(false);
594 canvas.drawOval(0, 0, 200, 200, SkPaint());
595 },
596 "R"); // R
597 auto child = TestUtils::createSkiaNode(
598 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500599 [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700600 // Record time clip will be ignored by projectee
601 canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500602
John Reck1bcacfd2017-11-03 10:12:19 -0700603 canvas.translate(-SCROLL_X,
604 -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
605 canvas.drawRenderNode(projectingRipple.get());
606 },
607 "C"); // C
608 auto parent =
609 TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
610 [&receiverBackground, &child](RenderProperties& properties,
611 SkiaRecordingCanvas& canvas) {
612 canvas.drawRenderNode(receiverBackground.get());
613 canvas.drawRenderNode(child.get());
614 },
615 "A"); // A
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500616
John Reck1bcacfd2017-11-03 10:12:19 -0700617 // prepareTree is required to find, which receivers have backward projected nodes
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500618 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700619 std::unique_ptr<CanvasContext> canvasContext(
620 CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500621 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
622 DamageAccumulator damageAccumulator;
623 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500624 parent->prepareTree(info);
625
Mike Reed6acfe162016-11-18 17:21:09 -0500626 std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500627 RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
628 canvas->drawDrawable(&drawable);
Stan Iliev52771272016-11-17 09:54:38 -0500629 EXPECT_EQ(2, canvas->mDrawCounter);
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500630}
631
632namespace {
John Reck1bcacfd2017-11-03 10:12:19 -0700633static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500634 ContextFactory contextFactory;
John Reck1bcacfd2017-11-03 10:12:19 -0700635 std::unique_ptr<CanvasContext> canvasContext(
636 CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500637 TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
638 DamageAccumulator damageAccumulator;
639 info.damageAccumulator = &damageAccumulator;
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500640 renderNode->prepareTree(info);
641
John Reck1bcacfd2017-11-03 10:12:19 -0700642 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500643 ZReorderCanvas canvas(100, 100);
644 RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
645 canvas.drawDrawable(&drawable);
646 return canvas.getIndex();
647}
648}
649
650RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
651 /* R is backward projected on B
652 A
653 / \
654 B C
655 |
656 R
657 */
John Reck1bcacfd2017-11-03 10:12:19 -0700658 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
659 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500660 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
661 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700662 }); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500663 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
664 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
665 props.setProjectBackwards(true);
666 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700667 }); // nodeR
668 }); // nodeC
669 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500670 EXPECT_EQ(3, drawNode(renderThread, nodeA));
671}
672
673RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
674 /* R is backward projected on E
675 A
676 / | \
677 / | \
678 B C E
679 |
680 R
681 */
John Reck1bcacfd2017-11-03 10:12:19 -0700682 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
683 SkiaRecordingCanvas& canvas) {
684 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500685 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700686 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
687 SkiaRecordingCanvas& canvas) { // drawn as 2
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500688 props.setProjectBackwards(true);
689 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700690 }); // nodeR
691 }); // nodeC
692 drawOrderedNode(&canvas, 2,
693 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // drawn as 3
694 props.setProjectionReceiver(true);
695 }); // nodeE
696 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500697 EXPECT_EQ(4, drawNode(renderThread, nodeA));
698}
699
700RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
701 /* R is backward projected without receiver
702 A
703 / \
704 B C
705 |
706 R
707 */
John Reck1bcacfd2017-11-03 10:12:19 -0700708 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
709 SkiaRecordingCanvas& canvas) {
710 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500711 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
712 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700713 // not having a projection receiver is an undefined behavior
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500714 props.setProjectBackwards(true);
715 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700716 }); // nodeR
717 }); // nodeC
718 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500719 EXPECT_EQ(2, drawNode(renderThread, nodeA));
720}
721
722RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
723 /* R is backward projected on C
724 A
725 / \
726 B C
727 |
728 R
729 */
John Reck1bcacfd2017-11-03 10:12:19 -0700730 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
731 SkiaRecordingCanvas& canvas) {
732 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500733 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
734 props.setProjectionReceiver(true);
735 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
736 props.setProjectBackwards(true);
737 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700738 }); // nodeR
739 }); // nodeC
740 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500741 EXPECT_EQ(3, drawNode(renderThread, nodeA));
742}
743
744RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
745 /* R is backward projected on R
746 A
747 / \
748 B C
749 |
750 R
751 */
John Reck1bcacfd2017-11-03 10:12:19 -0700752 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
753 SkiaRecordingCanvas& canvas) {
754 drawOrderedNode(&canvas, 0, nullptr); // nodeB
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500755 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
756 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700757 // having a node that is projected on itself is an undefined/unexpected behavior
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500758 props.setProjectionReceiver(true);
759 props.setProjectBackwards(true);
760 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700761 }); // nodeR
762 }); // nodeC
763 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500764 EXPECT_EQ(2, drawNode(renderThread, nodeA));
765}
766
John Reck1bcacfd2017-11-03 10:12:19 -0700767// Note: the outcome for this test is different in HWUI
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500768RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
769 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
770 A
771 /|\
772 / | \
773 B C R
774 */
John Reck1bcacfd2017-11-03 10:12:19 -0700775 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
776 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500777 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
778 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700779 }); // nodeB
780 drawOrderedNode(&canvas, 1,
781 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500782 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
783 props.setProjectBackwards(true);
784 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700785 }); // nodeR
786 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500787 EXPECT_EQ(2, drawNode(renderThread, nodeA));
788}
789
790RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
791 /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
792 A
793 |
794 G
795 /|\
796 / | \
797 B C R
798 */
John Reck1bcacfd2017-11-03 10:12:19 -0700799 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
800 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500801 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
802 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
803 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700804 }); // nodeB
805 drawOrderedNode(&canvas, 2,
806 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {}); // nodeC
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500807 drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
808 props.setProjectBackwards(true);
809 props.setClipToBounds(false);
John Reck1bcacfd2017-11-03 10:12:19 -0700810 }); // nodeR
811 }); // nodeG
812 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500813 EXPECT_EQ(3, drawNode(renderThread, nodeA));
814}
815
816RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
817 /* R is backward projected on B
818 A
819 |
820 B
821 |
822 C
823 |
824 R
825 */
John Reck1bcacfd2017-11-03 10:12:19 -0700826 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
827 SkiaRecordingCanvas& canvas) {
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500828 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
829 props.setProjectionReceiver(true);
830 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -0700831 drawOrderedNode(&canvas, 2,
832 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
833 props.setProjectBackwards(true);
834 props.setClipToBounds(false);
835 }); // nodeR
836 }); // nodeC
837 }); // nodeB
838 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500839 EXPECT_EQ(3, drawNode(renderThread, nodeA));
840}
841
842RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
843 /* B and G are receivables, R is backward projected
844 A
845 / \
846 B C
847 / \
848 G R
849 */
John Reck1bcacfd2017-11-03 10:12:19 -0700850 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
851 SkiaRecordingCanvas& canvas) {
852 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500853 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700854 }); // nodeB
855 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
856 drawOrderedNode(&canvas, 3,
857 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
858 props.setProjectionReceiver(true);
859 }); // nodeG
860 drawOrderedNode(&canvas, 1,
861 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
862 props.setProjectBackwards(true);
863 props.setClipToBounds(false);
864 }); // nodeR
865 }); // nodeC
866 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500867 EXPECT_EQ(4, drawNode(renderThread, nodeA));
868}
869
870RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
871 /* B and G are receivables, G is backward projected
872 A
873 / \
874 B C
875 / \
876 G R
877 */
John Reck1bcacfd2017-11-03 10:12:19 -0700878 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
879 SkiaRecordingCanvas& canvas) {
880 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500881 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700882 }); // nodeB
883 drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
884 drawOrderedNode(&canvas, 1,
885 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
886 props.setProjectionReceiver(true);
887 props.setProjectBackwards(true);
888 props.setClipToBounds(false);
889 }); // nodeG
890 drawOrderedNode(&canvas, 3,
891 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // R
892 }); // nodeR
893 }); // nodeC
894 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500895 EXPECT_EQ(4, drawNode(renderThread, nodeA));
896}
897
898RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
899 /* B and G are receivables, R is backward projected
900 A
901 / \
902 B C
903 / \
904 G D
905 |
906 R
907 */
John Reck1bcacfd2017-11-03 10:12:19 -0700908 auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props,
909 SkiaRecordingCanvas& canvas) {
910 drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // B
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500911 props.setProjectionReceiver(true);
John Reck1bcacfd2017-11-03 10:12:19 -0700912 }); // nodeB
913 drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // C
914 drawOrderedNode(&canvas, 2,
915 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // G
916 props.setProjectionReceiver(true);
917 }); // nodeG
918 drawOrderedNode(&canvas, 4,
919 [](RenderProperties& props, SkiaRecordingCanvas& canvas) { // D
920 drawOrderedNode(&canvas, 3, [](RenderProperties& props,
921 SkiaRecordingCanvas& canvas) { // R
922 props.setProjectBackwards(true);
923 props.setClipToBounds(false);
924 }); // nodeR
925 }); // nodeD
926 }); // nodeC
927 }); // nodeA
Stan Ilievdb45a4b2016-11-08 14:18:31 -0500928 EXPECT_EQ(5, drawNode(renderThread, nodeA));
Stan Iliev021693b2016-10-17 16:26:15 -0400929}
Stan Iliev52771272016-11-17 09:54:38 -0500930
931RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
932 static const int CANVAS_WIDTH = 100;
933 static const int CANVAS_HEIGHT = 200;
934 class SimpleTestCanvas : public TestCanvasBase {
935 public:
John Reck1bcacfd2017-11-03 10:12:19 -0700936 SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Stan Iliev52771272016-11-17 09:54:38 -0500937 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
938 EXPECT_EQ(0, mDrawCounter++);
939 }
940 void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
941 EXPECT_EQ(1, mDrawCounter++);
942 }
943 };
944
945 auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
John Reck1bcacfd2017-11-03 10:12:19 -0700946 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
947 sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
948 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
949 SkPaint());
950 canvas.drawBitmap(*bitmap, 10, 10, nullptr);
951 });
Stan Iliev52771272016-11-17 09:54:38 -0500952
953 SimpleTestCanvas canvas;
954 RenderNodeDrawable drawable(node.get(), &canvas, true);
955 canvas.drawDrawable(&drawable);
956 EXPECT_EQ(2, canvas.mDrawCounter);
957}
958
959RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
960 static const int CANVAS_WIDTH = 200;
961 static const int CANVAS_HEIGHT = 200;
962 class ColorTestCanvas : public TestCanvasBase {
963 public:
John Reck1bcacfd2017-11-03 10:12:19 -0700964 ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Stan Iliev52771272016-11-17 09:54:38 -0500965 void onDrawPaint(const SkPaint&) {
966 switch (mDrawCounter++) {
John Reck1bcacfd2017-11-03 10:12:19 -0700967 case 0:
968 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
969 TestUtils::getClipBounds(this));
970 break;
971 case 1:
972 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
973 break;
974 default:
975 ADD_FAILURE();
Stan Iliev52771272016-11-17 09:54:38 -0500976 }
977 }
978 };
979
John Reck1bcacfd2017-11-03 10:12:19 -0700980 auto unclippedColorView = TestUtils::createSkiaNode(
981 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
982 props.setClipToBounds(false);
983 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
984 });
Stan Iliev52771272016-11-17 09:54:38 -0500985
John Reck1bcacfd2017-11-03 10:12:19 -0700986 auto clippedColorView = TestUtils::createSkiaNode(
987 0, 0, 10, 10, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
988 canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
989 });
Stan Iliev52771272016-11-17 09:54:38 -0500990
991 ColorTestCanvas canvas;
992 RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
993 canvas.drawDrawable(&drawable);
994 EXPECT_EQ(1, canvas.mDrawCounter);
995 RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
996 canvas.drawDrawable(&drawable2);
997 EXPECT_EQ(2, canvas.mDrawCounter);
998}
999
1000TEST(RenderNodeDrawable, renderNode) {
1001 static const int CANVAS_WIDTH = 200;
1002 static const int CANVAS_HEIGHT = 200;
1003 class RenderNodeTestCanvas : public TestCanvasBase {
1004 public:
John Reck1bcacfd2017-11-03 10:12:19 -07001005 RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
Stan Iliev52771272016-11-17 09:54:38 -05001006 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
John Reck1bcacfd2017-11-03 10:12:19 -07001007 switch (mDrawCounter++) {
1008 case 0:
1009 EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
1010 TestUtils::getClipBounds(this));
1011 EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
1012 break;
1013 case 1:
1014 EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
1015 EXPECT_EQ(SK_ColorWHITE, paint.getColor());
1016 break;
1017 default:
1018 ADD_FAILURE();
Stan Iliev52771272016-11-17 09:54:38 -05001019 }
1020 }
1021 };
1022
John Reck1bcacfd2017-11-03 10:12:19 -07001023 auto child = TestUtils::createSkiaNode(
1024 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1025 SkPaint paint;
1026 paint.setColor(SK_ColorWHITE);
1027 canvas.drawRect(0, 0, 100, 100, paint);
1028 });
Stan Iliev52771272016-11-17 09:54:38 -05001029
John Reck1bcacfd2017-11-03 10:12:19 -07001030 auto parent = TestUtils::createSkiaNode(
1031 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Iliev52771272016-11-17 09:54:38 -05001032 [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -07001033 SkPaint paint;
1034 paint.setColor(SK_ColorDKGRAY);
1035 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
Stan Iliev52771272016-11-17 09:54:38 -05001036
John Reck1bcacfd2017-11-03 10:12:19 -07001037 canvas.save(SaveFlags::MatrixClip);
1038 canvas.translate(40, 40);
1039 canvas.drawRenderNode(child.get());
1040 canvas.restore();
1041 });
Stan Iliev52771272016-11-17 09:54:38 -05001042
1043 RenderNodeTestCanvas canvas;
1044 RenderNodeDrawable drawable(parent.get(), &canvas, true);
1045 canvas.drawDrawable(&drawable);
1046 EXPECT_EQ(2, canvas.mDrawCounter);
1047}
1048
Stan Ilievd7410f72017-04-04 15:23:54 -04001049TEST(ReorderBarrierDrawable, testShadowMatrix) {
1050 static const int CANVAS_WIDTH = 100;
1051 static const int CANVAS_HEIGHT = 100;
1052 static const float TRANSLATE_X = 11.0f;
1053 static const float TRANSLATE_Y = 22.0f;
1054 static const float CASTER_X = 40.0f;
1055 static const float CASTER_Y = 40.0f;
1056 static const float CASTER_WIDTH = 20.0f;
1057 static const float CASTER_HEIGHT = 20.0f;
1058
Stan Ilievd7410f72017-04-04 15:23:54 -04001059 class ShadowTestCanvas : public SkCanvas {
1060 public:
1061 ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
1062 int getIndex() { return mDrawCounter; }
1063
1064 virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
1065 // expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable,
1066 // 1 EndReorderBarrierDrawable
1067 mDrawCounter++;
1068 SkCanvas::onDrawDrawable(drawable, matrix);
1069 }
1070
1071 virtual void didTranslate(SkScalar dx, SkScalar dy) override {
1072 mDrawCounter++;
1073 EXPECT_EQ(dx, TRANSLATE_X);
1074 EXPECT_EQ(dy, TRANSLATE_Y);
1075 }
1076
1077 virtual void didConcat(const SkMatrix& matrix) override {
1078 // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow
1079 // matrix.
1080 mDrawCounter++;
1081 EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix);
John Reck1bcacfd2017-11-03 10:12:19 -07001082 EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
1083 getTotalMatrix());
Stan Ilievd7410f72017-04-04 15:23:54 -04001084 }
John Reck1bcacfd2017-11-03 10:12:19 -07001085
Stan Ilievd7410f72017-04-04 15:23:54 -04001086 protected:
1087 int mDrawCounter = 0;
1088 };
1089
John Reck1bcacfd2017-11-03 10:12:19 -07001090 auto parent = TestUtils::createSkiaNode(
1091 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
Stan Ilievd7410f72017-04-04 15:23:54 -04001092 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
John Reck1bcacfd2017-11-03 10:12:19 -07001093 canvas.translate(TRANSLATE_X, TRANSLATE_Y);
1094 canvas.insertReorderBarrier(true);
Stan Ilievd7410f72017-04-04 15:23:54 -04001095
John Reck1bcacfd2017-11-03 10:12:19 -07001096 auto node = TestUtils::createSkiaNode(
1097 CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, CASTER_Y + CASTER_HEIGHT,
1098 [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
1099 props.setElevation(42);
1100 props.mutableOutline().setRoundRect(0, 0, 20, 20, 5, 1);
1101 props.mutableOutline().setShouldClip(true);
1102 });
1103 canvas.drawRenderNode(node.get());
1104 canvas.insertReorderBarrier(false);
1105 });
Stan Ilievd7410f72017-04-04 15:23:54 -04001106
John Reck1bcacfd2017-11-03 10:12:19 -07001107 // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
Stan Ilievd7410f72017-04-04 15:23:54 -04001108 ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
1109 RenderNodeDrawable drawable(parent.get(), &canvas, false);
1110 canvas.drawDrawable(&drawable);
1111 EXPECT_EQ(6, canvas.getIndex());
1112}