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