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 iOS Platform implementation.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuIOSPlatform.hh"
25#include "gluRenderConfig.hpp"
26#include "gluFboRenderContext.hpp"
27
28#include "glwInitES20Direct.hpp"
29#include "glwInitES30Direct.hpp"
30
31
32namespace tcu
33{
34namespace ios
35{
36
37// ScreenManager
38
39ScreenManager::ScreenManager (tcuEAGLView* view)
40	: m_view(view)
41{
42}
43
44ScreenManager::~ScreenManager (void)
45{
46}
47
48CAEAGLLayer* ScreenManager::acquireScreen (void)
49{
50	if (!m_viewLock.tryLock())
51		throw ResourceError("View is already is in use");
52
53	return [m_view getEAGLLayer];
54}
55
56void ScreenManager::releaseScreen (CAEAGLLayer* layer)
57{
58	DE_UNREF(layer);
59	m_viewLock.unlock();
60}
61
62// ContextFactory
63
64ContextFactory::ContextFactory (ScreenManager* screenManager)
65	: glu::ContextFactory	("eagl", "iOS EAGL Context")
66	, m_screenManager		(screenManager)
67{
68}
69
70ContextFactory::~ContextFactory (void)
71{
72}
73
74glu::RenderContext* ContextFactory::createContext (const glu::RenderConfig& config, const tcu::CommandLine&) const
75{
76	RawContext* rawContext = new RawContext(config.type);
77
78	try
79	{
80		if (config.surfaceType == glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC)
81			return new glu::FboRenderContext(rawContext, config);
82		else if (config.surfaceType == glu::RenderConfig::SURFACETYPE_WINDOW)
83			return new ScreenContext(m_screenManager, config);
84		else
85			throw NotSupportedError("Unsupported surface type");
86	}
87	catch (...)
88	{
89		delete rawContext;
90		throw;
91	}
92}
93
94// Platform
95
96Platform::Platform (ScreenManager* screenManager)
97{
98	m_contextFactoryRegistry.registerFactory(new ContextFactory(screenManager));
99}
100
101Platform::~Platform (void)
102{
103}
104
105// RawContext
106
107static EAGLRenderingAPI getEAGLApi (glu::ContextType type)
108{
109	if (type.getAPI() == glu::ApiType::es(3,0))
110		return kEAGLRenderingAPIOpenGLES3;
111	else if (type.getAPI() == glu::ApiType::es(2,0))
112		return kEAGLRenderingAPIOpenGLES2;
113	else
114		throw NotSupportedError("Requested GL API is not supported on iOS");
115}
116
117RawContext::RawContext (glu::ContextType type)
118	: m_type		(type)
119	, m_context		(DE_NULL)
120	, m_emptyTarget	(0, 0, tcu::PixelFormat(0,0,0,0), 0, 0, 0)
121{
122	const EAGLRenderingAPI eaglApi = getEAGLApi(type);
123
124	m_context = [[EAGLContext alloc] initWithAPI:eaglApi];
125	if (!m_context)
126		throw ResourceError("Failed to create EAGL context");
127
128	try
129	{
130		if (![EAGLContext setCurrentContext:m_context])
131			throw ResourceError("Failed to set current EAGL context");
132
133		if (type.getAPI() == glu::ApiType::es(3,0))
134			glw::initES30Direct(&m_functions);
135		else if (type.getAPI() == glu::ApiType::es(2,0))
136			glw::initES20Direct(&m_functions);
137		else
138			throw InternalError("Unsupproted API for loading functions");
139	}
140	catch (...)
141	{
142		if ([EAGLContext currentContext] == m_context)
143			[EAGLContext setCurrentContext:nil];
144
145		[m_context release];
146		throw;
147	}
148}
149
150RawContext::~RawContext (void)
151{
152	if ([EAGLContext currentContext] == m_context)
153		[EAGLContext setCurrentContext:nil];
154
155	[m_context release];
156}
157
158void RawContext::postIterate (void)
159{
160}
161
162NSString* chooseLayerColorFormat (const glu::RenderConfig& config)
163{
164	const bool	cr		= config.redBits	!= glu::RenderConfig::DONT_CARE;
165	const bool	cg		= config.greenBits	!= glu::RenderConfig::DONT_CARE;
166	const bool	cb		= config.blueBits	!= glu::RenderConfig::DONT_CARE;
167	const bool	ca		= config.alphaBits	!= glu::RenderConfig::DONT_CARE;
168
169	if ((!cr || config.redBits		== 8) &&
170		(!cg || config.greenBits	== 8) &&
171		(!cb || config.blueBits		== 8) &&
172		(!ca || config.alphaBits	== 8))
173		return kEAGLColorFormatRGBA8;
174
175	if ((!cr || config.redBits		== 5) &&
176		(!cg || config.greenBits	== 6) &&
177		(!cb || config.blueBits		== 5) &&
178		(!ca || config.alphaBits	== 0))
179		return kEAGLColorFormatRGB565;
180
181	return nil;
182}
183
184// ScreenContext
185
186ScreenContext::ScreenContext (ScreenManager* screenManager, const glu::RenderConfig& config)
187	: RawContext			(config.type)
188	, m_screenManager		(screenManager)
189	, m_layer				(DE_NULL)
190	, m_framebuffer			(*this) // \note Perfectly safe to give reference to this RC as everything except postIterate() works at this point.
191	, m_colorBuffer			(*this)
192	, m_depthStencilBuffer	(*this)
193{
194	m_layer = m_screenManager->acquireScreen();
195	try
196	{
197		createFramebuffer(config);
198	}
199	catch (...)
200	{
201		m_screenManager->releaseScreen(m_layer);
202		throw;
203	}
204}
205
206ScreenContext::~ScreenContext (void)
207{
208	m_screenManager->releaseScreen(m_layer);
209}
210
211void ScreenContext::createFramebuffer (const glu::RenderConfig& config)
212{
213	const glw::Functions&	gl					= getFunctions();
214	const NSString* const	colorFormat			= chooseLayerColorFormat(config);
215	const deUint32			depthStencilFormat	= chooseDepthStencilFormat(config);
216	tcu::PixelFormat		pixelFormat;
217	int						width				= 0;
218	int						height				= 0;
219	int						depthBits			= 0;
220	int						stencilBits			= 0;
221
222	if (config.numSamples > 0)
223		throw NotSupportedError("Multisample config is not supported");
224
225	if (colorFormat == nil)
226		throw NotSupportedError("Unsupported color attachment format");
227
228	if ((config.depthBits > 0 || config.stencilBits > 0) && depthStencilFormat == 0)
229		throw NotSupportedError("Unsupported depth & stencil attachment format");
230
231	m_layer.opaque = TRUE;
232	m_layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
233								  colorFormat, kEAGLDrawablePropertyColorFormat,
234								  [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,
235								  nil];
236
237	gl.bindRenderbuffer(GL_RENDERBUFFER, *m_colorBuffer);
238	if (![getEAGLContext() renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)m_layer])
239		throw ResourceError("Failed to allocate color renderbuffer");
240	GLU_EXPECT_NO_ERROR(gl.getError(), "Creating color renderbuffer");
241
242	gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,		&width);
243	gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT,		&height);
244	gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_RED_SIZE,	&pixelFormat.redBits);
245	gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_GREEN_SIZE,	&pixelFormat.greenBits);
246	gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_BLUE_SIZE,	&pixelFormat.blueBits);
247	gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE,	&pixelFormat.alphaBits);
248	GLU_EXPECT_NO_ERROR(gl.getError(), "Querying surface size failed");
249
250	if (depthStencilFormat != 0)
251	{
252		gl.bindRenderbuffer(GL_RENDERBUFFER, *m_depthStencilBuffer);
253		gl.renderbufferStorage(GL_RENDERBUFFER, depthStencilFormat, width, height);
254
255		gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_DEPTH_SIZE,		&depthBits);
256		gl.getRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_STENCIL_SIZE,	&stencilBits);
257
258		GLU_EXPECT_NO_ERROR(gl.getError(), "Creating depth / stencil renderbuffer");
259	}
260
261	gl.bindFramebuffer(GL_FRAMEBUFFER, *m_framebuffer);
262	gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *m_colorBuffer);
263
264	if (depthStencilFormat != 0)
265	{
266		if (depthBits > 0)
267			gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *m_depthStencilBuffer);
268
269		if (stencilBits > 0)
270			gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *m_depthStencilBuffer);
271	}
272
273	GLU_EXPECT_NO_ERROR(gl.getError(), "Creating framebuffer");
274
275	if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
276		throw NotSupportedError("Framebuffer is not complete");
277
278	// Set up correct viewport for first test case.
279	gl.viewport(0, 0, width, height);
280
281	m_renderTarget = tcu::RenderTarget(width, height, pixelFormat, depthBits, stencilBits, 0);
282}
283
284void ScreenContext::postIterate (void)
285{
286	const glw::Functions& gl = getFunctions();
287	gl.bindRenderbuffer(GL_RENDERBUFFER, *m_colorBuffer);
288
289	if (![getEAGLContext() presentRenderbuffer:GL_RENDERBUFFER])
290		throw ResourceError("presentRenderbuffer() failed");
291}
292
293} // ios
294} // tcu
295