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