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