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