Home | History | Annotate | Download | only in common
      1 /*
      2  * Copyright (C) 2015 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 #pragma once
     18 
     19 #include <DisplayList.h>
     20 #include <Matrix.h>
     21 #include <Properties.h>
     22 #include <Rect.h>
     23 #include <RenderNode.h>
     24 #include <hwui/Bitmap.h>
     25 #include <pipeline/skia/SkiaRecordingCanvas.h>
     26 #include <private/hwui/DrawGlInfo.h>
     27 #include <renderstate/RenderState.h>
     28 #include <renderthread/RenderThread.h>
     29 
     30 #include <gtest/gtest.h>
     31 #include <memory>
     32 
     33 namespace android {
     34 namespace uirenderer {
     35 
     36 #define EXPECT_MATRIX_APPROX_EQ(a, b) EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b))
     37 
     38 #define EXPECT_RECT_APPROX_EQ(a, b)                          \
     39     EXPECT_TRUE(MathUtils::areEqual((a).left, (b).left) &&   \
     40                 MathUtils::areEqual((a).top, (b).top) &&     \
     41                 MathUtils::areEqual((a).right, (b).right) && \
     42                 MathUtils::areEqual((a).bottom, (b).bottom));
     43 
     44 #define EXPECT_CLIP_RECT(expRect, clipStatePtr)                                      \
     45     EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped";                         \
     46     if ((clipStatePtr)->mode == ClipMode::Rectangle) {                               \
     47         EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \
     48     } else {                                                                         \
     49         ADD_FAILURE() << "ClipState not a rect";                                     \
     50     }
     51 
     52 #define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
     53     TEST(test_case_name, test_name##_##pipeline) {                             \
     54         RenderPipelineType oldType = Properties::getRenderPipelineType();      \
     55         Properties::overrideRenderPipelineType(RenderPipelineType::pipeline);  \
     56         functionCall;                                                          \
     57         Properties::overrideRenderPipelineType(oldType);                       \
     58     };
     59 
     60 #define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
     61     INNER_PIPELINE_TEST(test_case_name, test_name, pipeline,                  \
     62                         TestUtils::runOnRenderThread(                         \
     63                                 test_case_name##_##test_name##_RenderThreadTest::doTheThing))
     64 
     65 /**
     66  * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
     67  * (for e.g. accessing its RenderState)
     68  */
     69 #define RENDERTHREAD_TEST(test_case_name, test_name)                                        \
     70     class test_case_name##_##test_name##_RenderThreadTest {                                 \
     71     public:                                                                                 \
     72         static void doTheThing(renderthread::RenderThread& renderThread);                   \
     73     };                                                                                      \
     74     INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);                    \
     75     /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
     76     /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */          \
     77     void test_case_name##_##test_name##_RenderThreadTest::doTheThing(                       \
     78             renderthread::RenderThread& renderThread)
     79 
     80 /**
     81  * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
     82  */
     83 #define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name)                          \
     84     class test_case_name##_##test_name##_RenderThreadTest {                                 \
     85     public:                                                                                 \
     86         static void doTheThing(renderthread::RenderThread& renderThread);                   \
     87     };                                                                                      \
     88     INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);                    \
     89     /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
     90     /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */          \
     91     void test_case_name##_##test_name##_RenderThreadTest::doTheThing(                       \
     92             renderthread::RenderThread& renderThread)
     93 
     94 /**
     95  * Sets a property value temporarily, generally for the duration of a test, restoring the previous
     96  * value when going out of scope.
     97  *
     98  * Can be used e.g. to test behavior only active while Properties::debugOverdraw is enabled.
     99  */
    100 template <typename T>
    101 class ScopedProperty {
    102 public:
    103     ScopedProperty(T& property, T newValue) : mPropertyPtr(&property), mOldValue(property) {
    104         property = newValue;
    105     }
    106     ~ScopedProperty() { *mPropertyPtr = mOldValue; }
    107 
    108 private:
    109     T* mPropertyPtr;
    110     T mOldValue;
    111 };
    112 
    113 class TestUtils {
    114 public:
    115     class SignalingDtor {
    116     public:
    117         SignalingDtor() : mSignal(nullptr) {}
    118         explicit SignalingDtor(int* signal) : mSignal(signal) {}
    119         void setSignal(int* signal) { mSignal = signal; }
    120         ~SignalingDtor() {
    121             if (mSignal) {
    122                 (*mSignal)++;
    123             }
    124         }
    125 
    126     private:
    127         int* mSignal;
    128     };
    129 
    130     class MockTreeObserver : public TreeObserver {
    131     public:
    132         virtual void onMaybeRemovedFromTree(RenderNode* node) {}
    133     };
    134 
    135     static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
    136         for (int i = 0; i < 16; i++) {
    137             if (!MathUtils::areEqual(a[i], b[i])) {
    138                 return false;
    139             }
    140         }
    141         return true;
    142     }
    143 
    144     static sk_sp<Bitmap> createBitmap(int width, int height,
    145                                       SkColorType colorType = kN32_SkColorType) {
    146         SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
    147         return Bitmap::allocateHeapBitmap(info);
    148     }
    149 
    150     static sk_sp<Bitmap> createBitmap(int width, int height, SkBitmap* outBitmap) {
    151         SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
    152         outBitmap->setInfo(info);
    153         return Bitmap::allocateHeapBitmap(outBitmap);
    154     }
    155 
    156     static sp<DeferredLayerUpdater> createTextureLayerUpdater(
    157             renderthread::RenderThread& renderThread);
    158 
    159     static sp<DeferredLayerUpdater> createTextureLayerUpdater(
    160             renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
    161             const SkMatrix& transform);
    162 
    163     template <class CanvasType>
    164     static std::unique_ptr<DisplayList> createDisplayList(
    165             int width, int height, std::function<void(CanvasType& canvas)> canvasCallback) {
    166         CanvasType canvas(width, height);
    167         canvasCallback(canvas);
    168         return std::unique_ptr<DisplayList>(canvas.finishRecording());
    169     }
    170 
    171     static sp<RenderNode> createNode(
    172             int left, int top, int right, int bottom,
    173             std::function<void(RenderProperties& props, Canvas& canvas)> setup) {
    174         sp<RenderNode> node = new RenderNode();
    175         RenderProperties& props = node->mutateStagingProperties();
    176         props.setLeftTopRightBottom(left, top, right, bottom);
    177         if (setup) {
    178             std::unique_ptr<Canvas> canvas(
    179                     Canvas::create_recording_canvas(props.getWidth(), props.getHeight()));
    180             setup(props, *canvas.get());
    181             node->setStagingDisplayList(canvas->finishRecording());
    182         }
    183         node->setPropertyFieldsDirty(0xFFFFFFFF);
    184         return node;
    185     }
    186 
    187     template <class RecordingCanvasType>
    188     static sp<RenderNode> createNode(
    189             int left, int top, int right, int bottom,
    190             std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) {
    191         sp<RenderNode> node = new RenderNode();
    192         RenderProperties& props = node->mutateStagingProperties();
    193         props.setLeftTopRightBottom(left, top, right, bottom);
    194         if (setup) {
    195             RecordingCanvasType canvas(props.getWidth(), props.getHeight());
    196             setup(props, canvas);
    197             node->setStagingDisplayList(canvas.finishRecording());
    198         }
    199         node->setPropertyFieldsDirty(0xFFFFFFFF);
    200         return node;
    201     }
    202 
    203     static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) {
    204         std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
    205                 node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node));
    206         contentCallback(*canvas.get());
    207         node.setStagingDisplayList(canvas->finishRecording());
    208     }
    209 
    210     static sp<RenderNode> createSkiaNode(
    211             int left, int top, int right, int bottom,
    212             std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)>
    213                     setup,
    214             const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
    215         sp<RenderNode> node = new RenderNode();
    216         if (name) {
    217             node->setName(name);
    218         }
    219         RenderProperties& props = node->mutateStagingProperties();
    220         props.setLeftTopRightBottom(left, top, right, bottom);
    221         if (displayList) {
    222             node->setStagingDisplayList(displayList);
    223         }
    224         if (setup) {
    225             std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas(
    226                     new skiapipeline::SkiaRecordingCanvas(nullptr, props.getWidth(),
    227                                                           props.getHeight()));
    228             setup(props, *canvas.get());
    229             node->setStagingDisplayList(canvas->finishRecording());
    230         }
    231         node->setPropertyFieldsDirty(0xFFFFFFFF);
    232         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
    233         return node;
    234     }
    235 
    236     /**
    237      * Forces a sync of a tree of RenderNode, such that every descendant will have its staging
    238      * properties and DisplayList moved to the render copies.
    239      *
    240      * Note: does not check dirtiness bits, so any non-staging DisplayLists will be discarded.
    241      * For this reason, this should generally only be called once on a tree.
    242      */
    243     static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
    244         syncHierarchyPropertiesAndDisplayListImpl(node.get());
    245     }
    246 
    247     static sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) {
    248         syncHierarchyPropertiesAndDisplayList(node);
    249         return node;
    250     }
    251 
    252     typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
    253 
    254     class TestTask : public renderthread::RenderTask {
    255     public:
    256         explicit TestTask(RtCallback rtCallback) : rtCallback(rtCallback) {}
    257         virtual ~TestTask() {}
    258         virtual void run() override;
    259         RtCallback rtCallback;
    260     };
    261 
    262     /**
    263      * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely.
    264      */
    265     static void runOnRenderThread(RtCallback rtCallback) {
    266         TestTask task(rtCallback);
    267         renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); });
    268     }
    269 
    270     static void runOnRenderThreadUnmanaged(RtCallback rtCallback) {
    271         auto& rt = renderthread::RenderThread::getInstance();
    272         rt.queue().runSync([&]() { rtCallback(rt); });
    273     }
    274 
    275 
    276     static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); }
    277     static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); }
    278 
    279     static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
    280 
    281     static void drawUtf8ToCanvas(Canvas* canvas, const char* text, const Paint& paint, float x,
    282                                  float y);
    283 
    284     static void drawUtf8ToCanvas(Canvas* canvas, const char* text, const Paint& paint,
    285                                  const SkPath& path);
    286 
    287     static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str);
    288 
    289     class MockFunctor : public Functor {
    290     public:
    291         virtual status_t operator()(int what, void* data) {
    292             mLastMode = what;
    293             return DrawGlInfo::kStatusDone;
    294         }
    295         int getLastMode() const { return mLastMode; }
    296 
    297     private:
    298         int mLastMode = -1;
    299     };
    300 
    301     static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y);
    302 
    303     static SkRect getClipBounds(const SkCanvas* canvas);
    304     static SkRect getLocalClipBounds(const SkCanvas* canvas);
    305 
    306     struct CallCounts {
    307         int sync = 0;
    308         int contextDestroyed = 0;
    309         int destroyed = 0;
    310         int glesDraw = 0;
    311     };
    312 
    313     static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
    314 
    315     static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
    316         auto callbacks = WebViewFunctorCallbacks{
    317                 .onSync =
    318                         [](int functor, void* client_data, const WebViewSyncData& data) {
    319                             expectOnRenderThread();
    320                             sMockFunctorCounts[functor].sync++;
    321                         },
    322                 .onContextDestroyed =
    323                         [](int functor, void* client_data) {
    324                             expectOnRenderThread();
    325                             sMockFunctorCounts[functor].contextDestroyed++;
    326                         },
    327                 .onDestroyed =
    328                         [](int functor, void* client_data) {
    329                             expectOnRenderThread();
    330                             sMockFunctorCounts[functor].destroyed++;
    331                         },
    332         };
    333         switch (mode) {
    334             case RenderMode::OpenGL_ES:
    335                 callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params) {
    336                     expectOnRenderThread();
    337                     sMockFunctorCounts[functor].glesDraw++;
    338                 };
    339                 break;
    340             default:
    341                 ADD_FAILURE();
    342                 return WebViewFunctorCallbacks{};
    343         }
    344         return callbacks;
    345     }
    346 
    347     static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
    348 
    349 private:
    350     static std::unordered_map<int, CallCounts> sMockFunctorCounts;
    351 
    352     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
    353         MarkAndSweepRemoved observer(nullptr);
    354         node->syncProperties();
    355         if (node->mNeedsDisplayListSync) {
    356             node->mNeedsDisplayListSync = false;
    357             node->syncDisplayList(observer, nullptr);
    358         }
    359         auto displayList = node->getDisplayList();
    360         if (displayList) {
    361             for (auto&& childDr :
    362                  static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList))
    363                          ->mChildNodes) {
    364                 syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
    365             }
    366         }
    367     }
    368 
    369 };  // class TestUtils
    370 
    371 } /* namespace uirenderer */
    372 } /* namespace android */
    373