1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Android EGL and Vulkan platforms.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuAndroidPlatform.hpp"
25 #include "tcuAndroidUtil.hpp"
26 #include "gluRenderContext.hpp"
27 #include "egluNativeDisplay.hpp"
28 #include "egluNativeWindow.hpp"
29 #include "egluGLContextFactory.hpp"
30 #include "egluUtil.hpp"
31 #include "eglwLibrary.hpp"
32 #include "eglwEnums.hpp"
33 #include "tcuFunctionLibrary.hpp"
34 #include "vkWsiPlatform.hpp"
35
36 // Assume no call translation is needed
37 #include <android/native_window.h>
38 struct egl_native_pixmap_t;
39 DE_STATIC_ASSERT(sizeof(eglw::EGLNativeDisplayType) == sizeof(void *));
40 DE_STATIC_ASSERT(sizeof(eglw::EGLNativePixmapType) == sizeof(struct egl_native_pixmap_t *));
41 DE_STATIC_ASSERT(sizeof(eglw::EGLNativeWindowType) == sizeof(ANativeWindow *));
42
43 namespace tcu
44 {
45 namespace Android
46 {
47
48 using namespace eglw;
49
50 static const eglu::NativeDisplay::Capability DISPLAY_CAPABILITIES = eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY;
51 static const eglu::NativeWindow::Capability WINDOW_CAPABILITIES = (eglu::NativeWindow::Capability)(
52 eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY | eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_PLATFORM |
53 eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_PLATFORM_EXTENSION | eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE |
54 eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE);
55
56 class NativeDisplay : public eglu::NativeDisplay
57 {
58 public:
NativeDisplay(void)59 NativeDisplay(void) : eglu::NativeDisplay(DISPLAY_CAPABILITIES), m_library("libEGL.so")
60 {
61 }
~NativeDisplay(void)62 virtual ~NativeDisplay(void)
63 {
64 }
65
getLegacyNative(void)66 virtual EGLNativeDisplayType getLegacyNative(void)
67 {
68 return EGL_DEFAULT_DISPLAY;
69 }
getLibrary(void) const70 virtual const eglw::Library &getLibrary(void) const
71 {
72 return m_library;
73 }
74
75 private:
76 eglw::DefaultLibrary m_library;
77 };
78
79 class NativeDisplayFactory : public eglu::NativeDisplayFactory
80 {
81 public:
82 NativeDisplayFactory(WindowRegistry &windowRegistry);
~NativeDisplayFactory(void)83 ~NativeDisplayFactory(void)
84 {
85 }
86
87 virtual eglu::NativeDisplay *createDisplay(const EGLAttrib *attribList) const;
88 };
89
90 class NativeWindow : public eglu::NativeWindow
91 {
92 public:
93 NativeWindow(Window *window, int width, int height, int32_t format);
94 virtual ~NativeWindow(void);
95
getLegacyNative(void)96 virtual EGLNativeWindowType getLegacyNative(void)
97 {
98 return m_window->getNativeWindow();
99 }
getPlatformExtension(void)100 virtual EGLNativeWindowType getPlatformExtension(void)
101 {
102 return m_window->getNativeWindow();
103 }
getPlatformNative(void)104 virtual EGLNativeWindowType getPlatformNative(void)
105 {
106 return m_window->getNativeWindow();
107 }
getScreenSize(void) const108 IVec2 getScreenSize(void) const
109 {
110 return m_window->getSize();
111 }
112
113 void setSurfaceSize(IVec2 size);
114
115 virtual void processEvents(void);
116
117 private:
118 Window *m_window;
119 int32_t m_format;
120 };
121
122 class NativeWindowFactory : public eglu::NativeWindowFactory
123 {
124 public:
125 NativeWindowFactory(WindowRegistry &windowRegistry);
126 ~NativeWindowFactory(void);
127
128 virtual eglu::NativeWindow *createWindow(eglu::NativeDisplay *nativeDisplay,
129 const eglu::WindowParams ¶ms) const;
130 virtual eglu::NativeWindow *createWindow(eglu::NativeDisplay *nativeDisplay, EGLDisplay display, EGLConfig config,
131 const EGLAttrib *attribList, const eglu::WindowParams ¶ms) const;
132
133 private:
134 virtual eglu::NativeWindow *createWindow(const eglu::WindowParams ¶ms, int32_t format) const;
135
136 WindowRegistry &m_windowRegistry;
137 };
138
139 // NativeWindow
140
NativeWindow(Window * window,int width,int height,int32_t format)141 NativeWindow::NativeWindow(Window *window, int width, int height, int32_t format)
142 : eglu::NativeWindow(WINDOW_CAPABILITIES)
143 , m_window(window)
144 , m_format(format)
145 {
146 // Set up buffers.
147 setSurfaceSize(IVec2(width, height));
148 }
149
~NativeWindow(void)150 NativeWindow::~NativeWindow(void)
151 {
152 m_window->release();
153 }
154
processEvents(void)155 void NativeWindow::processEvents(void)
156 {
157 if (m_window->isPendingDestroy())
158 throw eglu::WindowDestroyedError("Window has been destroyed");
159 }
160
setSurfaceSize(tcu::IVec2 size)161 void NativeWindow::setSurfaceSize(tcu::IVec2 size)
162 {
163 m_window->setBuffersGeometry(size.x() != eglu::WindowParams::SIZE_DONT_CARE ? size.x() : 0,
164 size.y() != eglu::WindowParams::SIZE_DONT_CARE ? size.y() : 0, m_format);
165 }
166
167 // NativeWindowFactory
168
NativeWindowFactory(WindowRegistry & windowRegistry)169 NativeWindowFactory::NativeWindowFactory(WindowRegistry &windowRegistry)
170 : eglu::NativeWindowFactory("default", "Default display", WINDOW_CAPABILITIES)
171 , m_windowRegistry(windowRegistry)
172 {
173 }
174
~NativeWindowFactory(void)175 NativeWindowFactory::~NativeWindowFactory(void)
176 {
177 }
178
createWindow(eglu::NativeDisplay * nativeDisplay,const eglu::WindowParams & params) const179 eglu::NativeWindow *NativeWindowFactory::createWindow(eglu::NativeDisplay *nativeDisplay,
180 const eglu::WindowParams ¶ms) const
181 {
182 DE_UNREF(nativeDisplay);
183 return createWindow(params, WINDOW_FORMAT_RGBA_8888);
184 }
185
createWindow(eglu::NativeDisplay * nativeDisplay,EGLDisplay display,EGLConfig config,const EGLAttrib * attribList,const eglu::WindowParams & params) const186 eglu::NativeWindow *NativeWindowFactory::createWindow(eglu::NativeDisplay *nativeDisplay, EGLDisplay display,
187 EGLConfig config, const EGLAttrib *attribList,
188 const eglu::WindowParams ¶ms) const
189 {
190 const int32_t format =
191 (int32_t)eglu::getConfigAttribInt(nativeDisplay->getLibrary(), display, config, EGL_NATIVE_VISUAL_ID);
192 DE_UNREF(nativeDisplay && attribList);
193 return createWindow(params, format);
194 }
195
createWindow(const eglu::WindowParams & params,int32_t format) const196 eglu::NativeWindow *NativeWindowFactory::createWindow(const eglu::WindowParams ¶ms, int32_t format) const
197 {
198 Window *window = m_windowRegistry.tryAcquireWindow();
199
200 if (!window)
201 throw ResourceError("Native window is not available", DE_NULL, __FILE__, __LINE__);
202
203 return new NativeWindow(window, params.width, params.height, format);
204 }
205
206 // NativeDisplayFactory
207
NativeDisplayFactory(WindowRegistry & windowRegistry)208 NativeDisplayFactory::NativeDisplayFactory(WindowRegistry &windowRegistry)
209 : eglu::NativeDisplayFactory("default", "Default display", DISPLAY_CAPABILITIES)
210 {
211 m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(windowRegistry));
212 }
213
createDisplay(const EGLAttrib * attribList) const214 eglu::NativeDisplay *NativeDisplayFactory::createDisplay(const EGLAttrib *attribList) const
215 {
216 DE_UNREF(attribList);
217 return new NativeDisplay();
218 }
219
220 // Vulkan
221
222 class VulkanLibrary : public vk::Library
223 {
224 public:
VulkanLibrary(const char * libraryPath)225 VulkanLibrary(const char *libraryPath)
226 : m_library(libraryPath != DE_NULL ? libraryPath : "libvulkan.so")
227 , m_driver(m_library)
228 {
229 }
230
getPlatformInterface(void) const231 const vk::PlatformInterface &getPlatformInterface(void) const
232 {
233 return m_driver;
234 }
235
getFunctionLibrary(void) const236 const tcu::FunctionLibrary &getFunctionLibrary(void) const
237 {
238 return m_library;
239 }
240
241 private:
242 const tcu::DynamicFunctionLibrary m_library;
243 const vk::PlatformDriver m_driver;
244 };
245
246 DE_STATIC_ASSERT(sizeof(vk::pt::AndroidNativeWindowPtr) == sizeof(ANativeWindow *));
247
248 class VulkanWindow : public vk::wsi::AndroidWindowInterface
249 {
250 public:
VulkanWindow(tcu::Android::Window & window)251 VulkanWindow(tcu::Android::Window &window)
252 : vk::wsi::AndroidWindowInterface(vk::pt::AndroidNativeWindowPtr(window.getNativeWindow()))
253 , m_window(window)
254 {
255 }
256
setVisible(bool visible)257 void setVisible(bool visible)
258 {
259 DE_UNREF(visible);
260 }
261
resize(const UVec2 & newSize)262 void resize(const UVec2 &newSize)
263 {
264 DE_UNREF(newSize);
265 }
266
setMinimized(bool minimized)267 void setMinimized(bool minimized)
268 {
269 DE_UNREF(minimized);
270 TCU_THROW(NotSupportedError, "Minimized on Android is not implemented");
271 }
272
~VulkanWindow(void)273 ~VulkanWindow(void)
274 {
275 m_window.release();
276 }
277
278 private:
279 tcu::Android::Window &m_window;
280 };
281
282 class VulkanDisplay : public vk::wsi::Display
283 {
284 public:
VulkanDisplay(WindowRegistry & windowRegistry)285 VulkanDisplay(WindowRegistry &windowRegistry) : m_windowRegistry(windowRegistry)
286 {
287 }
288
createWindow(const Maybe<UVec2> & initialSize) const289 vk::wsi::Window *createWindow(const Maybe<UVec2> &initialSize) const
290 {
291 Window *const window = m_windowRegistry.tryAcquireWindow();
292
293 if (window)
294 {
295 try
296 {
297 if (initialSize)
298 window->setBuffersGeometry((int)initialSize->x(), (int)initialSize->y(), WINDOW_FORMAT_RGBA_8888);
299
300 return new VulkanWindow(*window);
301 }
302 catch (...)
303 {
304 window->release();
305 throw;
306 }
307 }
308 else
309 TCU_THROW(ResourceError, "Native window is not available");
310 }
311
312 private:
313 WindowRegistry &m_windowRegistry;
314 };
315
getTotalSystemMemory(ANativeActivity * activity)316 static size_t getTotalSystemMemory(ANativeActivity *activity)
317 {
318 const size_t MiB = (size_t)(1 << 20);
319
320 try
321 {
322 const size_t totalMemory = getTotalAndroidSystemMemory(activity);
323 print("Device has %.2f MiB of system memory\n", static_cast<double>(totalMemory) / static_cast<double>(MiB));
324 return totalMemory;
325 }
326 catch (const std::exception &e)
327 {
328 // Use relatively high fallback size to encourage CDD-compliant behavior
329 const size_t fallbackSize = (sizeof(void *) == sizeof(uint64_t)) ? 2048 * MiB : 1024 * MiB;
330
331 print("WARNING: Failed to determine system memory size required by CDD: %s\n", e.what());
332 print("WARNING: Using fall-back size of %.2f MiB\n", double(fallbackSize) / double(MiB));
333
334 return fallbackSize;
335 }
336 }
337
338 // Platform
339
Platform(NativeActivity & activity)340 Platform::Platform(NativeActivity &activity)
341 : m_activity(activity)
342 , m_totalSystemMemory(getTotalSystemMemory(activity.getNativeActivity()))
343 {
344 m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry));
345 m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry));
346 }
347
~Platform(void)348 Platform::~Platform(void)
349 {
350 }
351
processEvents(void)352 bool Platform::processEvents(void)
353 {
354 m_windowRegistry.garbageCollect();
355 return true;
356 }
357
createLibrary(const char * libraryPath) const358 vk::Library *Platform::createLibrary(const char *libraryPath) const
359 {
360 return new VulkanLibrary(libraryPath);
361 }
362
describePlatform(std::ostream & dst) const363 void Platform::describePlatform(std::ostream &dst) const
364 {
365 tcu::Android::describePlatform(m_activity.getNativeActivity(), dst);
366 }
367
getMemoryLimits(tcu::PlatformMemoryLimits & limits) const368 void Platform::getMemoryLimits(tcu::PlatformMemoryLimits &limits) const
369 {
370 // Worst-case estimates
371 const size_t MiB = (size_t)(1 << 20);
372 const size_t baseMemUsage = 400 * MiB;
373
374 #if (DE_PTR_SIZE == 4)
375 // Some tests, such as:
376 //
377 // dEQP-VK.api.object_management.max_concurrent.*
378 // dEQP-VK.memory.allocation.random.*
379 //
380 // when run in succession, can lead to system memory fragmentation. It depends on the allocator, and on some 32-bit
381 // systems can lead to out of memory errors. As a workaround, we use a smaller amount of memory on 32-bit systems,
382 // as this typically avoids out of memory errors caused by fragmentation.
383 const double safeUsageRatio = 0.1;
384 #else
385 const double safeUsageRatio = 0.25;
386 #endif
387
388 limits.totalSystemMemory =
389 de::max((size_t)(double(int64_t(m_totalSystemMemory) - int64_t(baseMemUsage)) * safeUsageRatio), 16 * MiB);
390
391 // Assume UMA architecture
392 limits.totalDeviceLocalMemory = 0;
393
394 // Reasonable worst-case estimates
395 limits.deviceMemoryAllocationGranularity = 64 * 1024;
396 limits.devicePageSize = 4096;
397 limits.devicePageTableEntrySize = 8;
398 limits.devicePageTableHierarchyLevels = 3;
399 }
400
createWsiDisplay(vk::wsi::Type wsiType) const401 vk::wsi::Display *Platform::createWsiDisplay(vk::wsi::Type wsiType) const
402 {
403 if (wsiType == vk::wsi::TYPE_ANDROID)
404 return new VulkanDisplay(const_cast<WindowRegistry &>(m_windowRegistry));
405 else
406 TCU_THROW(NotSupportedError, "WSI type not supported on Android");
407 }
408
hasDisplay(vk::wsi::Type wsiType) const409 bool Platform::hasDisplay(vk::wsi::Type wsiType) const
410 {
411 if (wsiType == vk::wsi::TYPE_ANDROID)
412 return true;
413
414 return false;
415 }
416
417 } // namespace Android
418 } // namespace tcu
419