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 platform.
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_SET_SURFACE_SIZE |
53																										   eglu::NativeWindow::CAPABILITY_GET_SCREEN_SIZE);
54
55class NativeDisplay : public eglu::NativeDisplay
56{
57public:
58									NativeDisplay			(void) : eglu::NativeDisplay(DISPLAY_CAPABILITIES), m_library("libEGL.so") {}
59	virtual							~NativeDisplay			(void) {}
60
61	virtual EGLNativeDisplayType	getLegacyNative			(void)			{ return EGL_DEFAULT_DISPLAY;	}
62	virtual const eglw::Library&	getLibrary				(void) const	{ return m_library;				}
63
64private:
65	eglw::DefaultLibrary			m_library;
66};
67
68class NativeDisplayFactory : public eglu::NativeDisplayFactory
69{
70public:
71									NativeDisplayFactory	(WindowRegistry& windowRegistry);
72									~NativeDisplayFactory	(void) {}
73
74	virtual eglu::NativeDisplay*	createDisplay			(const EGLAttrib* attribList) const;
75};
76
77class NativeWindow : public eglu::NativeWindow
78{
79public:
80									NativeWindow			(Window* window, int width, int height, int32_t format);
81	virtual							~NativeWindow			(void);
82
83	virtual EGLNativeWindowType		getLegacyNative			(void)			{ return m_window->getNativeWindow();	}
84	IVec2							getScreenSize			(void) const	{ return m_window->getSize();			}
85
86	void							setSurfaceSize			(IVec2 size);
87
88	virtual void					processEvents			(void);
89
90private:
91	Window*							m_window;
92	int32_t							m_format;
93};
94
95class NativeWindowFactory : public eglu::NativeWindowFactory
96{
97public:
98									NativeWindowFactory		(WindowRegistry& windowRegistry);
99									~NativeWindowFactory	(void);
100
101	virtual eglu::NativeWindow*		createWindow			(eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const;
102	virtual eglu::NativeWindow*		createWindow			(eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, const eglu::WindowParams& params) const;
103
104private:
105	virtual eglu::NativeWindow*		createWindow			(const eglu::WindowParams& params, int32_t format) const;
106
107	WindowRegistry&					m_windowRegistry;
108};
109
110// NativeWindow
111
112NativeWindow::NativeWindow (Window* window, int width, int height, int32_t format)
113	: eglu::NativeWindow	(WINDOW_CAPABILITIES)
114	, m_window				(window)
115	, m_format				(format)
116{
117	// Set up buffers.
118	setSurfaceSize(IVec2(width, height));
119}
120
121NativeWindow::~NativeWindow (void)
122{
123	m_window->release();
124}
125
126void NativeWindow::processEvents (void)
127{
128	if (m_window->isPendingDestroy())
129		throw eglu::WindowDestroyedError("Window has been destroyed");
130}
131
132void NativeWindow::setSurfaceSize (tcu::IVec2 size)
133{
134	m_window->setBuffersGeometry(size.x() != eglu::WindowParams::SIZE_DONT_CARE ? size.x() : 0,
135								 size.y() != eglu::WindowParams::SIZE_DONT_CARE ? size.y() : 0,
136								 m_format);
137}
138
139// NativeWindowFactory
140
141NativeWindowFactory::NativeWindowFactory (WindowRegistry& windowRegistry)
142	: eglu::NativeWindowFactory	("default", "Default display", WINDOW_CAPABILITIES)
143	, m_windowRegistry			(windowRegistry)
144{
145}
146
147NativeWindowFactory::~NativeWindowFactory (void)
148{
149}
150
151eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const
152{
153	DE_UNREF(nativeDisplay);
154	return createWindow(params, WINDOW_FORMAT_RGBA_8888);
155}
156
157eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, const eglu::WindowParams& params) const
158{
159	const int32_t format = (int32_t)eglu::getConfigAttribInt(nativeDisplay->getLibrary(), display, config, EGL_NATIVE_VISUAL_ID);
160	DE_UNREF(nativeDisplay && attribList);
161	return createWindow(params, format);
162}
163
164eglu::NativeWindow* NativeWindowFactory::createWindow (const eglu::WindowParams& params, int32_t format) const
165{
166	Window* window = m_windowRegistry.tryAcquireWindow();
167
168	if (!window)
169		throw ResourceError("Native window is not available", DE_NULL, __FILE__, __LINE__);
170
171	return new NativeWindow(window, params.width, params.height, format);
172}
173
174// NativeDisplayFactory
175
176NativeDisplayFactory::NativeDisplayFactory (WindowRegistry& windowRegistry)
177	: eglu::NativeDisplayFactory("default", "Default display", DISPLAY_CAPABILITIES)
178{
179	m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(windowRegistry));
180}
181
182eglu::NativeDisplay* NativeDisplayFactory::createDisplay (const EGLAttrib* attribList) const
183{
184	DE_UNREF(attribList);
185	return new NativeDisplay();
186}
187
188// Vulkan
189
190class VulkanLibrary : public vk::Library
191{
192public:
193	VulkanLibrary (void)
194		: m_library	("libvulkan.so")
195		, m_driver	(m_library)
196	{
197	}
198
199	const vk::PlatformInterface& getPlatformInterface (void) const
200	{
201		return m_driver;
202	}
203
204private:
205	const tcu::DynamicFunctionLibrary	m_library;
206	const vk::PlatformDriver			m_driver;
207};
208
209DE_STATIC_ASSERT(sizeof(vk::pt::AndroidNativeWindowPtr) == sizeof(ANativeWindow*));
210
211class VulkanWindow : public vk::wsi::AndroidWindowInterface
212{
213public:
214	VulkanWindow (tcu::Android::Window& window)
215		: vk::wsi::AndroidWindowInterface	(vk::pt::AndroidNativeWindowPtr(window.getNativeWindow()))
216		, m_window							(window)
217	{
218	}
219
220	~VulkanWindow (void)
221	{
222		m_window.release();
223	}
224
225private:
226	tcu::Android::Window&	m_window;
227};
228
229class VulkanDisplay : public vk::wsi::Display
230{
231public:
232	VulkanDisplay (WindowRegistry& windowRegistry)
233		: m_windowRegistry(windowRegistry)
234	{
235	}
236
237	vk::wsi::Window* createWindow (const Maybe<UVec2>& initialSize) const
238	{
239		Window* const	window	= m_windowRegistry.tryAcquireWindow();
240
241		if (window)
242		{
243			try
244			{
245				if (initialSize)
246					window->setBuffersGeometry((int)initialSize->x(), (int)initialSize->y(), WINDOW_FORMAT_RGBA_8888);
247
248				return new VulkanWindow(*window);
249			}
250			catch (...)
251			{
252				window->release();
253				throw;
254			}
255		}
256		else
257			TCU_THROW(ResourceError, "Native window is not available");
258	}
259
260private:
261	WindowRegistry&		m_windowRegistry;
262};
263
264static size_t getTotalSystemMemory (ANativeActivity* activity)
265{
266	const size_t	MiB		= (size_t)(1<<20);
267
268	try
269	{
270		const size_t	cddRequiredSize	= getCDDRequiredSystemMemory(activity);
271
272		print("Device has at least %.2f MiB total system memory per Android CDD\n", double(cddRequiredSize) / double(MiB));
273
274		return cddRequiredSize;
275	}
276	catch (const std::exception& e)
277	{
278		// Use relatively high fallback size to encourage CDD-compliant behavior
279		const size_t	fallbackSize	= (sizeof(void*) == sizeof(deUint64)) ? 2048*MiB : 1024*MiB;
280
281		print("WARNING: Failed to determine system memory size required by CDD: %s\n", e.what());
282		print("WARNING: Using fall-back size of %.2f MiB\n", double(fallbackSize) / double(MiB));
283
284		return fallbackSize;
285	}
286}
287
288// Platform
289
290Platform::Platform (NativeActivity& activity)
291	: m_activity			(activity)
292	, m_totalSystemMemory	(getTotalSystemMemory(activity.getNativeActivity()))
293{
294	m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry));
295	m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry));
296}
297
298Platform::~Platform (void)
299{
300}
301
302bool Platform::processEvents (void)
303{
304	m_windowRegistry.garbageCollect();
305	return true;
306}
307
308vk::Library* Platform::createLibrary (void) const
309{
310	return new VulkanLibrary();
311}
312
313void Platform::describePlatform (std::ostream& dst) const
314{
315	tcu::Android::describePlatform(m_activity.getNativeActivity(), dst);
316}
317
318void Platform::getMemoryLimits (vk::PlatformMemoryLimits& limits) const
319{
320	// Worst-case estimates
321	const size_t	MiB				= (size_t)(1<<20);
322	const size_t	baseMemUsage	= 400*MiB;
323	const double	safeUsageRatio	= 0.25;
324
325	limits.totalSystemMemory					= de::max((size_t)(double(deInt64(m_totalSystemMemory)-deInt64(baseMemUsage)) * safeUsageRatio), 16*MiB);
326
327	// Assume UMA architecture
328	limits.totalDeviceLocalMemory				= 0;
329
330	// Reasonable worst-case estimates
331	limits.deviceMemoryAllocationGranularity	= 64*1024;
332	limits.devicePageSize						= 4096;
333	limits.devicePageTableEntrySize				= 8;
334	limits.devicePageTableHierarchyLevels		= 3;
335}
336
337vk::wsi::Display* Platform::createWsiDisplay (vk::wsi::Type wsiType) const
338{
339	if (wsiType == vk::wsi::TYPE_ANDROID)
340		return new VulkanDisplay(const_cast<WindowRegistry&>(m_windowRegistry));
341	else
342		TCU_THROW(NotSupportedError, "WSI type not supported on Android");
343}
344
345} // Android
346} // tcu
347