1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
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 Implementation-defined limit tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fImplementationLimitTests.hpp"
25#include "tcuTestLog.hpp"
26#include "gluDefs.hpp"
27#include "gluStrUtil.hpp"
28#include "gluRenderContext.hpp"
29
30#include <vector>
31#include <set>
32#include <algorithm>
33#include <iterator>
34#include <limits>
35
36#include "glwEnums.hpp"
37#include "glwFunctions.hpp"
38
39namespace deqp
40{
41namespace gles3
42{
43namespace Functional
44{
45
46using std::vector;
47using std::string;
48using std::set;
49using namespace glw; // GL types
50
51namespace LimitQuery
52{
53
54// Query function template.
55template<typename T>
56T query (const glw::Functions& gl, deUint32 param);
57
58// Compare template.
59template<typename T>
60inline bool compare (const T& min, const T& reported) { return min <= reported; }
61
62// Types for queries
63
64struct NegInt
65{
66	GLint value;
67	NegInt (GLint value_) : value(value_) {}
68};
69
70std::ostream& operator<< (std::ostream& str, const NegInt& v) { return str << v.value; }
71
72struct FloatRange
73{
74	float min;
75	float max;
76	FloatRange (float min_, float max_) : min(min_), max(max_) {}
77};
78
79std::ostream& operator<< (std::ostream& str, const FloatRange& range) { return str << range.min << ", " << range.max; }
80
81struct AlignmentInt
82{
83	GLint value;
84	AlignmentInt (GLint value_) : value(value_) {}
85};
86
87std::ostream& operator<< (std::ostream& str, const AlignmentInt& v) { return str << v.value; }
88
89// For custom formatting
90struct Boolean
91{
92	GLboolean value;
93	Boolean (GLboolean value_) : value(value_) {}
94};
95
96std::ostream& operator<< (std::ostream& str, const Boolean& boolean) { return str << (boolean.value ? "GL_TRUE" : "GL_FALSE"); }
97
98// Query function implementations.
99template<>
100GLint query<GLint> (const glw::Functions& gl, deUint32 param)
101{
102	GLint val = -1;
103	gl.getIntegerv(param, &val);
104	return val;
105}
106
107template<>
108GLint64 query<GLint64> (const glw::Functions& gl, deUint32 param)
109{
110	GLint64 val = -1;
111	gl.getInteger64v(param, &val);
112	return val;
113}
114
115template<>
116GLuint64 query<GLuint64> (const glw::Functions& gl, deUint32 param)
117{
118	GLint64 val = 0;
119	gl.getInteger64v(param, &val);
120	return (GLuint64)val;
121}
122
123template<>
124GLfloat query<GLfloat> (const glw::Functions& gl,deUint32 param)
125{
126	GLfloat val = -1000.f;
127	gl.getFloatv(param, &val);
128	return val;
129}
130
131template<>
132NegInt query<NegInt> (const glw::Functions& gl, deUint32 param)
133{
134	return NegInt(query<GLint>(gl, param));
135}
136
137template<>
138Boolean query<Boolean> (const glw::Functions& gl, deUint32 param)
139{
140	GLboolean val = GL_FALSE;
141	gl.getBooleanv(param, &val);
142	return Boolean(val);
143}
144
145template<>
146FloatRange query<FloatRange> (const glw::Functions& gl, deUint32 param)
147{
148	float v[2] = { -1.0f, -1.0f };
149	gl.getFloatv(param, &v[0]);
150	return FloatRange(v[0], v[1]);
151}
152
153template<>
154AlignmentInt query<AlignmentInt> (const glw::Functions& gl, deUint32 param)
155{
156	return AlignmentInt(query<GLint>(gl, param));
157}
158
159// Special comparison operators
160template<>
161bool compare<Boolean> (const Boolean& min, const Boolean& reported)
162{
163	return !min.value || (min.value && reported.value);
164}
165
166template<>
167bool compare<NegInt> (const NegInt& min, const NegInt& reported)
168{
169	// Reverse comparison.
170	return reported.value <= min.value;
171}
172
173template<>
174bool compare<FloatRange> (const FloatRange& min, const FloatRange& reported)
175{
176	return reported.min <= min.min && min.max <= reported.max;
177}
178
179template<>
180bool compare<AlignmentInt> (const AlignmentInt& min, const AlignmentInt& reported)
181{
182	// Reverse comparison.
183	return reported.value <= min.value;
184}
185
186// Special error descriptions
187
188enum QueryClass
189{
190	CLASS_VALUE = 0,
191	CLASS_RANGE,
192	CLASS_ALIGNMENT,
193};
194
195template <QueryClass Class>
196struct QueryClassTraits
197{
198	static const char* const s_errorDescription;
199};
200
201template <>
202const char* const QueryClassTraits<CLASS_VALUE>::s_errorDescription = "reported value is less than minimum required value!";
203
204template <>
205const char* const QueryClassTraits<CLASS_RANGE>::s_errorDescription = "reported range does not contain the minimum required range!";
206
207template <>
208const char* const QueryClassTraits<CLASS_ALIGNMENT>::s_errorDescription = "reported alignment is larger than minimum required aligmnent!";
209
210template <typename T>
211struct QueryTypeTraits
212{
213};
214
215template <> struct QueryTypeTraits<GLint>			{	enum { CLASS = CLASS_VALUE };		};
216template <> struct QueryTypeTraits<GLint64>			{	enum { CLASS = CLASS_VALUE };		};
217template <> struct QueryTypeTraits<GLuint64>		{	enum { CLASS = CLASS_VALUE };		};
218template <> struct QueryTypeTraits<GLfloat>			{	enum { CLASS = CLASS_VALUE };		};
219template <> struct QueryTypeTraits<Boolean>			{	enum { CLASS = CLASS_VALUE };		};
220template <> struct QueryTypeTraits<NegInt>			{	enum { CLASS = CLASS_VALUE };		};
221template <> struct QueryTypeTraits<FloatRange>		{	enum { CLASS = CLASS_RANGE };		};
222template <> struct QueryTypeTraits<AlignmentInt>	{	enum { CLASS = CLASS_ALIGNMENT };	};
223
224} // LimitQuery
225
226using namespace LimitQuery;
227using tcu::TestLog;
228
229template<typename T>
230class LimitQueryCase : public TestCase
231{
232public:
233	LimitQueryCase (Context& context, const char* name, const char* description, deUint32 limit, const T& minRequiredValue)
234		: TestCase				(context, name, description)
235		, m_limit				(limit)
236		, m_minRequiredValue	(minRequiredValue)
237	{
238	}
239
240	IterateResult iterate (void)
241	{
242		const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
243		const T					value	= query<T>(m_context.getRenderContext().getFunctions(), m_limit);
244		GLU_EXPECT_NO_ERROR(gl.getError(), "Query failed");
245
246		const bool isOk = compare<T>(m_minRequiredValue, value);
247
248		m_testCtx.getLog() << TestLog::Message << "Reported: " << value << TestLog::EndMessage;
249		m_testCtx.getLog() << TestLog::Message << "Minimum required: " << m_minRequiredValue << TestLog::EndMessage;
250
251		if (!isOk)
252			m_testCtx.getLog() << TestLog::Message << "FAIL: " << QueryClassTraits<(QueryClass)QueryTypeTraits<T>::CLASS>::s_errorDescription << TestLog::EndMessage;
253
254		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
255								isOk ? "Pass"				: "Requirement not satisfied");
256		return STOP;
257	}
258
259private:
260	deUint32	m_limit;
261	T			m_minRequiredValue;
262};
263
264static const deUint32 s_requiredCompressedTexFormats[] =
265{
266	GL_COMPRESSED_R11_EAC,
267	GL_COMPRESSED_SIGNED_R11_EAC,
268	GL_COMPRESSED_RG11_EAC,
269	GL_COMPRESSED_SIGNED_RG11_EAC,
270	GL_COMPRESSED_RGB8_ETC2,
271	GL_COMPRESSED_SRGB8_ETC2,
272	GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
273	GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
274	GL_COMPRESSED_RGBA8_ETC2_EAC,
275	GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
276};
277
278class CompressedTextureFormatsQueryCase : public TestCase
279{
280public:
281	CompressedTextureFormatsQueryCase (Context& context)
282		: TestCase(context, "compressed_texture_formats", "GL_COMPRESSED_TEXTURE_FORMATS")
283	{
284	}
285
286	IterateResult iterate (void)
287	{
288		const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
289		const GLint				numFormats		= query<GLint>(gl, GL_NUM_COMPRESSED_TEXTURE_FORMATS);
290		vector<GLint>			formats			(numFormats);
291		bool					allFormatsOk	= true;
292
293		if (numFormats > 0)
294			gl.getIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, &formats[0]);
295
296		GLU_EXPECT_NO_ERROR(gl.getError(), "Query failed");
297
298		// Log formats.
299		m_testCtx.getLog() << TestLog::Message << "Reported:" << TestLog::EndMessage;
300		for (vector<GLint>::const_iterator fmt = formats.begin(); fmt != formats.end(); fmt++)
301			m_testCtx.getLog() << TestLog::Message << glu::getCompressedTexFormatStr(*fmt) << TestLog::EndMessage;
302
303		// Check that all required formats are in list.
304		{
305			set<GLint> formatSet(formats.begin(), formats.end());
306
307			for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_requiredCompressedTexFormats); ndx++)
308			{
309				const deUint32	fmt		= s_requiredCompressedTexFormats[ndx];
310				const bool		found	= formatSet.find(fmt) != formatSet.end();
311
312				if (!found)
313				{
314					m_testCtx.getLog() << TestLog::Message << "ERROR: " << glu::getCompressedTexFormatStr(fmt) << " is missing!" << TestLog::EndMessage;
315					allFormatsOk = false;
316				}
317			}
318		}
319
320		m_testCtx.setTestResult(allFormatsOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
321								allFormatsOk ? "Pass"				: "Requirement not satisfied");
322		return STOP;
323	}
324};
325
326static vector<string> queryExtensionsNonIndexed (const glw::Functions& gl)
327{
328	const string	extensionStr	= (const char*)gl.getString(GL_EXTENSIONS);
329	vector<string>	extensionList;
330	size_t			pos				= 0;
331
332	for (;;)
333	{
334		const size_t	nextPos	= extensionStr.find(' ', pos);
335		const size_t	len		= nextPos == string::npos ? extensionStr.length()-pos : nextPos-pos;
336
337		if (len > 0)
338			extensionList.push_back(extensionStr.substr(pos, len));
339
340		if (nextPos == string::npos)
341			break;
342		else
343			pos = nextPos+1;
344	}
345
346	return extensionList;
347}
348
349static vector<string> queryExtensionsIndexed (const glw::Functions& gl)
350{
351	const int		numExtensions		= query<GLint>(gl, GL_NUM_EXTENSIONS);
352	vector<string>	extensions			(numExtensions);
353
354	GLU_EXPECT_NO_ERROR(gl.getError(), "GL_NUM_EXTENSIONS query failed");
355
356	for (int ndx = 0; ndx < numExtensions; ndx++)
357		extensions[ndx] = (const char*)gl.getStringi(GL_EXTENSIONS, (GLuint)ndx);
358
359	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetStringi(GL_EXTENSIONS) failed");
360
361	return extensions;
362}
363
364static bool compareExtensionLists (const vector<string>& a, const vector<string>& b)
365{
366	if (a.size() != b.size())
367		return false;
368
369	set<string> extsInB(b.begin(), b.end());
370
371	for (vector<string>::const_iterator i = a.begin(); i != a.end(); ++i)
372	{
373		if (extsInB.find(*i) == extsInB.end())
374			return false;
375	}
376
377	return true;
378}
379
380class ExtensionQueryCase : public TestCase
381{
382public:
383	ExtensionQueryCase (Context& context)
384		: TestCase(context, "extensions", "GL_EXTENSIONS")
385	{
386	}
387
388	IterateResult iterate (void)
389	{
390		const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
391		const vector<string>	nonIndexedExts		= queryExtensionsNonIndexed(gl);
392		const vector<string>	indexedExts			= queryExtensionsIndexed(gl);
393		const bool				isOk				= compareExtensionLists(nonIndexedExts, indexedExts);
394
395		m_testCtx.getLog() << TestLog::Message << "Extensions as reported by glGetStringi(GL_EXTENSIONS):" << TestLog::EndMessage;
396		for (vector<string>::const_iterator ext = indexedExts.begin(); ext != indexedExts.end(); ++ext)
397			m_testCtx.getLog() << TestLog::Message << *ext << TestLog::EndMessage;
398
399		if (!isOk)
400		{
401			m_testCtx.getLog() << TestLog::Message << "Extensions as reported by glGetString(GL_EXTENSIONS):" << TestLog::EndMessage;
402			for (vector<string>::const_iterator ext = nonIndexedExts.begin(); ext != nonIndexedExts.end(); ++ext)
403				m_testCtx.getLog() << TestLog::Message << *ext << TestLog::EndMessage;
404
405			m_testCtx.getLog() << TestLog::Message << "ERROR: Extension lists do not match!" << TestLog::EndMessage;
406		}
407
408		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
409								isOk ? "Pass"				: "Invalid extension list");
410		return STOP;
411	}
412};
413
414ImplementationLimitTests::ImplementationLimitTests (Context& context)
415	: TestCaseGroup(context, "implementation_limits", "Implementation-defined limits")
416{
417}
418
419ImplementationLimitTests::~ImplementationLimitTests (void)
420{
421}
422
423void ImplementationLimitTests::init (void)
424{
425	const int	minVertexUniformBlocks					= 12;
426	const int	minVertexUniformComponents				= 1024;
427
428	const int	minFragmentUniformBlocks				= 12;
429	const int	minFragmentUniformComponents			= 896;
430
431	const int	minUniformBlockSize						= 16384;
432	const int	minCombinedVertexUniformComponents		= (minVertexUniformBlocks*minUniformBlockSize)/4 + minVertexUniformComponents;
433	const int	minCombinedFragmentUniformComponents	= (minFragmentUniformBlocks*minUniformBlockSize)/4 + minFragmentUniformComponents;
434
435#define LIMIT_CASE(NAME, PARAM, TYPE, MIN_VAL)	\
436	addChild(new LimitQueryCase<TYPE>(m_context, #NAME, #PARAM, PARAM, MIN_VAL))
437
438	LIMIT_CASE(max_element_index,				GL_MAX_ELEMENT_INDEX,					GLint64,	(1<<24)-1);
439	LIMIT_CASE(subpixel_bits,					GL_SUBPIXEL_BITS,						GLint,		4);
440	LIMIT_CASE(max_3d_texture_size,				GL_MAX_3D_TEXTURE_SIZE,					GLint,		256);
441	LIMIT_CASE(max_texture_size,				GL_MAX_TEXTURE_SIZE,					GLint,		2048);
442	LIMIT_CASE(max_array_texture_layers,		GL_MAX_ARRAY_TEXTURE_LAYERS,			GLint,		256);
443	LIMIT_CASE(max_texture_lod_bias,			GL_MAX_TEXTURE_LOD_BIAS,				GLfloat,	2.0f);
444	LIMIT_CASE(max_cube_map_texture_size,		GL_MAX_CUBE_MAP_TEXTURE_SIZE,			GLint,		2048);
445	LIMIT_CASE(max_renderbuffer_size,			GL_MAX_RENDERBUFFER_SIZE,				GLint,		2048);
446	LIMIT_CASE(max_draw_buffers,				GL_MAX_DRAW_BUFFERS,					GLint,		4);
447	LIMIT_CASE(max_color_attachments,			GL_MAX_COLOR_ATTACHMENTS,				GLint,		4);
448	// GL_MAX_VIEWPORT_DIMS
449	LIMIT_CASE(aliased_point_size_range,		GL_ALIASED_POINT_SIZE_RANGE,			FloatRange,	FloatRange(1,1));
450	LIMIT_CASE(aliased_line_width_range,		GL_ALIASED_LINE_WIDTH_RANGE,			FloatRange,	FloatRange(1,1));
451	LIMIT_CASE(max_elements_indices,			GL_MAX_ELEMENTS_INDICES,				GLint,		0);
452	LIMIT_CASE(max_elements_vertices,			GL_MAX_ELEMENTS_VERTICES,				GLint,		0);
453	LIMIT_CASE(num_compressed_texture_formats,	GL_NUM_COMPRESSED_TEXTURE_FORMATS,		GLint,		DE_LENGTH_OF_ARRAY(s_requiredCompressedTexFormats));
454	addChild(new CompressedTextureFormatsQueryCase(m_context)); // GL_COMPRESSED_TEXTURE_FORMATS
455	// GL_PROGRAM_BINARY_FORMATS
456	LIMIT_CASE(num_program_binary_formats,		GL_NUM_PROGRAM_BINARY_FORMATS,			GLint,		0);
457	// GL_SHADER_BINARY_FORMATS
458	LIMIT_CASE(num_shader_binary_formats,		GL_NUM_SHADER_BINARY_FORMATS,			GLint,		0);
459	LIMIT_CASE(shader_compiler,					GL_SHADER_COMPILER,						Boolean,	GL_TRUE);
460	// Shader data type ranges & precisions
461	LIMIT_CASE(max_server_wait_timeout,			GL_MAX_SERVER_WAIT_TIMEOUT,				GLuint64,	0);
462
463	// Version and extension support
464	addChild(new ExtensionQueryCase(m_context)); // GL_EXTENSIONS + consistency validation
465	LIMIT_CASE(num_extensions,					GL_NUM_EXTENSIONS,						GLint,		0);
466	LIMIT_CASE(major_version,					GL_MAJOR_VERSION,						GLint,		3);
467	LIMIT_CASE(minor_version,					GL_MINOR_VERSION,						GLint,		0);
468	// GL_RENDERER
469	// GL_SHADING_LANGUAGE_VERSION
470	// GL_VENDOR
471	// GL_VERSION
472
473	// Vertex shader limits
474	LIMIT_CASE(max_vertex_attribs,				GL_MAX_VERTEX_ATTRIBS,					GLint,		16);
475	LIMIT_CASE(max_vertex_uniform_components,	GL_MAX_VERTEX_UNIFORM_COMPONENTS,		GLint,		minVertexUniformComponents);
476	LIMIT_CASE(max_vertex_uniform_vectors,		GL_MAX_VERTEX_UNIFORM_VECTORS,			GLint,		minVertexUniformComponents/4);
477	LIMIT_CASE(max_vertex_uniform_blocks,		GL_MAX_VERTEX_UNIFORM_BLOCKS,			GLint,		minVertexUniformBlocks);
478	LIMIT_CASE(max_vertex_output_components,	GL_MAX_VERTEX_OUTPUT_COMPONENTS,		GLint,		64);
479	LIMIT_CASE(max_vertex_texture_image_units,	GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,		GLint,		16);
480
481	// Fragment shader limits
482	LIMIT_CASE(max_fragment_uniform_components,	GL_MAX_FRAGMENT_UNIFORM_COMPONENTS,		GLint,		minFragmentUniformComponents);
483	LIMIT_CASE(max_fragment_uniform_vectors,	GL_MAX_FRAGMENT_UNIFORM_VECTORS,		GLint,		minFragmentUniformComponents/4);
484	LIMIT_CASE(max_fragment_uniform_blocks,		GL_MAX_FRAGMENT_UNIFORM_BLOCKS,			GLint,		minFragmentUniformBlocks);
485	LIMIT_CASE(max_fragment_input_components,	GL_MAX_FRAGMENT_INPUT_COMPONENTS,		GLint,		60);
486	LIMIT_CASE(max_texture_image_units,			GL_MAX_TEXTURE_IMAGE_UNITS,				GLint,		16);
487	LIMIT_CASE(min_program_texel_offset,		GL_MIN_PROGRAM_TEXEL_OFFSET,			NegInt,		-8);
488	LIMIT_CASE(max_program_texel_offset,		GL_MAX_PROGRAM_TEXEL_OFFSET,			GLint,		7);
489
490	// Aggregate shader limits
491	LIMIT_CASE(max_uniform_buffer_bindings,						GL_MAX_UNIFORM_BUFFER_BINDINGS,						GLint,				24);
492	LIMIT_CASE(max_uniform_block_size,							GL_MAX_UNIFORM_BLOCK_SIZE,							GLint64,			minUniformBlockSize);
493	LIMIT_CASE(uniform_buffer_offset_alignment,					GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT,					AlignmentInt,		256);
494	LIMIT_CASE(max_combined_uniform_blocks,						GL_MAX_COMBINED_UNIFORM_BLOCKS,						GLint,				24);
495	LIMIT_CASE(max_combined_vertex_uniform_components,			GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS,			GLint64,			minCombinedVertexUniformComponents);
496	LIMIT_CASE(max_combined_fragment_uniform_components,		GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS,		GLint64,			minCombinedFragmentUniformComponents);
497	LIMIT_CASE(max_varying_components,							GL_MAX_VARYING_COMPONENTS,							GLint,				60);
498	LIMIT_CASE(max_varying_vectors,								GL_MAX_VARYING_VECTORS,								GLint,				15);
499	LIMIT_CASE(max_combined_texture_image_units,				GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,				GLint,				32);
500
501	// Transform feedback limits
502	LIMIT_CASE(max_transform_feedback_interleaved_components,	GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,	GLint,				64);
503	LIMIT_CASE(max_transform_feedback_separate_attribs,			GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,			GLint,				4);
504	LIMIT_CASE(max_transform_feedback_separate_components,		GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,		GLint,				4);
505}
506
507} // Functional
508} // gles3
509} // deqp
510