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 "esextcGeometryShaderPrimitiveQueries.hpp"
25
26#include "gluDefs.hpp"
27#include "glwEnums.hpp"
28#include "glwFunctions.hpp"
29#include "tcuTestLog.hpp"
30
31namespace glcts
32{
33
34/* Fragment shader */
35const char* GeometryShaderPrimitiveQueries::m_fs_code = "${VERSION}\n"
36														"\n"
37														"precision highp float;\n"
38														"\n"
39														"void main()\n"
40														"{\n"
41														"}\n";
42
43/* Vertex shader */
44const char* GeometryShaderPrimitiveQueries::m_vs_code = "${VERSION}\n"
45														"\n"
46														"precision highp float;\n"
47														"\n"
48														"void main()\n"
49														"{\n"
50														"    gl_Position = vec4(gl_VertexID, 0, 0, 1);\n"
51														"}\n";
52
53/* Geometry shader */
54const char* GeometryShaderPrimitiveQueriesPoints::m_gs_code =
55	"${VERSION}\n"
56	"\n"
57	"${GEOMETRY_SHADER_REQUIRE}\n"
58	"\n"
59	"precision highp float;\n"
60	"\n"
61	"layout(points)                          in;\n"
62	"layout(points, max_vertices=8)          out;\n"
63	"\n"
64	"void main()\n"
65	"{\n"
66	"    for (int n = 0; n < 8; ++n)\n"
67	"    {\n"
68	"        gl_Position = vec4(1.0 / (float(n) + 1.0), 1.0 / (float(n) + 2.0), 0.0, 1.0);\n"
69	"        EmitVertex();\n"
70	"    }\n"
71	"\n"
72	"    EndPrimitive();\n"
73	"}\n";
74
75/* Geometry shader */
76const char* GeometryShaderPrimitiveQueriesLines::m_gs_code =
77	"${VERSION}\n"
78	"\n"
79	"${GEOMETRY_SHADER_REQUIRE}\n"
80	"\n"
81	"precision highp float;\n"
82	"\n"
83	"layout(points)                          in;\n"
84	"layout(line_strip, max_vertices=10)     out;\n"
85	"\n"
86	"void main()\n"
87	"{\n"
88	"    for (int n = 0; n < 10; ++n)\n"
89	"    {\n"
90	"        gl_Position = vec4(1.0 / (float(n) + 1.0), 1.0 / (float(n) + 2.0), 0.0, 1.0);\n"
91	"        EmitVertex();\n"
92	"    }\n"
93	"\n"
94	"    EndPrimitive();\n"
95	"}\n";
96
97/* Geometry shader */
98const char* GeometryShaderPrimitiveQueriesTriangles::m_gs_code =
99	"${VERSION}\n"
100	"\n"
101	"${GEOMETRY_SHADER_REQUIRE}\n"
102	"\n"
103	"precision highp float;\n"
104	"\n"
105	"layout(points)                          in;\n"
106	"layout(triangle_strip, max_vertices=12) out;\n"
107	"\n"
108	"void main()\n"
109	"{\n"
110	"    for (int n = 0; n < 12; ++n)\n"
111	"    {\n"
112	"        gl_Position = vec4(1.0 / (float(n) + 1.0), 1.0 / (float(n) + 2.0), 0.0, 1.0);\n"
113	"        EmitVertex();\n"
114	"    }\n"
115	"\n"
116	"    EndPrimitive();\n"
117	"}\n";
118
119/** Constructor
120 *
121 * @param context       Test context
122 * @param name          Test case's name
123 * @param description   Test case's description
124 **/
125GeometryShaderPrimitiveQueriesPoints::GeometryShaderPrimitiveQueriesPoints(Context&				context,
126																		   const ExtParameters& extParams,
127																		   const char* name, const char* description)
128	: GeometryShaderPrimitiveQueries(context, extParams, name, description)
129{
130}
131
132/** Gets geometry shader code
133 *
134 * @return geometry shader code
135 **/
136const char* GeometryShaderPrimitiveQueriesPoints::getGeometryShaderCode()
137{
138	return m_gs_code;
139}
140
141/** Gets the number of emitted vertices
142 *
143 * @return number of emitted vertices
144 **/
145glw::GLint GeometryShaderPrimitiveQueriesPoints::getAmountOfEmittedVertices()
146{
147	return 8;
148}
149
150/** Gets the transform feedback mode
151 *
152 * @return transform feedback mode
153 **/
154glw::GLenum GeometryShaderPrimitiveQueriesPoints::getTFMode()
155{
156	return GL_POINTS;
157}
158
159/** Constructor
160 *
161 * @param context       Test context
162 * @param name          Test case's name
163 * @param description   Test case's desricption
164 **/
165GeometryShaderPrimitiveQueriesLines::GeometryShaderPrimitiveQueriesLines(Context&			  context,
166																		 const ExtParameters& extParams,
167																		 const char* name, const char* description)
168	: GeometryShaderPrimitiveQueries(context, extParams, name, description)
169{
170}
171
172/** Gets geometry shader code
173 *
174 * @return geometry shader code
175 **/
176const char* GeometryShaderPrimitiveQueriesLines::getGeometryShaderCode()
177{
178	return m_gs_code;
179}
180
181/** Gets the number of emitted vertices
182 *
183 * @return number of emitted vertices
184 **/
185glw::GLint GeometryShaderPrimitiveQueriesLines::getAmountOfEmittedVertices()
186{
187	return 18;
188}
189
190/** Gets the transform feedback mode
191 *
192 * @return transform feedback mode
193 **/
194glw::GLenum GeometryShaderPrimitiveQueriesLines::getTFMode()
195{
196	return GL_LINES;
197}
198
199/** Constructor
200 *
201 * @param context       Test context
202 * @param name          Test case's name
203 * @param description   Test case's desricption
204 **/
205GeometryShaderPrimitiveQueriesTriangles::GeometryShaderPrimitiveQueriesTriangles(Context&			  context,
206																				 const ExtParameters& extParams,
207																				 const char*		  name,
208																				 const char*		  description)
209	: GeometryShaderPrimitiveQueries(context, extParams, name, description)
210{
211}
212
213/** Gets geometry shader code
214 *
215 * @return geometry shader code
216 **/
217const char* GeometryShaderPrimitiveQueriesTriangles::getGeometryShaderCode()
218{
219	return m_gs_code;
220}
221
222/** Gets the number of emitted vertices
223 *
224 * @return number of emitted vertices
225 **/
226glw::GLint GeometryShaderPrimitiveQueriesTriangles::getAmountOfEmittedVertices()
227{
228	return 30;
229}
230
231/** Gets the transform feedback mode
232 *
233 * @return transform feedback mode
234 **/
235glw::GLenum GeometryShaderPrimitiveQueriesTriangles::getTFMode()
236{
237	return GL_TRIANGLES;
238}
239
240/** Constructor
241 *
242 * @param context       Test context
243 * @param name          Test case's name
244 * @param description   Test case's desricption
245 **/
246GeometryShaderPrimitiveQueries::GeometryShaderPrimitiveQueries(Context& context, const ExtParameters& extParams,
247															   const char* name, const char* description)
248	: TestCaseBase(context, extParams, name, description)
249	, m_n_texture_components(4)
250	, m_bo_large_id(0)
251	, m_bo_small_id(0)
252	, m_fs_id(0)
253	, m_gs_id(0)
254	, m_po_id(0)
255	, m_qo_primitives_generated_id(0)
256	, m_qo_tf_primitives_written_id(0)
257	, m_vao_id(0)
258	, m_vs_id(0)
259{
260	/* Nothing to be done here */
261}
262
263/** Deinitializes GLES objects created during the test.
264 *
265 */
266void GeometryShaderPrimitiveQueries::deinit(void)
267{
268	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
269
270	/* Reset OpenGL ES state */
271	gl.useProgram(0);
272	gl.bindBuffer(GL_ARRAY_BUFFER, 0);
273	gl.bindVertexArray(0);
274
275	if (m_po_id != 0)
276	{
277		gl.deleteProgram(m_po_id);
278	}
279
280	if (m_fs_id != 0)
281	{
282		gl.deleteShader(m_fs_id);
283	}
284
285	if (m_gs_id != 0)
286	{
287		gl.deleteShader(m_gs_id);
288	}
289
290	if (m_vs_id != 0)
291	{
292		gl.deleteShader(m_vs_id);
293	}
294
295	if (m_bo_small_id != 0)
296	{
297		gl.deleteBuffers(1, &m_bo_small_id);
298	}
299
300	if (m_bo_large_id != 0)
301	{
302		gl.deleteBuffers(1, &m_bo_large_id);
303	}
304
305	if (m_qo_primitives_generated_id != 0)
306	{
307		gl.deleteQueries(1, &m_qo_primitives_generated_id);
308	}
309
310	if (m_qo_tf_primitives_written_id != 0)
311	{
312		gl.deleteQueries(1, &m_qo_tf_primitives_written_id);
313	}
314
315	if (m_vao_id != 0)
316	{
317		gl.deleteVertexArrays(1, &m_vao_id);
318	}
319
320	/* Release base class */
321	TestCaseBase::deinit();
322}
323
324/** Executes the test.
325 *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
326 *  @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again.
327 *  Note the function throws exception should an error occur!
328 **/
329tcu::TestNode::IterateResult GeometryShaderPrimitiveQueries::iterate(void)
330{
331	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
332
333	/* Check if geometry_shader extension is supported */
334	if (!m_is_geometry_shader_extension_supported)
335	{
336		throw tcu::NotSupportedError(GEOMETRY_SHADER_EXTENSION_NOT_SUPPORTED, "", __FILE__, __LINE__);
337	}
338
339	/* Create shader objects and a program object */
340	m_vs_id = gl.createShader(GL_VERTEX_SHADER);
341	m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
342	m_gs_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER);
343	m_po_id = gl.createProgram();
344
345	GLU_EXPECT_NO_ERROR(gl.getError(), "Error creating program/shader objects.");
346
347	/* Try to build test-specific program object */
348	const char* tf_varyings[] = { "gl_Position" };
349	const char* gs_code		  = getGeometryShaderCode();
350
351	gl.transformFeedbackVaryings(m_po_id, sizeof(tf_varyings) / sizeof(tf_varyings[0]), tf_varyings,
352								 GL_SEPARATE_ATTRIBS);
353
354	if (!buildProgram(m_po_id, m_fs_id, 1 /* part */, &m_fs_code, m_gs_id, 1 /* part */, &gs_code, m_vs_id,
355					  1 /* part */, &m_vs_code))
356	{
357		TCU_FAIL("Could not create a program for GeometryShaderPrimitiveQueries!");
358	}
359
360	/* Create and bind a vertex array object */
361	gl.genVertexArrays(1, &m_vao_id);
362	gl.bindVertexArray(m_vao_id);
363
364	GLU_EXPECT_NO_ERROR(gl.getError(), "Error creating a vertex array object.");
365
366	/* Create two buffer objects
367	 *
368	 * One with sufficiently large storage space to hold position data for particular output.
369	 * Another one of insufficient size.
370	 */
371	gl.genBuffers(1, &m_bo_large_id);
372	gl.genBuffers(1, &m_bo_small_id);
373
374	gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_large_id);
375	gl.bufferData(GL_ARRAY_BUFFER,
376				  getAmountOfEmittedVertices() * m_n_texture_components /* components */ * sizeof(float), NULL,
377				  GL_STATIC_DRAW);
378
379	gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_small_id);
380	gl.bufferData(GL_ARRAY_BUFFER,
381				  (getAmountOfEmittedVertices() / 2) * m_n_texture_components /* components */ * sizeof(float), NULL,
382				  GL_STATIC_DRAW);
383
384	GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating buffer objects.");
385
386	/* Create primitive query objects */
387	gl.genQueries(1, &m_qo_tf_primitives_written_id);
388	gl.genQueries(1, &m_qo_primitives_generated_id);
389
390	glw::GLuint nPrimitivesGenerated = 0;
391	glw::GLuint nTFPrimitivesWritten = 0;
392
393	/* Test case 13.1 */
394	readPrimitiveQueryValues(m_bo_large_id, &nPrimitivesGenerated, &nTFPrimitivesWritten);
395
396	if (nPrimitivesGenerated == 0)
397	{
398		m_testCtx.getLog()
399			<< tcu::TestLog::Message
400			<< "Retrieved GL_PRIMITIVES_GENERATED_EXT query object value is zero which should never happen."
401			<< tcu::TestLog::EndMessage;
402
403		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
404		return STOP;
405	}
406
407	if (nTFPrimitivesWritten == 0)
408	{
409		m_testCtx.getLog() << tcu::TestLog::Message << "Retrieved GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query "
410													   "object value is zero which should never happen."
411						   << tcu::TestLog::EndMessage;
412
413		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
414		return STOP;
415	}
416
417	if (nPrimitivesGenerated != nTFPrimitivesWritten)
418	{
419		m_testCtx.getLog() << tcu::TestLog::Message << "Retrieved GL_PRIMITIVES_GENERATED_EXT query object value("
420						   << nPrimitivesGenerated
421						   << ") is different than for GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN ("
422						   << nTFPrimitivesWritten << ")" << tcu::TestLog::EndMessage;
423
424		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
425		return STOP;
426	}
427
428	/* Test case 13.2 */
429	nPrimitivesGenerated = 0;
430	nTFPrimitivesWritten = 0;
431
432	readPrimitiveQueryValues(m_bo_small_id, &nPrimitivesGenerated, &nTFPrimitivesWritten);
433
434	if (nPrimitivesGenerated == 0)
435	{
436		m_testCtx.getLog()
437			<< tcu::TestLog::Message
438			<< "Retrieved GL_PRIMITIVES_GENERATED_EXT query object value is zero which should never happen."
439			<< tcu::TestLog::EndMessage;
440
441		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
442		return STOP;
443	}
444
445	if (nTFPrimitivesWritten == 0)
446	{
447		m_testCtx.getLog() << tcu::TestLog::Message << "Retrieved GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query "
448													   "object value is zero which should never happen."
449						   << tcu::TestLog::EndMessage;
450
451		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
452		return STOP;
453	}
454
455	if ((nPrimitivesGenerated / 2) != nTFPrimitivesWritten)
456	{
457		m_testCtx.getLog() << tcu::TestLog::Message
458						   << "Retrieved GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query object value("
459						   << nPrimitivesGenerated << ") should be half the amount of GL_PRIMITIVES_GENERATED_EXT ("
460						   << nTFPrimitivesWritten << ")" << tcu::TestLog::EndMessage;
461
462		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
463		return STOP;
464	}
465
466	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
467	return STOP;
468}
469
470/**
471 * The function binds the provided bufferId to transform feedback target and sets up a query
472 * for GL_PRIMITIVES_GENERATED_EXT GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN.
473 * It then issues a draw call to execute the vertex and geometry shaders. Then results
474 * of the queries are written to nPrimitivesGenerated and nPrimitivesWritten variables.
475 *
476 * @param context bufferId   id of the buffer to be bound to transform feedback target
477 * @return nPrimitivesGenerated      the result of GL_PRIMITIVES_GENERATED_EXT query
478 * @return nPrimitivesWritten        the result of GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query
479 */
480void GeometryShaderPrimitiveQueries::readPrimitiveQueryValues(glw::GLint bufferId, glw::GLuint* nPrimitivesGenerated,
481															  glw::GLuint* nPrimitivesWritten)
482{
483	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
484
485	/* Bind the buffer object to hold the captured data.*/
486	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bufferId);
487
488	/* Activate the program object */
489	gl.useProgram(m_po_id);
490	GLU_EXPECT_NO_ERROR(gl.getError(), "Error using program object");
491
492	gl.beginTransformFeedback(getTFMode());
493	GLU_EXPECT_NO_ERROR(gl.getError(), "Error starting transform feedback");
494
495	/* Activate the queries */
496	gl.beginQuery(m_glExtTokens.PRIMITIVES_GENERATED, m_qo_primitives_generated_id);
497	GLU_EXPECT_NO_ERROR(gl.getError(), "Error starting GL_PRIMITIVES_GENERATED_EXT query");
498
499	gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, m_qo_tf_primitives_written_id);
500	GLU_EXPECT_NO_ERROR(gl.getError(), "Error starting GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query");
501
502	/* Render */
503	gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
504	GLU_EXPECT_NO_ERROR(gl.getError(), "Error executing draw call");
505
506	gl.endTransformFeedback();
507	GLU_EXPECT_NO_ERROR(gl.getError(), "Error finishing transform feedback");
508
509	/* Query objects end here. */
510	gl.endQuery(m_glExtTokens.PRIMITIVES_GENERATED);
511	gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
512
513	/* Retrieve query values */
514	gl.getQueryObjectuiv(m_qo_primitives_generated_id, GL_QUERY_RESULT, nPrimitivesGenerated);
515	gl.getQueryObjectuiv(m_qo_tf_primitives_written_id, GL_QUERY_RESULT, nPrimitivesWritten);
516
517	GLU_EXPECT_NO_ERROR(gl.getError(), "Error retrieving query values.");
518}
519
520} // namespace glcts
521