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