• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ScopedProperty(T & property,T newValue)103     ScopedProperty(T& property, T newValue) : mPropertyPtr(&property), mOldValue(property) {
104         property = newValue;
105     }
~ScopedProperty()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:
SignalingDtor()117         SignalingDtor() : mSignal(nullptr) {}
SignalingDtor(int * signal)118         explicit SignalingDtor(int* signal) : mSignal(signal) {}
setSignal(int * signal)119         void setSignal(int* signal) { mSignal = signal; }
~SignalingDtor()120         ~SignalingDtor() {
121             if (mSignal) {
122                 (*mSignal)++;
123             }
124         }
125 
126     private:
127         int* mSignal;
128     };
129 
130     class MockTreeObserver : public TreeObserver {
131     public:
onMaybeRemovedFromTree(RenderNode * node)132         virtual void onMaybeRemovedFromTree(RenderNode* node) {}
133     };
134 
matricesAreApproxEqual(const Matrix4 & a,const Matrix4 & b)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 
createBitmap(int width,int height,SkBitmap * outBitmap)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>
createDisplayList(int width,int height,std::function<void (CanvasType & canvas)> canvasCallback)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 
createNode(int left,int top,int right,int bottom,std::function<void (RenderProperties & props,Canvas & canvas)> setup)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>
createNode(int left,int top,int right,int bottom,std::function<void (RenderProperties & props,RecordingCanvasType & canvas)> setup)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 
recordNode(RenderNode & node,std::function<void (Canvas &)> contentCallback)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      */
syncHierarchyPropertiesAndDisplayList(sp<RenderNode> & node)243     static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
244         syncHierarchyPropertiesAndDisplayListImpl(node.get());
245     }
246 
getSyncedNode(sp<RenderNode> & node)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:
TestTask(RtCallback rtCallback)256         explicit TestTask(RtCallback rtCallback) : rtCallback(rtCallback) {}
~TestTask()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      */
runOnRenderThread(RtCallback rtCallback)265     static void runOnRenderThread(RtCallback rtCallback) {
266         TestTask task(rtCallback);
267         renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); });
268     }
269 
runOnRenderThreadUnmanaged(RtCallback rtCallback)270     static void runOnRenderThreadUnmanaged(RtCallback rtCallback) {
271         auto& rt = renderthread::RenderThread::getInstance();
272         rt.queue().runSync([&]() { rtCallback(rt); });
273     }
274 
275 
isRenderThreadRunning()276     static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); }
getRenderThreadTid()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:
operator()291         virtual status_t operator()(int what, void* data) {
292             mLastMode = what;
293             return DrawGlInfo::kStatusDone;
294         }
getLastMode()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 
expectOnRenderThread()313     static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
314 
createMockFunctor(RenderMode mode)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 
countsForFunctor(int functor)347     static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
348 
349 private:
350     static std::unordered_map<int, CallCounts> sMockFunctorCounts;
351 
syncHierarchyPropertiesAndDisplayListImpl(RenderNode * node)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