1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
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 OpenGL ES 3plus wrapper context.
22 *//*--------------------------------------------------------------------*/
23
24#include "gluES3PlusWrapperContext.hpp"
25#include "gluRenderContext.hpp"
26#include "gluRenderConfig.hpp"
27#include "glwInitFunctions.hpp"
28#include "glwFunctionLoader.hpp"
29#include "gluContextFactory.hpp"
30#include "deThreadLocal.hpp"
31#include "glwEnums.hpp"
32
33#include <sstream>
34#include <vector>
35#include <string>
36#include <cstring>
37#include <algorithm>
38#include <map>
39
40namespace glu
41{
42
43namespace es3plus
44{
45
46using std::vector;
47using std::string;
48
49class Context
50{
51public:
52								Context			(const glw::Functions& gl_);
53								~Context		(void);
54
55	void						addExtension	(const char* name);
56
57	const glw::Functions&		gl;			//!< GL 4.3 core context functions.
58
59	// Wrapper state.
60	string						vendor;
61	string						version;
62	string						renderer;
63	string						shadingLanguageVersion;
64	string						extensions;
65	vector<string>				extensionList;
66	bool						primitiveRestartEnabled;
67
68	deUint32					defaultVAO;
69	bool						defaultVAOBound;
70};
71
72Context::Context (const glw::Functions& gl_)
73	: gl						(gl_)
74	, vendor					("drawElements")
75	, version					("OpenGL ES 3.1")
76	, renderer					((const char*)gl.getString(GL_RENDERER))
77	, shadingLanguageVersion	("OpenGL ES GLSL ES 3.1")
78	, primitiveRestartEnabled	(false)
79	, defaultVAO				(0)
80	, defaultVAOBound			(false)
81{
82	gl.genVertexArrays(1, &defaultVAO);
83	if (gl.getError() != GL_NO_ERROR || defaultVAO == 0)
84		throw tcu::InternalError("Failed to allocate VAO for emulation");
85
86	gl.bindVertexArray(defaultVAO);
87	if (gl.getError() != GL_NO_ERROR)
88		throw tcu::InternalError("Failed to bind default VAO");
89	defaultVAOBound = true;
90
91	gl.enable(GL_PROGRAM_POINT_SIZE);
92	gl.getError(); // supress potential errors, feature is not critical
93
94	gl.enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
95	gl.getError(); // suppress
96
97	// Extensions
98	addExtension("GL_OES_texture_stencil8");
99	addExtension("GL_OES_sample_shading");
100	addExtension("GL_OES_sample_variables");
101	addExtension("GL_OES_shader_multisample_interpolation");
102	addExtension("GL_OES_shader_image_atomic");
103	addExtension("GL_OES_texture_storage_multisample_2d_array");
104
105	// \todo [2014-03-18 pyry] Enable only if base ctx supports these or compatible GL_NV_blend_equation_advanced ext
106	addExtension("GL_KHR_blend_equation_advanced");
107	addExtension("GL_KHR_blend_equation_advanced_coherent");
108
109	addExtension("GL_EXT_shader_io_blocks");
110	addExtension("GL_EXT_geometry_shader");
111	addExtension("GL_EXT_geometry_point_size");
112	addExtension("GL_EXT_tessellation_shader");
113	addExtension("GL_EXT_tessellation_point_size");
114	addExtension("GL_EXT_gpu_shader5");
115	addExtension("GL_KHR_debug");
116	addExtension("GL_EXT_texture_cube_map_array");
117}
118
119Context::~Context (void)
120{
121	if (defaultVAO)
122		gl.deleteVertexArrays(1, &defaultVAO);
123}
124
125void Context::addExtension (const char* name)
126{
127	if (!extensions.empty())
128		extensions += " ";
129	extensions += name;
130
131	extensionList.push_back(name);
132}
133
134static de::ThreadLocal tls_context;
135
136void setCurrentContext (Context* context)
137{
138	tls_context.set(context);
139}
140
141inline Context* getCurrentContext (void)
142{
143	return (Context*)tls_context.get();
144}
145
146static GLW_APICALL void GLW_APIENTRY getIntegerv (deUint32 pname, deInt32* params)
147{
148	Context* context = getCurrentContext();
149
150	if (context)
151	{
152		if (pname == GL_NUM_EXTENSIONS && params)
153			*params = (deInt32)context->extensionList.size();
154		else
155			context->gl.getIntegerv(pname, params);
156	}
157}
158
159static GLW_APICALL const glw::GLubyte* GLW_APIENTRY getString (deUint32 name)
160{
161	Context* context = getCurrentContext();
162
163	if (context)
164	{
165		switch (name)
166		{
167			case GL_VENDOR:						return (const glw::GLubyte*)context->vendor.c_str();
168			case GL_VERSION:					return (const glw::GLubyte*)context->version.c_str();
169			case GL_RENDERER:					return (const glw::GLubyte*)context->renderer.c_str();
170			case GL_SHADING_LANGUAGE_VERSION:	return (const glw::GLubyte*)context->shadingLanguageVersion.c_str();
171			case GL_EXTENSIONS:					return (const glw::GLubyte*)context->extensions.c_str();
172			default:							return context->gl.getString(name);
173		}
174	}
175	else
176		return DE_NULL;
177}
178
179static GLW_APICALL const glw::GLubyte* GLW_APIENTRY getStringi (deUint32 name, deUint32 index)
180{
181	Context* context = getCurrentContext();
182
183	if (context)
184	{
185		if (name == GL_EXTENSIONS)
186		{
187			if ((size_t)index < context->extensionList.size())
188				return (const glw::GLubyte*)context->extensionList[index].c_str();
189			else
190				return context->gl.getStringi(name, ~0u);
191		}
192		else
193			return context->gl.getStringi(name, index);
194	}
195	else
196		return DE_NULL;
197}
198
199static GLW_APICALL void GLW_APIENTRY enable (deUint32 cap)
200{
201	Context* context = getCurrentContext();
202
203	if (context)
204	{
205		if (cap == GL_PRIMITIVE_RESTART_FIXED_INDEX)
206		{
207			context->primitiveRestartEnabled = true;
208			// \todo [2013-09-30 pyry] Call to glPrimitiveRestartIndex() is required prior to all draw calls!
209		}
210		else
211			context->gl.enable(cap);
212	}
213}
214
215static GLW_APICALL void GLW_APIENTRY disable (deUint32 cap)
216{
217	Context* context = getCurrentContext();
218
219	if (context)
220	{
221		if (cap == GL_PRIMITIVE_RESTART_FIXED_INDEX)
222			context->primitiveRestartEnabled = false;
223		else
224			context->gl.disable(cap);
225	}
226}
227
228static GLW_APICALL void GLW_APIENTRY bindVertexArray (deUint32 array)
229{
230	Context* context = getCurrentContext();
231
232	if (context)
233	{
234		context->gl.bindVertexArray(array == 0 ? context->defaultVAO : array);
235		context->defaultVAOBound = (array == 0);
236	}
237}
238
239static GLW_APICALL void GLW_APIENTRY hint (deUint32 target, deUint32 mode)
240{
241	Context* context = getCurrentContext();
242
243	if (context)
244	{
245		if (target != GL_GENERATE_MIPMAP_HINT)
246			context->gl.hint(target, mode);
247		// \todo [2013-09-30 pyry] Verify mode.
248	}
249}
250
251static void translateShaderSource (deUint32 shaderType, std::ostream& dst, const std::string& src, const std::vector<std::string>& filteredExtensions)
252{
253	bool				foundVersion	= false;
254	std::istringstream	istr			(src);
255	std::string			line;
256	int					srcLineNdx		= 1;
257
258	while (std::getline(istr, line, '\n'))
259	{
260		if (line == "#version 310 es")
261		{
262			foundVersion = true;
263			dst << "#version 430\n";
264			if (shaderType == GL_VERTEX_SHADER)
265			{
266				// ARB_separate_shader_objects requires gl_PerVertex to be explicitly declared
267				dst << "out gl_PerVertex {\n"
268					<< "    vec4 gl_Position;\n"
269					<< "    float gl_PointSize;\n"
270					<< "    float gl_ClipDistance[];\n"
271					<< "};\n"
272					<< "#line " << (srcLineNdx + 1) << "\n";
273			}
274		}
275		else if (line == "#version 300 es")
276		{
277			foundVersion = true;
278			dst << "#version 330\n";
279		}
280		else if (line.substr(0, 10) == "precision ")
281		{
282			const size_t	precPos		= 10;
283			const size_t	precEndPos	= line.find(' ', precPos);
284			const size_t	endPos		= line.find(';');
285
286			if (precEndPos != std::string::npos && endPos != std::string::npos && endPos > precEndPos+1)
287			{
288				const size_t		typePos		= precEndPos+1;
289				const std::string	precision	= line.substr(precPos, precEndPos-precPos);
290				const std::string	type		= line.substr(typePos, endPos-typePos);
291				const bool			precOk		= precision == "lowp" || precision == "mediump" || precision == "highp";
292
293				if (precOk &&
294					(type == "image2D" || type == "uimage2D" || type == "iimage2D" ||
295					 type == "imageCube" || type == "uimageCube" || type == "iimageCube" ||
296					 type == "image3D" || type == "iimage3D" || type == "uimage3D" ||
297					 type == "image2DArray" || type == "iimage2DArray" || type == "uimage2DArray" ||
298					 type == "imageCubeArray" || type == "iimageCubeArray" || type == "uimageCubeArray"))
299					dst << "// "; // Filter out statement
300			}
301
302			dst << line << "\n";
303		}
304		else if (line.substr(0, 11) == "#extension ")
305		{
306			const size_t	extNamePos		= 11;
307			const size_t	extNameEndPos	= line.find_first_of(" :", extNamePos);
308			const size_t	behaviorPos		= line.find_first_not_of(" :", extNameEndPos);
309
310			if (extNameEndPos != std::string::npos && behaviorPos != std::string::npos)
311			{
312				const std::string	extName				= line.substr(extNamePos, extNameEndPos-extNamePos);
313				const std::string	behavior			= line.substr(behaviorPos);
314				const bool			filteredExtension	= std::find(filteredExtensions.begin(), filteredExtensions.end(), extName) != filteredExtensions.end();
315				const bool			validBehavior		= behavior == "require" || behavior == "enable" || behavior == "warn" || behavior == "disable";
316
317				if (filteredExtension && validBehavior)
318					dst << "// "; // Filter out extension
319			}
320			dst << line << "\n";
321		}
322		else if (line.substr(0, 21) == "layout(blend_support_")
323			dst << "// " << line << "\n";
324		else
325			dst << line << "\n";
326
327		srcLineNdx += 1;
328	}
329
330	DE_ASSERT(foundVersion);
331	DE_UNREF(foundVersion);
332}
333
334static std::string translateShaderSources (deUint32 shaderType, deInt32 count, const char* const* strings, const int* length, const std::vector<std::string>& filteredExtensions)
335{
336	std::ostringstream	srcIn;
337	std::ostringstream	srcOut;
338
339	for (int ndx = 0; ndx < count; ndx++)
340	{
341		const int len = length && length[ndx] >= 0 ? length[ndx] : (int)strlen(strings[ndx]);
342		srcIn << std::string(strings[ndx], strings[ndx] + len);
343	}
344
345	translateShaderSource(shaderType, srcOut, srcIn.str(), filteredExtensions);
346
347	return srcOut.str();
348}
349
350static GLW_APICALL void GLW_APIENTRY shaderSource (deUint32 shader, deInt32 count, const char* const* strings, const int* length)
351{
352	Context* context = getCurrentContext();
353
354	if (context)
355	{
356		if (count > 0 && strings)
357		{
358			deInt32				shaderType = GL_NONE;
359			context->gl.getShaderiv(shader, GL_SHADER_TYPE, &shaderType);
360			{
361				const std::string	translatedSrc	= translateShaderSources(shaderType, count, strings, length, context->extensionList);
362				const char*			srcPtr			= translatedSrc.c_str();
363				context->gl.shaderSource(shader, 1, &srcPtr, DE_NULL);
364			}
365		}
366		else
367			context->gl.shaderSource(shader, count, strings, length);
368	}
369}
370
371static GLW_APICALL void GLW_APIENTRY bindFramebuffer (deUint32 target, deUint32 framebuffer)
372{
373	Context* context = getCurrentContext();
374
375	if (context)
376	{
377		context->gl.bindFramebuffer(target, framebuffer);
378
379		// Emulate ES behavior where sRGB conversion is only controlled by color buffer format.
380		if (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER || target == GL_READ_FRAMEBUFFER)
381			((framebuffer != 0) ? context->gl.enable : context->gl.disable)(GL_FRAMEBUFFER_SRGB);
382	}
383}
384
385static GLW_APICALL void GLW_APIENTRY blendBarrierKHR (void)
386{
387	Context* context = getCurrentContext();
388
389	if (context)
390	{
391		// \todo [2014-03-18 pyry] Use BlendBarrierNV() if supported
392		context->gl.finish();
393	}
394}
395
396static GLW_APICALL deUint32 GLW_APIENTRY createShaderProgramv (deUint32 type, deInt32 count, const char* const* strings)
397{
398	Context* context = getCurrentContext();
399
400	if (context)
401	{
402		if (count > 0 && strings)
403		{
404			const std::string	translatedSrc	= translateShaderSources(type, count, strings, DE_NULL, context->extensionList);
405			const char*			srcPtr			= translatedSrc.c_str();
406			return context->gl.createShaderProgramv(type, 1, &srcPtr);
407		}
408		else
409			return context->gl.createShaderProgramv(type, count, strings);
410	}
411	return 0;
412}
413
414static void initFunctions (glw::Functions* dst, const glw::Functions& src)
415{
416	// Functions directly passed to GL context.
417#include "gluES3PlusWrapperFuncs.inl"
418
419	// Wrapped functions.
420	dst->bindVertexArray		= bindVertexArray;
421	dst->disable				= disable;
422	dst->enable					= enable;
423	dst->getIntegerv			= getIntegerv;
424	dst->getString				= getString;
425	dst->getStringi				= getStringi;
426	dst->hint					= hint;
427	dst->shaderSource			= shaderSource;
428	dst->createShaderProgramv	= createShaderProgramv;
429	dst->bindFramebuffer		= bindFramebuffer;
430
431	// Extension functions
432	{
433		using std::map;
434
435		class ExtFuncLoader : public glw::FunctionLoader
436		{
437		public:
438			ExtFuncLoader (const map<string, glw::GenericFuncType>& extFuncs)
439				: m_extFuncs(extFuncs)
440			{
441			}
442
443			glw::GenericFuncType get (const char* name) const
444			{
445				map<string, glw::GenericFuncType>::const_iterator pos = m_extFuncs.find(name);
446				return pos != m_extFuncs.end() ? pos->second : DE_NULL;
447			}
448
449		private:
450			const map<string, glw::GenericFuncType>& m_extFuncs;
451		};
452
453		map<string, glw::GenericFuncType>	extFuncMap;
454		const ExtFuncLoader					extFuncLoader	(extFuncMap);
455
456		// OES_sample_shading
457		extFuncMap["glMinSampleShadingOES"]			= (glw::GenericFuncType)src.minSampleShading;
458
459		// OES_texture_storage_multisample_2d_array
460		extFuncMap["glTexStorage3DMultisampleOES"]	= (glw::GenericFuncType)src.texStorage3DMultisample;
461
462		// KHR_blend_equation_advanced
463		extFuncMap["glBlendBarrierKHR"]				= (glw::GenericFuncType)blendBarrierKHR;
464
465		// EXT_tessellation_shader
466		extFuncMap["glPatchParameteriEXT"]			= (glw::GenericFuncType)src.patchParameteri;
467
468		// EXT_geometry_shader
469		extFuncMap["glFramebufferTextureEXT"]		= (glw::GenericFuncType)src.framebufferTexture;
470
471		// KHR_debug
472		extFuncMap["glDebugMessageControlKHR"]		= (glw::GenericFuncType)src.debugMessageControl;
473		extFuncMap["glDebugMessageInsertKHR"]		= (glw::GenericFuncType)src.debugMessageInsert;
474		extFuncMap["glDebugMessageCallbackKHR"]		= (glw::GenericFuncType)src.debugMessageCallback;
475		extFuncMap["glGetDebugMessageLogKHR"]		= (glw::GenericFuncType)src.getDebugMessageLog;
476		extFuncMap["glGetPointervKHR"] 				= (glw::GenericFuncType)src.getPointerv;
477		extFuncMap["glPushDebugGroupKHR"]			= (glw::GenericFuncType)src.pushDebugGroup;
478		extFuncMap["glPopDebugGroupKHR"] 			= (glw::GenericFuncType)src.popDebugGroup;
479		extFuncMap["glObjectLabelKHR"] 				= (glw::GenericFuncType)src.objectLabel;
480		extFuncMap["glGetObjectLabelKHR"]			= (glw::GenericFuncType)src.getObjectLabel;
481		extFuncMap["glObjectPtrLabelKHR"]			= (glw::GenericFuncType)src.objectPtrLabel;
482		extFuncMap["glGetObjectPtrLabelKHR"]		= (glw::GenericFuncType)src.getObjectPtrLabel;
483
484		{
485			int	numExts	= 0;
486			dst->getIntegerv(GL_NUM_EXTENSIONS, &numExts);
487
488			if (numExts > 0)
489			{
490				vector<const char*> extStr(numExts);
491
492				for (int ndx = 0; ndx < numExts; ndx++)
493					extStr[ndx] = (const char*)dst->getStringi(GL_EXTENSIONS, ndx);
494
495				glw::initExtensionsES(dst, &extFuncLoader, (int)extStr.size(), &extStr[0]);
496			}
497		}
498	}
499}
500
501} // es3plus
502
503ES3PlusWrapperContext::ES3PlusWrapperContext (const ContextFactory& factory, const RenderConfig& config, const tcu::CommandLine& cmdLine)
504	: m_context		(DE_NULL)
505	, m_wrapperCtx	(DE_NULL)
506{
507	// Flags that are valid for both core & es context. Currently only excludes CONTEXT_FORWARD_COMPATIBLE
508	const ContextFlags validContextFlags = CONTEXT_ROBUST | CONTEXT_DEBUG;
509
510	static const ContextType wrappableNativeTypes[] =
511	{
512		ContextType(ApiType::core(4,4), config.type.getFlags() & validContextFlags),	// !< higher in the list, preferred
513		ContextType(ApiType::core(4,3), config.type.getFlags() & validContextFlags),
514	};
515
516	if (config.type.getAPI() != ApiType::es(3,1))
517		throw tcu::NotSupportedError("Unsupported context type (ES3.1 wrapper supports only ES3.1)");
518
519	// try to create any wrappable context
520
521	for (int nativeCtxNdx = 0; nativeCtxNdx < DE_LENGTH_OF_ARRAY(wrappableNativeTypes); ++nativeCtxNdx)
522	{
523		glu::ContextType nativeContext = wrappableNativeTypes[nativeCtxNdx];
524
525		try
526		{
527			glu::RenderConfig nativeConfig = config;
528			nativeConfig.type = nativeContext;
529
530			m_context		= factory.createContext(nativeConfig, cmdLine);
531			m_wrapperCtx	= new es3plus::Context(m_context->getFunctions());
532
533			es3plus::setCurrentContext(m_wrapperCtx);
534			es3plus::initFunctions(&m_functions, m_context->getFunctions());
535			break;
536		}
537		catch (...)
538		{
539			es3plus::setCurrentContext(DE_NULL);
540
541			delete m_wrapperCtx;
542			delete m_context;
543
544			m_wrapperCtx = DE_NULL;
545			m_context = DE_NULL;
546
547			// throw only if all tries failed (that is, this was the last potential target)
548			if (nativeCtxNdx + 1 == DE_LENGTH_OF_ARRAY(wrappableNativeTypes))
549				throw;
550			else
551				continue;
552		}
553	}
554}
555
556ES3PlusWrapperContext::~ES3PlusWrapperContext (void)
557{
558	delete m_wrapperCtx;
559	delete m_context;
560}
561
562ContextType ES3PlusWrapperContext::getType (void) const
563{
564	return ContextType(ApiType::es(3,1), m_context->getType().getFlags());
565}
566
567} // glu
568