BakedOpDispatcherTests.cpp revision 37413289478a965336239c731ebfea37ac4dde28
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
31using namespace android::uirenderer;
32
33static BakedOpRenderer::LightInfo sLightInfo;
34const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
35
36class ValidatingBakedOpRenderer : public BakedOpRenderer {
37public:
38    ValidatingBakedOpRenderer(RenderState& renderState, std::function<void(const Glop& glop)> validator)
39            : BakedOpRenderer(Caches::getInstance(), renderState, true, sLightInfo)
40            , mValidator(validator) {
41        mGlopReceiver = ValidatingGlopReceiver;
42    }
43private:
44    static void ValidatingGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
45            const ClipBase* clip, const Glop& glop) {
46
47        auto vbor = reinterpret_cast<ValidatingBakedOpRenderer*>(&renderer);
48        vbor->mValidator(glop);
49    }
50    std::function<void(const Glop& glop)> mValidator;
51};
52
53typedef void (*TestBakedOpReceiver)(BakedOpRenderer&, const BakedOpState&);
54
55static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, RecordedOp* op,
56        std::function<void(const Glop& glop)> glopVerifier) {
57    // Create op, and wrap with basic state.
58    LinearAllocator allocator;
59    auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 100));
60    auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
61    ASSERT_NE(nullptr, state);
62
63    int glopCount = 0;
64    auto glopReceiver = [&glopVerifier, &glopCount] (const Glop& glop) {
65        ASSERT_EQ(glopCount++, 0) << "Only one Glop expected";
66        glopVerifier(glop);
67    };
68    ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
69
70    // Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior
71#define X(Type) \
72        [](BakedOpRenderer& renderer, const BakedOpState& state) { \
73            BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \
74        },
75    static TestBakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
76#undef X
77    unmergedReceivers[op->opId](renderer, *state);
78    ASSERT_EQ(1, glopCount) << "Exactly one Glop expected";
79}
80
81RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
82    SkPaint strokePaint;
83    strokePaint.setStyle(SkPaint::kStroke_Style);
84    strokePaint.setStrokeWidth(4);
85
86    float intervals[] = {1.0f, 1.0f};
87    auto dashEffect = SkDashPathEffect::Create(intervals, 2, 0);
88    strokePaint.setPathEffect(dashEffect);
89    dashEffect->unref();
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
116RENDERTHREAD_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, [&renderThread] (const Glop& glop) {
122        // rect glop is dispatched with paint props applied
123        EXPECT_EQ(renderThread.renderState().meshState().getUnitQuadVBO(),
124                glop.mesh.vertices.bufferObject) << "Unit quad should be drawn";
125        EXPECT_EQ(nullptr, glop.fill.texture.texture) << "Should be no texture when layer is null";
126        EXPECT_FLOAT_EQ(128 / 255.0f, glop.fill.color.a) << "Rect quad should use op alpha";
127    });
128}
129
130static int getGlopTransformFlags(renderthread::RenderThread& renderThread, RecordedOp* op) {
131    int result = 0;
132    testUnmergedGlopDispatch(renderThread, op, [&result] (const Glop& glop) {
133        result = glop.transform.transformFlags;
134    });
135    return result;
136}
137
138RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) {
139    Rect bounds(10, 15, 20, 25);
140    SkPaint paint;
141    SkPaint aaPaint;
142    aaPaint.setAntiAlias(true);
143
144    RoundRectOp roundRectOp(bounds, Matrix4::identity(), nullptr, &paint, 0, 270);
145    EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &roundRectOp))
146            << "Expect no offset for round rect op.";
147
148    const float points[4] = {0.5, 0.5, 1.0, 1.0};
149    PointsOp antiAliasedPointsOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
150    EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedPointsOp))
151                << "Expect no offset for AA points.";
152    PointsOp pointsOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
153    EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &pointsOp))
154            << "Expect an offset for non-AA points.";
155
156    LinesOp antiAliasedLinesOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
157    EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedLinesOp))
158            << "Expect no offset for AA lines.";
159    LinesOp linesOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
160    EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &linesOp))
161            << "Expect an offset for non-AA lines.";
162}
163
164RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) {
165    auto node = TestUtils::createNode(0, 0, 100, 100,
166            [](RenderProperties& props, TestCanvas& canvas) {
167
168        android::Paint shadowPaint;
169        shadowPaint.setColor(SK_ColorRED);
170
171        SkScalar sigma = Blur::convertRadiusToSigma(5);
172        shadowPaint.setLooper(SkBlurDrawLooper::Create(SK_ColorWHITE, sigma, 3, 3))->unref();
173
174        TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25);
175        TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50);
176    });
177
178    int  glopCount = 0;
179    auto glopReceiver = [&glopCount] (const Glop& glop) {
180        if (glopCount < 2) {
181            // two white shadows
182            EXPECT_EQ(FloatColor({1, 1, 1, 1}), glop.fill.color);
183        } else {
184            // two text draws merged into one, drawn after both shadows
185            EXPECT_EQ(FloatColor({1, 0, 0, 1}), glop.fill.color);
186        }
187        glopCount++;
188    };
189
190    ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
191
192    FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
193            sLightGeometry, Caches::getInstance());
194    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
195
196    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
197    ASSERT_EQ(3, glopCount) << "Exactly three glops expected";
198}
199
200static void validateLayerDraw(renderthread::RenderThread& renderThread,
201        std::function<void(const Glop& glop)> validator) {
202    auto node = TestUtils::createNode(0, 0, 100, 100,
203            [](RenderProperties& props, TestCanvas& canvas) {
204        props.mutateLayerProperties().setType(LayerType::RenderLayer);
205
206        // provide different blend mode, so decoration draws contrast
207        props.mutateLayerProperties().setXferMode(SkXfermode::Mode::kSrc_Mode);
208        canvas.drawColor(Color::Black, SkXfermode::Mode::kSrcOver_Mode);
209    });
210    OffscreenBuffer** layerHandle = node->getLayerHandle();
211
212    auto syncedNode = TestUtils::getSyncedNode(node);
213
214    // create RenderNode's layer here in same way prepareTree would
215    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
216    *layerHandle = &layer;
217    {
218        LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
219        layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(0, 0, 100, 100));
220
221        ValidatingBakedOpRenderer renderer(renderThread.renderState(), validator);
222        FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
223                sLightGeometry, Caches::getInstance());
224        frameBuilder.deferLayers(layerUpdateQueue);
225        frameBuilder.deferRenderNode(*syncedNode);
226        frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
227    }
228
229    // clean up layer pointer, so we can safely destruct RenderNode
230    *layerHandle = nullptr;
231}
232
233static FloatColor makeFloatColor(uint32_t color) {
234    FloatColor c;
235    c.set(color);
236    return c;
237}
238
239RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) {
240    for (bool debugOverdraw : { false, true }) {
241        for (bool debugLayersUpdates : { false, true }) {
242            ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
243            ScopedProperty<bool> lupProp(Properties::debugLayersUpdates, debugLayersUpdates);
244
245            int glopCount = 0;
246            validateLayerDraw(renderThread, [&glopCount, &debugLayersUpdates](const Glop& glop) {
247                if (glopCount == 0) {
248                    // 0 - Black layer fill
249                    EXPECT_TRUE(glop.fill.colorEnabled);
250                    EXPECT_EQ(makeFloatColor(Color::Black), glop.fill.color);
251                } else if (glopCount == 1) {
252                    // 1 - Uncolored (textured) layer draw
253                    EXPECT_FALSE(glop.fill.colorEnabled);
254                } else if (glopCount == 2) {
255                    // 2 - layer overlay, if present
256                    EXPECT_TRUE(glop.fill.colorEnabled);
257                    // blend srcover, different from that of layer
258                    EXPECT_EQ(GLenum(GL_ONE), glop.blend.src);
259                    EXPECT_EQ(GLenum(GL_ONE_MINUS_SRC_ALPHA), glop.blend.dst);
260                    EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0),
261                            glop.fill.color) << "Should be transparent green if debugLayersUpdates";
262                } else if (glopCount < 7) {
263                    // 3 - 6 - overdraw indicator overlays, if present
264                    EXPECT_TRUE(glop.fill.colorEnabled);
265                    uint32_t expectedColor = Caches::getInstance().getOverdrawColor(glopCount - 2);
266                    ASSERT_EQ(makeFloatColor(expectedColor), glop.fill.color);
267                } else {
268                    ADD_FAILURE() << "Too many glops observed";
269                }
270                glopCount++;
271            });
272            int expectedCount = 2;
273            if (debugLayersUpdates || debugOverdraw) expectedCount++;
274            if (debugOverdraw) expectedCount += 4;
275            EXPECT_EQ(expectedCount, glopCount);
276        }
277    }
278}
279