Home | History | Annotate | Download | only in unit
      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:
     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:
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    229 static FloatColor makeFloatColor(uint32_t color) {
    230     FloatColor c;
    231     c.set(color);
    232     return c;
    233 }
    234 
    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 
    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