1/*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2014-2016 The Khronos Group Inc.
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
22 */ /*-------------------------------------------------------------------*/
23
24#include "esextcTessellationShaderProgramInterfaces.hpp"
25#include "gluContextInfo.hpp"
26#include "gluDefs.hpp"
27#include "glwEnums.hpp"
28#include "glwFunctions.hpp"
29#include "tcuTestLog.hpp"
30
31namespace glcts
32{
33/** Constructor
34 *
35 * @param context       Test context
36 * @param name          Test case's name
37 * @param description   Test case's desricption
38 **/
39TessellationShaderProgramInterfaces::TessellationShaderProgramInterfaces(Context&			  context,
40																		 const ExtParameters& extParams)
41	: TestCaseBase(context, extParams, "ext_program_interface_query_dependency",
42				   "Verifies EXT_program_interface_query works correctly for tessellation"
43				   " control and tessellation evaluation shaders")
44	, m_fs_shader_id(0)
45	, m_po_id(0)
46	, m_tc_shader_id(0)
47	, m_te_shader_id(0)
48	, m_vs_shader_id(0)
49	, m_is_atomic_counters_supported(false)
50	, m_is_shader_storage_blocks_supported(false)
51{
52	/* Left blank on purpose */
53}
54
55/** Deinitializes all ES objects created for the test. */
56void TessellationShaderProgramInterfaces::deinit()
57{
58	/** Call base class' deinit() function */
59	TestCaseBase::deinit();
60
61	/* Release all objects we might've created */
62	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
63
64	if (m_fs_shader_id != 0)
65	{
66		gl.deleteShader(m_fs_shader_id);
67
68		m_fs_shader_id = 0;
69	}
70
71	if (m_po_id != 0)
72	{
73		gl.deleteProgram(m_po_id);
74
75		m_po_id = 0;
76	}
77
78	if (m_tc_shader_id != 0)
79	{
80		gl.deleteShader(m_tc_shader_id);
81
82		m_tc_shader_id = 0;
83	}
84
85	if (m_te_shader_id != 0)
86	{
87		gl.deleteShader(m_te_shader_id);
88
89		m_te_shader_id = 0;
90	}
91
92	if (m_vs_shader_id != 0)
93	{
94		gl.deleteShader(m_vs_shader_id);
95
96		m_vs_shader_id = 0;
97	}
98}
99
100/** Initializes all ES objects that will be used for the test. */
101void TessellationShaderProgramInterfaces::initTest()
102{
103	/* The test requires EXT_tessellation_shader and EXT_program_interfaces_query extensions */
104	if (!m_is_tessellation_shader_supported || !m_is_program_interface_query_supported)
105	{
106		return;
107	}
108
109	/* Generate a program object we will later configure */
110	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
111
112	m_po_id = gl.createProgram();
113
114	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed");
115
116	/* Generate shader objects the test will use */
117	m_fs_shader_id = gl.createShader(GL_FRAGMENT_SHADER);
118	m_tc_shader_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
119	m_te_shader_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
120	m_vs_shader_id = gl.createShader(GL_VERTEX_SHADER);
121
122	GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed");
123
124	glw::GLint gl_max_tess_control_shader_storage_blocks;
125	glw::GLint gl_max_tess_control_atomic_counter_buffers;
126	glw::GLint gl_max_tess_control_atomic_counters;
127	glw::GLint gl_max_tess_evaluation_shader_storage_blocks;
128	glw::GLint gl_max_tess_evaluation_atomic_counter_buffers;
129	glw::GLint gl_max_tess_evaluation_atomic_counters;
130
131	gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, &gl_max_tess_control_atomic_counter_buffers);
132	GLU_EXPECT_NO_ERROR(gl.getError(),
133						"glGetIntegerv() failed for GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT pname");
134	gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_ATOMIC_COUNTERS, &gl_max_tess_control_atomic_counters);
135	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT pname");
136	gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, &gl_max_tess_control_shader_storage_blocks);
137	GLU_EXPECT_NO_ERROR(gl.getError(),
138						"glGetIntegerv() failed for GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT pname");
139	gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS,
140				   &gl_max_tess_evaluation_atomic_counter_buffers);
141	GLU_EXPECT_NO_ERROR(gl.getError(),
142						"glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT pname");
143	gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_ATOMIC_COUNTERS, &gl_max_tess_evaluation_atomic_counters);
144	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT pname");
145	gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS,
146				   &gl_max_tess_evaluation_shader_storage_blocks);
147	GLU_EXPECT_NO_ERROR(gl.getError(),
148						"glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT pname");
149
150	m_is_atomic_counters_supported =
151		(gl_max_tess_control_atomic_counter_buffers > 1) && (gl_max_tess_evaluation_atomic_counter_buffers > 1) &&
152		(gl_max_tess_control_atomic_counters > 0) && (gl_max_tess_evaluation_atomic_counters > 0);
153
154	m_is_shader_storage_blocks_supported =
155		(gl_max_tess_control_shader_storage_blocks > 0) && (gl_max_tess_evaluation_shader_storage_blocks > 0);
156
157	const char* atomic_counters_header = NULL;
158	if (m_is_atomic_counters_supported)
159	{
160		atomic_counters_header = "#define USE_ATOMIC_COUNTERS 1\n";
161	}
162	else
163	{
164		atomic_counters_header = "\n";
165	}
166
167	const char* shader_storage_blocks_header = NULL;
168	if (m_is_shader_storage_blocks_supported)
169	{
170		shader_storage_blocks_header = "#define USE_SHADER_STORAGE_BLOCKS\n";
171	}
172	else
173	{
174		shader_storage_blocks_header = "\n";
175	}
176
177	/* Build shader program */
178	const char* fs_body = "${VERSION}\n"
179						  "\n"
180						  "precision highp float;\n"
181						  "\n"
182						  "out vec4 test_output;\n"
183						  "\n"
184						  "void main()\n"
185						  "{\n"
186						  "    test_output = vec4(1, 0, 0, 0);\n"
187						  "}\n";
188
189	const char* tc_body = "\n"
190						  "layout (vertices = 1) out;\n"
191						  "\n"
192						  /* Uniforms */
193						  "uniform vec2 tc_uniform1;\n"
194						  "uniform mat4 tc_uniform2;\n"
195						  "\n"
196						  /* Uniform blocks */
197						  "uniform tc_uniform_block1\n"
198						  "{\n"
199						  "    float tc_uniform_block1_1;\n"
200						  "};\n"
201						  /* Atomic counter buffers */
202						  "#ifdef USE_ATOMIC_COUNTERS\n"
203						  "layout(binding = 1, offset = 0) uniform atomic_uint tc_atomic_counter1;\n"
204						  "#endif\n"
205						  /* Shader storage blocks & buffer variables */
206						  "#ifdef USE_SHADER_STORAGE_BLOCKS\n"
207						  "layout(std140, binding = 0) buffer tc_shader_storage_block1\n"
208						  "{\n"
209						  "    vec4 tc_shader_storage_buffer_object_1[];\n"
210						  "};\n"
211						  "#endif\n"
212						  /* Body */
213						  "void main()\n"
214						  "{\n"
215						  "    int test = 1;\n"
216						  "\n"
217						  /* Uniforms */
218						  "    if (tc_uniform1.x    == 0.0) test = 2;\n"
219						  "    if (tc_uniform2[0].y == 1.0) test = 3;\n"
220						  /* Uniform blocks */
221						  "    if (tc_uniform_block1_1 == 3.0) test = 4;\n"
222						  /* Atomic counter buffers */
223						  "#ifdef USE_ATOMIC_COUNTERS\n"
224						  "    if (atomicCounter(tc_atomic_counter1) == 1u) test = 5;\n"
225						  "#endif\n"
226						  /* Shader storage blocks & buffer variables */
227						  "#ifdef USE_SHADER_STORAGE_BLOCKS\n"
228						  "    if (tc_shader_storage_buffer_object_1[0].x == 0.0) test = 6;\n"
229						  "#endif\n"
230						  "\n"
231						  "    gl_out           [gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
232						  "    gl_TessLevelOuter[0]                           = 2.0 * float(test);\n"
233						  "    gl_TessLevelOuter[1]                           = 3.0;\n"
234						  "}\n";
235
236	const char* tc_code[] = { "${VERSION}\n",
237							  /* Required EXT_tessellation_shader functionality */
238							  "${TESSELLATION_SHADER_REQUIRE}\n", atomic_counters_header, shader_storage_blocks_header,
239							  tc_body };
240
241	const char* te_body = "\n"
242						  "layout (isolines, ccw, equal_spacing, point_mode) in;\n"
243						  "\n"
244						  /* Uniforms */
245						  "uniform vec2 te_uniform1;\n"
246						  "uniform mat4 te_uniform2;\n"
247						  "\n"
248						  /* Uniform blocks */
249						  "uniform te_uniform_block1\n"
250						  "{\n"
251						  "    float te_uniform_block1_1;\n"
252						  "};\n"
253						  /* Atomic counter buffers */
254						  "#ifdef USE_ATOMIC_COUNTERS\n"
255						  "layout(binding = 2, offset = 0) uniform atomic_uint te_atomic_counter1;\n"
256						  "#endif\n"
257						  /* Shader storage blocks & buffer variables */
258						  "#ifdef USE_SHADER_STORAGE_BLOCKS\n"
259						  "layout(std140, binding = 0) buffer te_shader_storage_block1\n"
260						  "{\n"
261						  "    vec4 te_shader_storage_buffer_object_1[];\n"
262						  "};\n"
263						  "#endif\n"
264						  "void main()\n"
265						  "{\n"
266						  "    int test = 1;\n"
267						  "\n"
268						  /* Uniforms */
269						  "    if (te_uniform1.x    == 0.0) test = 2;\n"
270						  "    if (te_uniform2[0].y == 1.0) test = 3;\n"
271						  /* Uniform blocks */
272						  "    if (te_uniform_block1_1 == 3.0) test = 4;\n"
273						  /* Atomic counter buffers */
274						  "#ifdef USE_ATOMIC_COUNTERS\n"
275						  "    if (atomicCounter(te_atomic_counter1) == 1u) test = 5;\n"
276						  "#endif\n"
277						  /* Shader storage blocks & buffer variables */
278						  "#ifdef USE_SHADER_STORAGE_BLOCKS\n"
279						  "   if (te_shader_storage_buffer_object_1[0].x == 0.0) test = 6;\n"
280						  "#endif\n"
281						  "\n"
282						  "    gl_Position = gl_in[0].gl_Position * float(test);\n"
283						  "}\n";
284
285	const char* te_code[] = { "${VERSION}\n",
286							  /* Required EXT_tessellation_shader functionality */
287							  "${TESSELLATION_SHADER_REQUIRE}\n", atomic_counters_header, shader_storage_blocks_header,
288							  te_body };
289
290	const char* vs_body = "${VERSION}\n"
291						  "\n"
292						  "in vec4 test_input;\n"
293						  "\n"
294						  "void main()\n"
295						  "{\n"
296						  "    gl_Position = vec4(gl_VertexID, test_input.y, 0, 1);\n"
297						  "}\n";
298
299	bool link_success = buildProgram(m_po_id, m_fs_shader_id, 1, &fs_body, m_tc_shader_id, 5, tc_code, m_te_shader_id,
300									 5, te_code, m_vs_shader_id, 1, &vs_body);
301
302	if (!link_success)
303	{
304		TCU_FAIL("Program compilation and linking failed");
305	}
306
307	/* We're good to execute the test! */
308}
309
310/** Executes the test.
311 *
312 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
313 *
314 *  Note the function throws exception should an error occur!
315 *
316 *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
317 **/
318tcu::TestNode::IterateResult TessellationShaderProgramInterfaces::iterate(void)
319{
320	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
321
322	/* Do not execute if required extensions are not supported. */
323	if (!m_is_tessellation_shader_supported)
324	{
325		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
326	}
327
328	/* Initialize ES objects needed to run the test */
329	initTest();
330
331	/* Iterate through all interfaces */
332	const glw::GLenum interfaces[] = {
333		GL_UNIFORM,			GL_UNIFORM_BLOCK, GL_ATOMIC_COUNTER_BUFFER, GL_SHADER_STORAGE_BLOCK,
334		GL_BUFFER_VARIABLE, GL_PROGRAM_INPUT, GL_PROGRAM_OUTPUT
335	};
336	const unsigned int n_interfaces = sizeof(interfaces) / sizeof(interfaces[0]);
337
338	for (unsigned int n_interface = 0; n_interface < n_interfaces; ++n_interface)
339	{
340		glw::GLenum interface = interfaces[n_interface];
341
342		if ((interface == GL_SHADER_STORAGE_BLOCK || interface == GL_BUFFER_VARIABLE) &&
343			!m_is_shader_storage_blocks_supported)
344		{
345			continue;
346		}
347
348		if (interface == GL_ATOMIC_COUNTER_BUFFER && !m_is_atomic_counters_supported)
349		{
350			continue;
351		}
352
353		/* For each interface, we want to check whether a specific resource
354		 * is recognized by the implementation. If the name is unknown,
355		 * the test should fail; if it's recognized, we should verify it's referenced
356		 * by both TC and TE shaders
357		 */
358		const char* tc_resource_name = DE_NULL;
359		const char* te_resource_name = DE_NULL;
360
361		switch (interface)
362		{
363		case GL_UNIFORM:
364		{
365			tc_resource_name = "tc_uniform1";
366			te_resource_name = "te_uniform1";
367
368			break;
369		}
370
371		case GL_UNIFORM_BLOCK:
372		{
373			tc_resource_name = "tc_uniform_block1";
374			te_resource_name = "te_uniform_block1";
375
376			break;
377		}
378
379		case GL_ATOMIC_COUNTER_BUFFER:
380		{
381			/* Atomic counter buffers are tested in a separate codepath. */
382			break;
383		}
384
385		case GL_SHADER_STORAGE_BLOCK:
386		{
387			tc_resource_name = "tc_shader_storage_block1";
388			te_resource_name = "te_shader_storage_block1";
389
390			break;
391		}
392
393		case GL_BUFFER_VARIABLE:
394		{
395			tc_resource_name = "tc_shader_storage_buffer_object_1";
396			te_resource_name = "te_shader_storage_buffer_object_1";
397
398			break;
399		}
400
401		case GL_PROGRAM_INPUT:
402		{
403			tc_resource_name = DE_NULL;
404			te_resource_name = DE_NULL;
405
406			break;
407		}
408
409		case GL_PROGRAM_OUTPUT:
410		{
411			tc_resource_name = DE_NULL;
412			te_resource_name = DE_NULL;
413
414			break;
415		}
416
417		default:
418		{
419			TCU_FAIL("Unrecognized interface type");
420		}
421		} /* switch (interface) */
422
423		/* Run in two iterations - first for TC, then for TE */
424		for (int n_iteration = 0; n_iteration < 2; ++n_iteration)
425		{
426			glw::GLenum property = (n_iteration == 0) ? m_glExtTokens.REFERENCED_BY_TESS_CONTROL_SHADER :
427														m_glExtTokens.REFERENCED_BY_TESS_EVALUATION_SHADER;
428
429			if (interface == GL_ATOMIC_COUNTER_BUFFER)
430			{
431				/* We only need a single iteration run for this interface */
432				if (n_iteration == 1)
433				{
434					continue;
435				}
436
437				/* Atomic counter buffers are not assigned names, hence they need to be
438				 * tested slightly differently.
439				 *
440				 * Exactly two atomic counter buffers should be defined. Make sure that's the case.
441				 */
442				glw::GLint n_active_resources = 0;
443
444				gl.getProgramInterfaceiv(m_po_id, interface, GL_ACTIVE_RESOURCES, &n_active_resources);
445				GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInterfaceiv() failed.");
446
447				if (n_active_resources != 2)
448				{
449					TCU_FAIL("Invalid amount of atomic counter buffer binding points reported");
450				}
451
452				/* Check both resources and make sure they report separate atomic counters */
453				bool was_tc_atomic_counter_buffer_reported = false;
454				bool was_te_atomic_counter_buffer_reported = false;
455
456				for (int n_resource = 0; n_resource < n_active_resources; ++n_resource)
457				{
458					const glw::GLenum tc_property		= m_glExtTokens.REFERENCED_BY_TESS_CONTROL_SHADER;
459					glw::GLint		  tc_property_value = 0;
460					const glw::GLenum te_property		= m_glExtTokens.REFERENCED_BY_TESS_EVALUATION_SHADER;
461					glw::GLint		  te_property_value = 0;
462
463					gl.getProgramResourceiv(m_po_id, interface, n_resource, 1, /* propCount */
464											&tc_property, 1,				   /* bufSize */
465											NULL,							   /* length */
466											&tc_property_value);
467					GLU_EXPECT_NO_ERROR(
468						gl.getError(),
469						"glGetProgramResourceiv() failed for GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT property");
470
471					gl.getProgramResourceiv(m_po_id, interface, n_resource, 1, /* propCount */
472											&te_property, 1,				   /* bufSize */
473											NULL,							   /* length */
474											&te_property_value);
475					GLU_EXPECT_NO_ERROR(
476						gl.getError(),
477						"glGetProgramResourceiv() failed for GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT property");
478
479					if (tc_property_value == GL_TRUE)
480					{
481						if (was_tc_atomic_counter_buffer_reported)
482						{
483							TCU_FAIL("Tessellation control-specific atomic counter buffer is reported twice");
484						}
485
486						was_tc_atomic_counter_buffer_reported = true;
487					}
488
489					if (te_property_value == GL_TRUE)
490					{
491						if (was_te_atomic_counter_buffer_reported)
492						{
493							TCU_FAIL("Tessellation evaluation-specific atomic counter buffer is reported twice");
494						}
495
496						was_te_atomic_counter_buffer_reported = true;
497					}
498				} /* for (all active resources) */
499
500				if (!was_tc_atomic_counter_buffer_reported || !was_te_atomic_counter_buffer_reported)
501				{
502					TCU_FAIL("Either tessellation control or tessellation evaluation atomic counter buffer was not "
503							 "reported");
504				}
505			}
506			else
507			{
508				/* Retrieve resource index first, as long as the name is not NULL.
509				 * If it's NULL, the property's value is assumed to be GL_FALSE for
510				 * all reported active resources.
511				 **/
512				const char* resource_name = (n_iteration == 0) ? tc_resource_name : te_resource_name;
513
514				if (resource_name == DE_NULL)
515				{
516					/* Make sure the property has GL_FALSE value for any resources
517					 * reported for this interface. */
518					glw::GLint n_active_resources = 0;
519
520					gl.getProgramInterfaceiv(m_po_id, interface, GL_ACTIVE_RESOURCES, &n_active_resources);
521					GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInterfaceiv() failed.");
522
523					for (glw::GLint n_resource = 0; n_resource < n_active_resources; ++n_resource)
524					{
525						verifyPropertyValue(interface, property, n_resource, GL_FALSE);
526					} /* for (all resource indices) */
527				}
528				else
529				{
530					glw::GLuint resource_index = gl.getProgramResourceIndex(m_po_id, interface, resource_name);
531					GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceIndex() failed");
532
533					if (resource_index == GL_INVALID_INDEX)
534					{
535						m_testCtx.getLog() << tcu::TestLog::Message << "Resource [" << resource_name
536										   << "] was not recognized." << tcu::TestLog::EndMessage;
537
538						TCU_FAIL("Resource not recognized.");
539					}
540
541					/* Now that we know the index, we can check the GL_REFERENCED_BY_...
542					 * property value */
543					verifyPropertyValue(interface, property, resource_index, GL_TRUE);
544				}
545			} /* (interface != GL_ATOMIC_COUNTER_BUFFER) */
546		}	 /* for (both iterations) */
547	}		  /* for (all interfaces) */
548
549	/* All done */
550	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
551	return STOP;
552}
553
554/** Checks if a property value reported for user-specified program object interface
555 *  at given index is as expected.
556 *
557 *  NOTE: This function throws TestError exception if retrieved value does not
558 *        match @param expected_value.
559 *
560 *  @param interface      Program object interface to use for the query;
561 *  @param property       Interface property to check;
562 *  @param index          Property index to use for the test;
563 *  @param expected_value Value that is expected to be reported by ES implementation.
564 **/
565void TessellationShaderProgramInterfaces::verifyPropertyValue(glw::GLenum interface, glw::GLenum property,
566															  glw::GLuint index, glw::GLint expected_value)
567{
568	const glw::Functions& gl			 = m_context.getRenderContext().getFunctions();
569	glw::GLint			  property_value = 0;
570
571	gl.getProgramResourceiv(m_po_id, interface, index, 1, /* propCount */
572							&property, 1,				  /* bufSize */
573							NULL,						  /* length */
574							&property_value);
575	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv() failed");
576
577	if (property_value != expected_value)
578	{
579		TCU_FAIL("Invalid GL_REFERENCED_BY_... property value reported");
580	}
581}
582
583} /* namespace glcts */
584