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>
38struct egl_native_pixmap_t;
39DE_STATIC_ASSERT(sizeof(eglw::EGLNativeDisplayType) == sizeof(void*));
40DE_STATIC_ASSERT(sizeof(eglw::EGLNativePixmapType) == sizeof(struct egl_native_pixmap_t*));
41DE_STATIC_ASSERT(sizeof(eglw::EGLNativeWindowType) == sizeof(ANativeWindow*));
42
43namespace tcu
44{
45namespace Android
46{
47
48using namespace eglw;
49
50static const eglu::NativeDisplay::Capability	DISPLAY_CAPABILITIES	= eglu::NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY;
51static const eglu::NativeWindow::Capability		WINDOW_CAPABILITIES		= (eglu::NativeWindow::Capability)(eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY |
52																										   eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_PLATFORM |
53																										   eglu::NativeWindow::CAPABILITY_CREATE_SURFACE_PLATFORM_EXTENSION |
54																										   eglu::NativeWindow::CAPABILITY_SET_SURFACE_SIZE |
55																										   eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE);
56
57class NativeDisplay : public eglu::NativeDisplay
58{
59public:
60									NativeDisplay			(void) : eglu::NativeDisplay(DISPLAY_CAPABILITIES), m_library("libEGL.so") {}
61	virtual							~NativeDisplay			(void) {}
62
63	virtual EGLNativeDisplayType	getLegacyNative			(void)			{ return EGL_DEFAULT_DISPLAY;	}
64	virtual const eglw::Library&	getLibrary				(void) const	{ return m_library;				}
65
66private:
67	eglw::DefaultLibrary			m_library;
68};
69
70class NativeDisplayFactory : public eglu::NativeDisplayFactory
71{
72public:
73									NativeDisplayFactory	(WindowRegistry& windowRegistry);
74									~NativeDisplayFactory	(void) {}
75
76	virtual eglu::NativeDisplay*	createDisplay			(const EGLAttrib* attribList) const;
77};
78
79class NativeWindow : public eglu::NativeWindow
80{
81public:
82									NativeWindow			(Window* window, int width, int height, int32_t format);
83	virtual							~NativeWindow			(void);
84
85	virtual EGLNativeWindowType		getLegacyNative			(void)			{ return m_window->getNativeWindow();	}
86	virtual EGLNativeWindowType		getPlatformExtension	(void)			{ return m_window->getNativeWindow();	}
87	virtual EGLNativeWindowType		getPlatformNative		(void)			{ return m_window->getNativeWindow();	}
88	IVec2							getScreenSize			(void) const	{ return m_window->getSize();			}
89
90	void							setSurfaceSize			(IVec2 size);
91
92	virtual void					processEvents			(void);
93
94private:
95	Window*							m_window;
96	int32_t							m_format;
97};
98
99class NativeWindowFactory : public eglu::NativeWindowFactory
100{
101public:
102									NativeWindowFactory		(WindowRegistry& windowRegistry);
103									~NativeWindowFactory	(void);
104
105	virtual eglu::NativeWindow*		createWindow			(eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const;
106	virtual eglu::NativeWindow*		createWindow			(eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, const eglu::WindowParams& params) const;
107
108private:
109	virtual eglu::NativeWindow*		createWindow			(const eglu::WindowParams& params, int32_t format) const;
110
111	WindowRegistry&					m_windowRegistry;
112};
113
114// NativeWindow
115
116NativeWindow::NativeWindow (Window* window, int width, int height, int32_t format)
117	: eglu::NativeWindow	(WINDOW_CAPABILITIES)
118	, m_window				(window)
119	, m_format				(format)
120{
121	// Set up buffers.
122	setSurfaceSize(IVec2(width, height));
123}
124
125NativeWindow::~NativeWindow (void)
126{
127	m_window->release();
128}
129
130void NativeWindow::processEvents (void)
131{
132	if (m_window->isPendingDestroy())
133		throw eglu::WindowDestroyedError("Window has been destroyed");
134}
135
136void NativeWindow::setSurfaceSize (tcu::IVec2 size)
137{
138	m_window->setBuffersGeometry(size.x() != eglu::WindowParams::SIZE_DONT_CARE ? size.x() : 0,
139								 size.y() != eglu::WindowParams::SIZE_DONT_CARE ? size.y() : 0,
140								 m_format);
141}
142
143// NativeWindowFactory
144
145NativeWindowFactory::NativeWindowFactory (WindowRegistry& windowRegistry)
146	: eglu::NativeWindowFactory	("default", "Default display", WINDOW_CAPABILITIES)
147	, m_windowRegistry			(windowRegistry)
148{
149}
150
151NativeWindowFactory::~NativeWindowFactory (void)
152{
153}
154
155eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const
156{
157	DE_UNREF(nativeDisplay);
158	return createWindow(params, WINDOW_FORMAT_RGBA_8888);
159}
160
161eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, const eglu::WindowParams& params) const
162{
163	const int32_t format = (int32_t)eglu::getConfigAttribInt(nativeDisplay->getLibrary(), display, config, EGL_NATIVE_VISUAL_ID);
164	DE_UNREF(nativeDisplay && attribList);
165	return createWindow(params, format);
166}
167
168eglu::NativeWindow* NativeWindowFactory::createWindow (const eglu::WindowParams& params, int32_t format) const
169{
170	Window* window = m_windowRegistry.tryAcquireWindow();
171
172	if (!window)
173		throw ResourceError("Native window is not available", DE_NULL, __FILE__, __LINE__);
174
175	return new NativeWindow(window, params.width, params.height, format);
176}
177
178// NativeDisplayFactory
179
180NativeDisplayFactory::NativeDisplayFactory (WindowRegistry& windowRegistry)
181	: eglu::NativeDisplayFactory("default", "Default display", DISPLAY_CAPABILITIES)
182{
183	m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(windowRegistry));
184}
185
186eglu::NativeDisplay* NativeDisplayFactory::createDisplay (const EGLAttrib* attribList) const
187{
188	DE_UNREF(attribList);
189	return new NativeDisplay();
190}
191
192// Vulkan
193
194class VulkanLibrary : public vk::Library
195{
196public:
197	VulkanLibrary (const char* libraryPath)
198		: m_library	(libraryPath != DE_NULL ? libraryPath : "libvulkan.so")
199		, m_driver	(m_library)
200	{
201	}
202
203	const vk::PlatformInterface& getPlatformInterface		(void) const
204	{
205		return m_driver;
206	}
207
208	const tcu::FunctionLibrary&		getFunctionLibrary		(void) const
209	{
210		return m_library;
211	}
212
213private:
214	const tcu::DynamicFunctionLibrary	m_library;
215	const vk::PlatformDriver			m_driver;
216};
217
218DE_STATIC_ASSERT(sizeof(vk::pt::AndroidNativeWindowPtr) == sizeof(ANativeWindow*));
219
220class VulkanWindow : public vk::wsi::AndroidWindowInterface
221{
222public:
223	VulkanWindow (tcu::Android::Window& window)
224		: vk::wsi::AndroidWindowInterface	(vk::pt::AndroidNativeWindowPtr(window.getNativeWindow()))
225		, m_window							(window)
226	{
227	}
228
229	void setVisible(bool visible)
230	{
231		DE_UNREF(visible);
232	}
233
234	void resize(const UVec2& newSize)
235	{
236		DE_UNREF(newSize);
237	}
238
239	void setMinimized(bool minimized)
240	{
241		DE_UNREF(minimized);
242		TCU_THROW(NotSupportedError, "Minimized on Android is not implemented");
243	}
244
245	~VulkanWindow (void)
246	{
247		m_window.release();
248	}
249
250private:
251	tcu::Android::Window&	m_window;
252};
253
254class VulkanDisplay : public vk::wsi::Display
255{
256public:
257	VulkanDisplay (WindowRegistry& windowRegistry)
258		: m_windowRegistry(windowRegistry)
259	{
260	}
261
262	vk::wsi::Window* createWindow (const Maybe<UVec2>& initialSize) const
263	{
264		Window* const	window	= m_windowRegistry.tryAcquireWindow();
265
266		if (window)
267		{
268			try
269			{
270				if (initialSize)
271					window->setBuffersGeometry((int)initialSize->x(), (int)initialSize->y(), WINDOW_FORMAT_RGBA_8888);
272
273				return new VulkanWindow(*window);
274			}
275			catch (...)
276			{
277				window->release();
278				throw;
279			}
280		}
281		else
282			TCU_THROW(ResourceError, "Native window is not available");
283	}
284
285private:
286	WindowRegistry&		m_windowRegistry;
287};
288
289static size_t getTotalSystemMemory (ANativeActivity* activity)
290{
291	const size_t	MiB		= (size_t)(1<<20);
292
293	try
294	{
295		const size_t totalMemory = getTotalAndroidSystemMemory(activity);
296		print("Device has %.2f MiB of system memory\n", static_cast<double>(totalMemory) / static_cast<double>(MiB));
297		return totalMemory;
298	}
299	catch (const std::exception& e)
300	{
301		// Use relatively high fallback size to encourage CDD-compliant behavior
302		const size_t	fallbackSize	= (sizeof(void*) == sizeof(deUint64)) ? 2048*MiB : 1024*MiB;
303
304		print("WARNING: Failed to determine system memory size required by CDD: %s\n", e.what());
305		print("WARNING: Using fall-back size of %.2f MiB\n", double(fallbackSize) / double(MiB));
306
307		return fallbackSize;
308	}
309}
310
311// Platform
312
313Platform::Platform (NativeActivity& activity)
314	: m_activity			(activity)
315	, m_totalSystemMemory	(getTotalSystemMemory(activity.getNativeActivity()))
316{
317	m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry));
318	m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry));
319}
320
321Platform::~Platform (void)
322{
323}
324
325bool Platform::processEvents (void)
326{
327	m_windowRegistry.garbageCollect();
328	return true;
329}
330
331vk::Library* Platform::createLibrary (const char* libraryPath) const
332{
333	return new VulkanLibrary(libraryPath);
334}
335
336void Platform::describePlatform (std::ostream& dst) const
337{
338	tcu::Android::describePlatform(m_activity.getNativeActivity(), dst);
339}
340
341void Platform::getMemoryLimits (tcu::PlatformMemoryLimits& limits) const
342{
343	// Worst-case estimates
344	const size_t	MiB				= (size_t)(1<<20);
345	const size_t	baseMemUsage	= 400*MiB;
346
347#if (DE_PTR_SIZE == 4)
348	// Some tests, such as:
349	//
350	// dEQP-VK.api.object_management.max_concurrent.*
351	// dEQP-VK.memory.allocation.random.*
352	//
353	// when run in succession, can lead to system memory fragmentation. It depends on the allocator, and on some 32-bit
354	// systems can lead to out of memory errors. As a workaround, we use a smaller amount of memory on 32-bit systems,
355	// as this typically avoids out of memory errors caused by fragmentation.
356	const double	safeUsageRatio	= 0.1;
357#else
358	const double	safeUsageRatio	= 0.25;
359#endif
360
361	limits.totalSystemMemory					= de::max((size_t)(double(deInt64(m_totalSystemMemory)-deInt64(baseMemUsage)) * safeUsageRatio), 16*MiB);
362
363	// Assume UMA architecture
364	limits.totalDeviceLocalMemory				= 0;
365
366	// Reasonable worst-case estimates
367	limits.deviceMemoryAllocationGranularity	= 64*1024;
368	limits.devicePageSize						= 4096;
369	limits.devicePageTableEntrySize				= 8;
370	limits.devicePageTableHierarchyLevels		= 3;
371}
372
373vk::wsi::Display* Platform::createWsiDisplay (vk::wsi::Type wsiType) const
374{
375	if (wsiType == vk::wsi::TYPE_ANDROID)
376		return new VulkanDisplay(const_cast<WindowRegistry&>(m_windowRegistry));
377	else
378		TCU_THROW(NotSupportedError, "WSI type not supported on Android");
379}
380
381bool Platform::hasDisplay (vk::wsi::Type wsiType) const
382{
383	if (wsiType == vk::wsi::TYPE_ANDROID)
384		return true;
385
386	return false;
387}
388
389} // Android
390} // tcu
391