1 /*
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
19 #include <BakedOpDispatcher.h>
20 #include <BakedOpRenderer.h>
21 #include <FrameBuilder.h>
22 #include <LayerUpdateQueue.h>
23 #include <hwui/Paint.h>
24 #include <RecordedOp.h>
25 #include <tests/common/TestUtils.h>
26 #include <utils/Color.h>
27
28 #include <SkBlurDrawLooper.h>
29 #include <SkDashPathEffect.h>
30 #include <SkPath.h>
31
32 using namespace android::uirenderer;
33
34 static BakedOpRenderer::LightInfo sLightInfo;
35 const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
36
37 class ValidatingBakedOpRenderer : public BakedOpRenderer {
38 public:
ValidatingBakedOpRenderer(RenderState & renderState,std::function<void (const Glop & glop)> validator)39 ValidatingBakedOpRenderer(RenderState& renderState, std::function<void(const Glop& glop)> validator)
40 : BakedOpRenderer(Caches::getInstance(), renderState, true, false, sLightInfo)
41 , mValidator(validator) {
42 mGlopReceiver = ValidatingGlopReceiver;
43 }
44 private:
ValidatingGlopReceiver(BakedOpRenderer & renderer,const Rect * dirtyBounds,const ClipBase * clip,const Glop & glop)45 static void ValidatingGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
46 const ClipBase* clip, const Glop& glop) {
47
48 auto vbor = reinterpret_cast<ValidatingBakedOpRenderer*>(&renderer);
49 vbor->mValidator(glop);
50 }
51 std::function<void(const Glop& glop)> mValidator;
52 };
53
54 typedef void (*TestBakedOpReceiver)(BakedOpRenderer&, const BakedOpState&);
55
testUnmergedGlopDispatch(renderthread::RenderThread & renderThread,RecordedOp * op,std::function<void (const Glop & glop)> glopVerifier,int expectedGlopCount=1)56 static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, RecordedOp* op,
57 std::function<void(const Glop& glop)> glopVerifier, int expectedGlopCount = 1) {
58 // Create op, and wrap with basic state.
59 LinearAllocator allocator;
60 auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 100));
61 auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
62 ASSERT_NE(nullptr, state);
63
64 int glopCount = 0;
65 auto glopReceiver = [&glopVerifier, &glopCount, &expectedGlopCount] (const Glop& glop) {
66 ASSERT_LE(glopCount++, expectedGlopCount) << expectedGlopCount << "glop(s) expected";
67 glopVerifier(glop);
68 };
69 ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
70
71 // Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior
72 #define X(Type) \
73 [](BakedOpRenderer& renderer, const BakedOpState& state) { \
74 BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \
75 },
76 static TestBakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
77 #undef X
78 unmergedReceivers[op->opId](renderer, *state);
79 ASSERT_EQ(expectedGlopCount, glopCount) << "Exactly " << expectedGlopCount
80 << "Glop(s) expected";
81 }
82
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher,pathTexture_positionOvalArc)83 RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
84 SkPaint strokePaint;
85 strokePaint.setStyle(SkPaint::kStroke_Style);
86 strokePaint.setStrokeWidth(4);
87
88 float intervals[] = {1.0f, 1.0f};
89 strokePaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
90
91 auto textureGlopVerifier = [] (const Glop& glop) {
92 // validate glop produced by renderPathTexture (so texture, unit quad)
93 auto texture = glop.fill.texture.texture;
94 ASSERT_NE(nullptr, texture);
95 float expectedOffset = floor(4 * 1.5f + 0.5f);
96 EXPECT_EQ(expectedOffset, reinterpret_cast<PathTexture*>(texture)->offset)
97 << "Should see conservative offset from PathCache::computeBounds";
98 Rect expectedBounds(10, 15, 20, 25);
99 expectedBounds.outset(expectedOffset);
100
101 Matrix4 expectedModelView;
102 expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
103 expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
104 EXPECT_EQ(expectedModelView, glop.transform.modelView)
105 << "X and Y offsets, and scale both applied to model view";
106 };
107
108 // Arc and Oval will render functionally the same glop, differing only in texture content
109 ArcOp arcOp(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint, 0, 270, true);
110 testUnmergedGlopDispatch(renderThread, &arcOp, textureGlopVerifier);
111
112 OvalOp ovalOp(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint);
113 testUnmergedGlopDispatch(renderThread, &ovalOp, textureGlopVerifier);
114 }
115
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher,onLayerOp_bufferless)116 RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
117 SkPaint layerPaint;
118 layerPaint.setAlpha(128);
119 OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
120 LayerOp op(Rect(10, 10), Matrix4::identity(), nullptr, &layerPaint, &buffer);
121 testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) {
122 ADD_FAILURE() << "Nothing should happen";
123 }, 0);
124 }
125
getGlopTransformFlags(renderthread::RenderThread & renderThread,RecordedOp * op)126 static int getGlopTransformFlags(renderthread::RenderThread& renderThread, RecordedOp* op) {
127 int result = 0;
128 testUnmergedGlopDispatch(renderThread, op, [&result] (const Glop& glop) {
129 result = glop.transform.transformFlags;
130 });
131 return result;
132 }
133
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher,offsetFlags)134 RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) {
135 Rect bounds(10, 15, 20, 25);
136 SkPaint paint;
137 SkPaint aaPaint;
138 aaPaint.setAntiAlias(true);
139
140 RoundRectOp roundRectOp(bounds, Matrix4::identity(), nullptr, &paint, 0, 270);
141 EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &roundRectOp))
142 << "Expect no offset for round rect op.";
143
144 const float points[4] = {0.5, 0.5, 1.0, 1.0};
145 PointsOp antiAliasedPointsOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
146 EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedPointsOp))
147 << "Expect no offset for AA points.";
148 PointsOp pointsOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
149 EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &pointsOp))
150 << "Expect an offset for non-AA points.";
151
152 LinesOp antiAliasedLinesOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
153 EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedLinesOp))
154 << "Expect no offset for AA lines.";
155 LinesOp linesOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
156 EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &linesOp))
157 << "Expect an offset for non-AA lines.";
158 }
159
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher,renderTextWithShadow)160 RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) {
161 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
162 [](RenderProperties& props, RecordingCanvas& canvas) {
163
164 android::Paint shadowPaint;
165 shadowPaint.setColor(SK_ColorRED);
166
167 SkScalar sigma = Blur::convertRadiusToSigma(5);
168 shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3));
169
170 TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25);
171 TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50);
172 });
173
174 int glopCount = 0;
175 auto glopReceiver = [&glopCount] (const Glop& glop) {
176 if (glopCount < 2) {
177 // two white shadows
178 EXPECT_EQ(FloatColor({1, 1, 1, 1}), glop.fill.color);
179 } else {
180 // two text draws merged into one, drawn after both shadows
181 EXPECT_EQ(FloatColor({1, 0, 0, 1}), glop.fill.color);
182 }
183 glopCount++;
184 };
185
186 ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
187
188 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
189 sLightGeometry, Caches::getInstance());
190 frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
191
192 frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
193 ASSERT_EQ(3, glopCount) << "Exactly three glops expected";
194 }
195
validateLayerDraw(renderthread::RenderThread & renderThread,std::function<void (const Glop & glop)> validator)196 static void validateLayerDraw(renderthread::RenderThread& renderThread,
197 std::function<void(const Glop& glop)> validator) {
198 auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
199 [](RenderProperties& props, RecordingCanvas& canvas) {
200 props.mutateLayerProperties().setType(LayerType::RenderLayer);
201
202 // provide different blend mode, so decoration draws contrast
203 props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc);
204 canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
205 });
206 OffscreenBuffer** layerHandle = node->getLayerHandle();
207
208 auto syncedNode = TestUtils::getSyncedNode(node);
209
210 // create RenderNode's layer here in same way prepareTree would
211 OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
212 *layerHandle = &layer;
213 {
214 LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
215 layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(0, 0, 100, 100));
216
217 ValidatingBakedOpRenderer renderer(renderThread.renderState(), validator);
218 FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
219 sLightGeometry, Caches::getInstance());
220 frameBuilder.deferLayers(layerUpdateQueue);
221 frameBuilder.deferRenderNode(*syncedNode);
222 frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
223 }
224
225 // clean up layer pointer, so we can safely destruct RenderNode
226 *layerHandle = nullptr;
227 }
228
makeFloatColor(uint32_t color)229 static FloatColor makeFloatColor(uint32_t color) {
230 FloatColor c;
231 c.set(color);
232 return c;
233 }
234
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher,layerUpdateProperties)235 RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) {
236 for (bool debugOverdraw : { false, true }) {
237 for (bool debugLayersUpdates : { false, true }) {
238 ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
239 ScopedProperty<bool> lupProp(Properties::debugLayersUpdates, debugLayersUpdates);
240
241 int glopCount = 0;
242 validateLayerDraw(renderThread, [&glopCount, &debugLayersUpdates](const Glop& glop) {
243 if (glopCount == 0) {
244 // 0 - Black layer fill
245 EXPECT_TRUE(glop.fill.colorEnabled);
246 EXPECT_EQ(makeFloatColor(Color::Black), glop.fill.color);
247 } else if (glopCount == 1) {
248 // 1 - Uncolored (textured) layer draw
249 EXPECT_FALSE(glop.fill.colorEnabled);
250 } else if (glopCount == 2) {
251 // 2 - layer overlay, if present
252 EXPECT_TRUE(glop.fill.colorEnabled);
253 // blend srcover, different from that of layer
254 EXPECT_EQ(GLenum(GL_ONE), glop.blend.src);
255 EXPECT_EQ(GLenum(GL_ONE_MINUS_SRC_ALPHA), glop.blend.dst);
256 EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0),
257 glop.fill.color) << "Should be transparent green if debugLayersUpdates";
258 } else if (glopCount < 7) {
259 // 3 - 6 - overdraw indicator overlays, if present
260 EXPECT_TRUE(glop.fill.colorEnabled);
261 uint32_t expectedColor = Caches::getInstance().getOverdrawColor(glopCount - 2);
262 ASSERT_EQ(makeFloatColor(expectedColor), glop.fill.color);
263 } else {
264 ADD_FAILURE() << "Too many glops observed";
265 }
266 glopCount++;
267 });
268 int expectedCount = 2;
269 if (debugLayersUpdates || debugOverdraw) expectedCount++;
270 if (debugOverdraw) expectedCount += 4;
271 EXPECT_EQ(expectedCount, glopCount);
272 }
273 }
274 }
275
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher,pathTextureSnapping)276 RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) {
277 Rect bounds(10, 15, 20, 25);
278 SkPaint paint;
279 SkPath path;
280 path.addRect(SkRect::MakeXYWH(1.5, 3.8, 100, 90));
281 PathOp op(bounds, Matrix4::identity(), nullptr, &paint, &path);
282 testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) {
283 auto texture = glop.fill.texture.texture;
284 ASSERT_NE(nullptr, texture);
285 EXPECT_EQ(1, reinterpret_cast<PathTexture*>(texture)->left);
286 EXPECT_EQ(3, reinterpret_cast<PathTexture*>(texture)->top);
287 });
288 }
289