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 <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:
     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:
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    230 static FloatColor makeFloatColor(uint32_t color) {
    231     FloatColor c;
    232     c.set(color);
    233     return c;
    234 }
    235 
    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 
    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