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