• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "esextcGPUShader5FmaAccuracy.hpp"
25 
26 #include "gluContextInfo.hpp"
27 #include "glwEnums.hpp"
28 #include "glwFunctions.hpp"
29 #include "tcuTestLog.hpp"
30 
31 #include <vector>
32 
33 namespace glcts
34 {
35 /* Fragment Shader */
36 const glw::GLchar* const GPUShader5FmaAccuracyTest::m_fragment_shader_code =
37 	"${VERSION}\n"
38 	"\n"
39 	"${GPU_SHADER5_REQUIRE}\n"
40 	"\n"
41 	"precision highp float;\n"
42 	"\n"
43 	"layout(location = 0) out vec4 fs_out_color;\n"
44 	"\n"
45 	"void main()\n"
46 	"{\n"
47 	"    fs_out_color = vec4(1, 1, 1, 1);\n"
48 	"}\n";
49 
50 /* Vertex Shader for fma pass */
51 const glw::GLchar* const GPUShader5FmaAccuracyTest::m_vertex_shader_code_for_fma_pass =
52 	"${VERSION}\n"
53 	"\n"
54 	"${GPU_SHADER5_REQUIRE}\n"
55 	"\n"
56 	"precision highp float;\n"
57 	"\n"
58 	"uniform uint  uni_number_of_steps;\n"
59 	"out     float vs_fs_result;\n"
60 	"\n"
61 	"void main()\n"
62 	"{\n"
63 	"    float border_case   = 1.0;\n"
64 	"    float h             = 1.0 / float(uni_number_of_steps);\n"
65 	"    float current_value = border_case;\n"
66 	"\n"
67 	"    for (uint step = 0u; step < uni_number_of_steps; ++step)\n"
68 	"    {\n"
69 	"        float next_value = fma(h, current_value, current_value);\n"
70 	"\n"
71 	"        current_value = next_value;\n"
72 	"    }\n"
73 	"\n"
74 	"    vs_fs_result = current_value;\n"
75 	"}\n";
76 
77 /* Vertex Shader for float pass */
78 const glw::GLchar* const GPUShader5FmaAccuracyTest::m_vertex_shader_code_for_float_pass =
79 	"${VERSION}\n"
80 	"\n"
81 	"${GPU_SHADER5_REQUIRE}\n"
82 	"\n"
83 	"precision highp float;\n"
84 	"\n"
85 	"uniform uint  uni_number_of_steps;\n"
86 	"out     float vs_fs_result;\n"
87 	"\n"
88 	"void main()\n"
89 	"{\n"
90 	"    float border_case   = 1.0;\n"
91 	"    float h             = 1.0 / float(uni_number_of_steps);\n"
92 	"    float current_value = border_case;\n"
93 	"\n"
94 	"    for (uint step = 0u; step < uni_number_of_steps; ++step)\n"
95 	"    {\n"
96 	"        float next_value = h * current_value + current_value;\n"
97 	"\n"
98 	"        current_value = next_value;\n"
99 	"    }\n"
100 	"\n"
101 	"    vs_fs_result = current_value;\n"
102 	"}\n";
103 
104 /* Constants */
105 const glw::GLuint  GPUShader5FmaAccuracyTest::m_buffer_size			   = sizeof(glw::GLfloat);
106 const unsigned int GPUShader5FmaAccuracyTest::m_n_draw_call_executions = 10;
107 const float		   GPUShader5FmaAccuracyTest::m_expected_result		   = 2.71828f;
108 
109 /** Constructor
110  *
111  *  @param context       Test context
112  *  @param name          Test case's name
113  *  @param description   Test case's description
114  **/
GPUShader5FmaAccuracyTest(Context & context,const ExtParameters & extParams,const char * name,const char * description)115 GPUShader5FmaAccuracyTest::GPUShader5FmaAccuracyTest(Context& context, const ExtParameters& extParams, const char* name,
116 													 const char* description)
117 	: TestCaseBase(context, extParams, name, description)
118 	, m_fragment_shader_id(0)
119 	, m_program_object_id_for_float_pass(0)
120 	, m_program_object_id_for_fma_pass(0)
121 	, m_vertex_shader_id_for_float_pass(0)
122 	, m_vertex_shader_id_for_fma_pass(0)
123 	, m_buffer_object_id(0)
124 	, m_vertex_array_object_id(0)
125 {
126 	/* Nothing to be done here */
127 }
128 
129 /** Initializes GLES objects used during the test.
130  *
131  **/
initTest()132 void GPUShader5FmaAccuracyTest::initTest()
133 {
134 	/* This test should only run if EXT_gpu_shader5 is supported */
135 	if (!m_is_gpu_shader5_supported)
136 	{
137 		throw tcu::NotSupportedError(GPU_SHADER5_EXTENSION_NOT_SUPPORTED, "", __FILE__, __LINE__);
138 	}
139 
140 	/* Retrieve ES entry points. */
141 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
142 
143 	/* Create programs and shaders */
144 	m_program_object_id_for_fma_pass   = gl.createProgram();
145 	m_program_object_id_for_float_pass = gl.createProgram();
146 
147 	m_fragment_shader_id			  = gl.createShader(GL_FRAGMENT_SHADER);
148 	m_vertex_shader_id_for_fma_pass   = gl.createShader(GL_VERTEX_SHADER);
149 	m_vertex_shader_id_for_float_pass = gl.createShader(GL_VERTEX_SHADER);
150 
151 	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create program or shader object(s)");
152 
153 	/* Declare a name of a varying we will be capturing via TF */
154 	const glw::GLchar* const captured_varying_name = "vs_fs_result";
155 
156 	/* Set up transform feedback for fma pass*/
157 	gl.transformFeedbackVaryings(m_program_object_id_for_fma_pass, 1 /* count */, &captured_varying_name,
158 								 GL_INTERLEAVED_ATTRIBS);
159 
160 	/* Build program for fma pass */
161 	if (false == buildProgram(m_program_object_id_for_fma_pass, m_fragment_shader_id, 1 /* number of FS parts */,
162 							  &m_fragment_shader_code, m_vertex_shader_id_for_fma_pass, 1 /* number of VS parts */,
163 							  &m_vertex_shader_code_for_fma_pass))
164 	{
165 		TCU_FAIL("Could not create program from a valid vertex/fragment shader");
166 	}
167 
168 	/* Set up transform feedback for float pass*/
169 	gl.transformFeedbackVaryings(m_program_object_id_for_float_pass, 1 /* count */, &captured_varying_name,
170 								 GL_INTERLEAVED_ATTRIBS);
171 
172 	/* Build program for float pass */
173 	if (false == buildProgram(m_program_object_id_for_float_pass, m_fragment_shader_id, 1 /* number of FS parts */,
174 							  &m_fragment_shader_code, m_vertex_shader_id_for_float_pass, 1 /* number of VS parts */,
175 							  &m_vertex_shader_code_for_float_pass))
176 	{
177 		TCU_FAIL("Could not create program from valid vertex/fragment shader");
178 	}
179 
180 	/* Generate and bind VAO */
181 	gl.genVertexArrays(1, &m_vertex_array_object_id);
182 	gl.bindVertexArray(m_vertex_array_object_id);
183 
184 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create and bind vertex array object");
185 
186 	/* Generate, bind and allocate buffer */
187 	gl.genBuffers(1, &m_buffer_object_id);
188 	gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer_object_id);
189 	gl.bufferData(GL_ARRAY_BUFFER, m_buffer_size, DE_NULL /* undefined start data */, GL_STATIC_DRAW);
190 
191 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create vertex array object");
192 }
193 
194 /** Executes the test.
195  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
196  *
197  *  Note the function throws exception should an error occur!
198  *
199  *  @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again.
200  *
201  **/
iterate()202 tcu::TestCase::IterateResult GPUShader5FmaAccuracyTest::iterate()
203 {
204 	initTest();
205 
206 	/* Storage space for result values */
207 	std::vector<glw::GLfloat> results_of_float_pass;
208 	std::vector<glw::GLfloat> results_of_fma_pass;
209 
210 	results_of_fma_pass.resize(m_n_draw_call_executions);
211 	results_of_float_pass.resize(m_n_draw_call_executions);
212 
213 	/* Execute fma pass */
214 	executePass(m_program_object_id_for_fma_pass, &results_of_fma_pass[0]);
215 
216 	/* Execute float pass */
217 	executePass(m_program_object_id_for_float_pass, &results_of_float_pass[0]);
218 
219 	/* Storage space for relative errors */
220 	std::vector<glw::GLfloat> relative_errors_for_float_pass;
221 	std::vector<glw::GLfloat> relative_errors_for_fma_pass;
222 
223 	relative_errors_for_fma_pass.resize(m_n_draw_call_executions);
224 	relative_errors_for_float_pass.resize(m_n_draw_call_executions);
225 
226 	/* Calculate relative errors */
227 	for (glw::GLuint i = 0; i < m_n_draw_call_executions; ++i)
228 	{
229 		calculateRelativeError(results_of_fma_pass[i], m_expected_result, relative_errors_for_fma_pass[i]);
230 		calculateRelativeError(results_of_float_pass[i], m_expected_result, relative_errors_for_float_pass[i]);
231 	}
232 
233 	/* Sum relative errors */
234 	glw::GLfloat relative_error_sum_for_fma_pass   = 0.0f;
235 	glw::GLfloat relative_error_sum_for_float_pass = 0.0f;
236 
237 	for (glw::GLuint i = 0; i < m_n_draw_call_executions; ++i)
238 	{
239 		relative_error_sum_for_fma_pass += relative_errors_for_fma_pass[i];
240 		relative_error_sum_for_float_pass += relative_errors_for_float_pass[i];
241 	}
242 
243 	if (relative_error_sum_for_fma_pass <= relative_error_sum_for_float_pass)
244 	{
245 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
246 	}
247 	else
248 	{
249 		m_testCtx.getLog() << tcu::TestLog::Message << "Routine fma has lower accuracy than a * b + c !"
250 						   << tcu::TestLog::EndMessage;
251 
252 		/* Log sum of relative errors */
253 		m_testCtx.getLog() << tcu::TestLog::Section("Sum of relative error", "");
254 		m_testCtx.getLog() << tcu::TestLog::Message << "fma       " << relative_error_sum_for_fma_pass
255 						   << tcu::TestLog::EndMessage;
256 		m_testCtx.getLog() << tcu::TestLog::Message << "a * b + c " << relative_error_sum_for_float_pass
257 						   << tcu::TestLog::EndMessage;
258 		m_testCtx.getLog() << tcu::TestLog::EndSection;
259 
260 		/* Log relative errors */
261 		m_testCtx.getLog() << tcu::TestLog::Section("Relative errors", "");
262 		logArray("fma", &relative_errors_for_fma_pass[0], m_n_draw_call_executions);
263 		logArray("a * b + c", &relative_errors_for_float_pass[0], m_n_draw_call_executions);
264 		m_testCtx.getLog() << tcu::TestLog::EndSection;
265 
266 		/* Log results */
267 		m_testCtx.getLog() << tcu::TestLog::Section("Results", "");
268 		logArray("fma", &results_of_fma_pass[0], m_n_draw_call_executions);
269 		logArray("a * b + c", &results_of_float_pass[0], m_n_draw_call_executions);
270 		m_testCtx.getLog() << tcu::TestLog::EndSection;
271 
272 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
273 	}
274 
275 	return STOP;
276 }
277 
278 /** Deinitializes GLES objects created during the test.
279  *
280  */
deinit()281 void GPUShader5FmaAccuracyTest::deinit()
282 {
283 	/* GL */
284 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
285 
286 	/* Bind default values */
287 	gl.useProgram(0);
288 	gl.bindVertexArray(0);
289 	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, 0 /* id */);
290 	gl.bindBuffer(GL_ARRAY_BUFFER, 0);
291 
292 	/* Delete everything */
293 	if (0 != m_vertex_array_object_id)
294 	{
295 		gl.deleteVertexArrays(1, &m_vertex_array_object_id);
296 
297 		m_vertex_array_object_id = 0;
298 	}
299 
300 	if (0 != m_buffer_object_id)
301 	{
302 		gl.deleteBuffers(1, &m_buffer_object_id);
303 
304 		m_buffer_object_id = 0;
305 	}
306 
307 	if (0 != m_program_object_id_for_fma_pass)
308 	{
309 		gl.deleteProgram(m_program_object_id_for_fma_pass);
310 
311 		m_program_object_id_for_fma_pass = 0;
312 	}
313 
314 	if (0 != m_program_object_id_for_float_pass)
315 	{
316 		gl.deleteProgram(m_program_object_id_for_float_pass);
317 
318 		m_program_object_id_for_float_pass = 0;
319 	}
320 
321 	if (0 != m_fragment_shader_id)
322 	{
323 		gl.deleteShader(m_fragment_shader_id);
324 
325 		m_fragment_shader_id = 0;
326 	}
327 
328 	if (0 != m_vertex_shader_id_for_fma_pass)
329 	{
330 		gl.deleteShader(m_vertex_shader_id_for_fma_pass);
331 
332 		m_vertex_shader_id_for_fma_pass = 0;
333 	}
334 
335 	if (0 != m_vertex_shader_id_for_float_pass)
336 	{
337 		gl.deleteShader(m_vertex_shader_id_for_float_pass);
338 
339 		m_vertex_shader_id_for_float_pass = 0;
340 	}
341 
342 	/* Call base class' deinit() */
343 	TestCaseBase::deinit();
344 }
345 
346 /** Calculate relative error for given result and expected value.
347  *
348  *  rel_err = delta / expected_result
349  *  delta   = | expected_result - result |
350  *
351  *  @param result          Result value
352  *  @param expected_result Expected value
353  *  @param relative_error  Set to value of relative error
354  **/
calculateRelativeError(glw::GLfloat result,glw::GLfloat expected_result,glw::GLfloat & relative_error)355 void GPUShader5FmaAccuracyTest::calculateRelativeError(glw::GLfloat result, glw::GLfloat expected_result,
356 													   glw::GLfloat& relative_error)
357 {
358 	const glw::GLfloat delta = de::abs(expected_result - result);
359 
360 	relative_error = delta / expected_result;
361 }
362 
363 /** Executes single pass of test.
364  *
365  *  @param program_object_id Gpu program that will be used during draw calls
366  *  @param results           Storage used for result values. It is expected to have capacity for at least m_n_draw_call_executions elements
367  **/
executePass(glw::GLuint program_object_id,glw::GLfloat * results)368 void GPUShader5FmaAccuracyTest::executePass(glw::GLuint program_object_id, glw::GLfloat* results)
369 {
370 	/* GL */
371 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
372 
373 	/* Uniform location */
374 	glw::GLint uniform_location = gl.getUniformLocation(program_object_id, "uni_number_of_steps");
375 
376 	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get uniform location");
377 
378 	if (-1 == uniform_location)
379 	{
380 		TCU_FAIL("Unexpected inactive uniform location");
381 	}
382 
383 	/* Setup transform feedback */
384 	gl.enable(GL_RASTERIZER_DISCARD);
385 	GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) call failed");
386 
387 	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */
388 					  m_buffer_object_id);
389 	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to bind buffer object id to transform feedback binding point");
390 
391 	/* Setup draw call */
392 	gl.useProgram(program_object_id);
393 	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed");
394 
395 	for (unsigned int i = 0; i < m_n_draw_call_executions; ++i)
396 	{
397 		/* Get number of steps for the Euler iterative algorithm */
398 		glw::GLuint number_of_steps = getNumberOfStepsForIndex(i);
399 
400 		/* Set uniform data */
401 		gl.uniform1ui(uniform_location, number_of_steps);
402 		GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set uniform data");
403 
404 		/* Start transform feedback */
405 		gl.beginTransformFeedback(GL_POINTS);
406 		{
407 			/* Draw */
408 			gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* one point */);
409 		}
410 		/* Stop transform feedback */
411 		gl.endTransformFeedback();
412 
413 		GLU_EXPECT_NO_ERROR(gl.getError(), "Error doing a draw call");
414 
415 		/* Map transfrom feedback results */
416 		const glw::GLfloat* transform_feedback_data = (const glw::GLfloat*)gl.mapBufferRange(
417 			GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, m_buffer_size, GL_MAP_READ_BIT);
418 
419 		GLU_EXPECT_NO_ERROR(gl.getError(), "Could not map the buffer object into process space");
420 
421 		/* Copy data to results array */
422 		results[i] = *transform_feedback_data;
423 
424 		/* Unmap transform feedback buffer */
425 		gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
426 
427 		GLU_EXPECT_NO_ERROR(gl.getError(), "Error unmapping the buffer object");
428 	}
429 }
430 
431 /** Get number of steps that will be used by vertex shader to compute its result for given index of iteration
432  *
433  *  @param index Index of iteration
434  *
435  *  @return Number of steps
436  **/
getNumberOfStepsForIndex(glw::GLuint index)437 glw::GLuint GPUShader5FmaAccuracyTest::getNumberOfStepsForIndex(glw::GLuint index)
438 {
439 	glw::GLuint number_of_steps = 10;
440 
441 	for (glw::GLuint i = 0; i < index; ++i)
442 	{
443 		number_of_steps *= 2;
444 	}
445 
446 	return number_of_steps;
447 }
448 
449 /** Logs contents of array
450  *
451  *  @param description Description of data
452  *  @param data        Data to log
453  *  @param length      Number of values to log
454  **/
logArray(const char * description,glw::GLfloat * data,glw::GLuint length)455 void GPUShader5FmaAccuracyTest::logArray(const char* description, glw::GLfloat* data, glw::GLuint length)
456 {
457 	m_testCtx.getLog() << tcu::TestLog::Message << description << tcu::TestLog::EndMessage;
458 
459 	tcu::MessageBuilder message = m_testCtx.getLog() << tcu::TestLog::Message;
460 
461 	message << "| ";
462 
463 	for (glw::GLuint i = 0; i < length; ++i)
464 	{
465 		message << data[i] << " | ";
466 	}
467 
468 	message << tcu::TestLog::EndMessage;
469 }
470 } /* glcts */
471