• 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 /*!
25  * \file esextcGPUShader5FmaPrecision.cpp
26  * \brief gpu_shader5 extension - fma precision Test (Test 8)
27  */ /*-------------------------------------------------------------------*/
28 
29 #include "esextcGPUShader5FmaPrecision.hpp"
30 
31 #include "deDefs.hpp"
32 #include "deMath.h"
33 #include "gluDefs.hpp"
34 #include "glwEnums.hpp"
35 #include "glwFunctions.hpp"
36 #include "tcuTestLog.hpp"
37 #include <cstring>
38 #include <sstream>
39 
40 namespace glcts
41 {
42 /** Constructor
43  *  @param S             Type of input data
44  *  @param context       Test context
45  *  @param name          Test case's name
46  *  @param description   Test case's description
47  */
48 template <INPUT_DATA_TYPE S>
GPUShader5FmaPrecision(Context & context,const ExtParameters & extParams,const char * name,const char * description)49 GPUShader5FmaPrecision<S>::GPUShader5FmaPrecision(Context& context, const ExtParameters& extParams, const char* name,
50 												  const char* description)
51 	: TestCaseBase(context, extParams, name, description)
52 	, m_amplitude(100.0f)
53 	, m_fs_id(0)
54 	, m_po_id(0)
55 	, m_vao_id(0)
56 	, m_vbo_a_id(0)
57 	, m_vbo_b_id(0)
58 	, m_vbo_c_id(0)
59 	, m_vbo_result_fma_id(0)
60 	, m_vbo_result_std_id(0)
61 	, m_vs_id(0)
62 {
63 	/* Nothing to be done here */
64 }
65 
66 /** Deinitializes GLES objects created during the test.
67  *
68  */
69 template <INPUT_DATA_TYPE S>
deinit(void)70 void					  GPUShader5FmaPrecision<S>::deinit(void)
71 {
72 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
73 
74 	/* Reset OpenGL ES state */
75 	gl.useProgram(0);
76 	gl.bindBuffer(GL_ARRAY_BUFFER, 0);
77 	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
78 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
79 	gl.bindVertexArray(0);
80 
81 	if (m_po_id != 0)
82 	{
83 		gl.deleteProgram(m_po_id);
84 
85 		m_po_id = 0;
86 	}
87 
88 	if (m_fs_id != 0)
89 	{
90 		gl.deleteShader(m_fs_id);
91 
92 		m_fs_id = 0;
93 	}
94 
95 	if (m_vs_id != 0)
96 	{
97 		gl.deleteShader(m_vs_id);
98 
99 		m_vs_id = 0;
100 	}
101 
102 	if (m_vbo_a_id != 0)
103 	{
104 		gl.deleteBuffers(1, &m_vbo_a_id);
105 
106 		m_vbo_a_id = 0;
107 	}
108 
109 	if (m_vbo_b_id != 0)
110 	{
111 		gl.deleteBuffers(1, &m_vbo_b_id);
112 
113 		m_vbo_b_id = 0;
114 	}
115 
116 	if (m_vbo_c_id != 0)
117 	{
118 		gl.deleteBuffers(1, &m_vbo_c_id);
119 
120 		m_vbo_c_id = 0;
121 	}
122 
123 	if (m_vbo_result_fma_id != 0)
124 	{
125 		gl.deleteBuffers(1, &m_vbo_result_fma_id);
126 
127 		m_vbo_result_fma_id = 0;
128 	}
129 
130 	if (m_vbo_result_std_id != 0)
131 	{
132 		gl.deleteBuffers(1, &m_vbo_result_std_id);
133 
134 		m_vbo_result_std_id = 0;
135 	}
136 
137 	if (m_vao_id != 0)
138 	{
139 		gl.deleteVertexArrays(1, &m_vao_id);
140 
141 		m_vao_id = 0;
142 	}
143 
144 	/* Call base class' deinit() */
145 	TestCaseBase::deinit();
146 }
147 
148 /** Initializes GLES objects used during the test.
149  *
150  */
151 template <INPUT_DATA_TYPE S>
initTest(void)152 void					  GPUShader5FmaPrecision<S>::initTest(void)
153 {
154 	/* Check if gpu_shader5 extension is supported */
155 	if (!m_is_gpu_shader5_supported)
156 	{
157 		throw tcu::NotSupportedError(GPU_SHADER5_EXTENSION_NOT_SUPPORTED, "", __FILE__, __LINE__);
158 	}
159 
160 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
161 
162 	/* generate test data */
163 	generateData();
164 
165 	/* Set up shader and program objects */
166 	m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
167 	m_vs_id = gl.createShader(GL_VERTEX_SHADER);
168 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create shader objects!");
169 
170 	m_po_id = gl.createProgram();
171 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create program object!");
172 
173 	/* Set up transform feedback */
174 	gl.enable(GL_RASTERIZER_DISCARD);
175 	GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) call failed");
176 
177 	const char* varyings[] = { "resultFma", "resultStd" };
178 
179 	gl.transformFeedbackVaryings(m_po_id, 2, varyings, GL_SEPARATE_ATTRIBS);
180 	GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed");
181 
182 	/* Get shader code */
183 	const char* fsCode	= getFragmentShaderCode();
184 	std::string vsCode	= generateVertexShaderCode();
185 	const char* vsCodeStr = vsCode.c_str();
186 
187 	if (!buildProgram(m_po_id, m_fs_id, 1 /* part */, &fsCode, m_vs_id, 1 /* part */, &vsCodeStr))
188 	{
189 		TCU_FAIL("Could not create a program from valid vertex/fragment shader!");
190 	}
191 
192 	/* Create and bind vertex array object */
193 	gl.genVertexArrays(1, &m_vao_id);
194 	gl.bindVertexArray(m_vao_id);
195 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring vertex array object!");
196 
197 	/* Configure buffer objects with input data*/
198 	gl.genBuffers(1, &m_vbo_a_id);
199 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_a_id);
200 	gl.bufferData(GL_ARRAY_BUFFER, sizeof(m_data_a), m_data_a, GL_STATIC_READ);
201 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring buffer object!");
202 
203 	gl.genBuffers(1, &m_vbo_b_id);
204 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_b_id);
205 	gl.bufferData(GL_ARRAY_BUFFER, sizeof(m_data_b), m_data_b, GL_STATIC_READ);
206 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring buffer object!");
207 
208 	gl.genBuffers(1, &m_vbo_c_id);
209 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_c_id);
210 	gl.bufferData(GL_ARRAY_BUFFER, sizeof(m_data_c), m_data_c, GL_STATIC_READ);
211 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring buffer object!");
212 
213 	gl.useProgram(m_po_id);
214 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not use program!");
215 
216 	/* Configure vertex attrib pointers */
217 	glw::GLint posAttribA = gl.getAttribLocation(m_po_id, "a");
218 
219 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation() failed");
220 
221 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_a_id);
222 	gl.vertexAttribPointer(posAttribA, S, GL_FLOAT, GL_FALSE, 0 /* stride */, DE_NULL);
223 	gl.enableVertexAttribArray(posAttribA);
224 
225 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring input vertex data attrib pointer!");
226 
227 	glw::GLint posAttribB = gl.getAttribLocation(m_po_id, "b");
228 
229 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_b_id);
230 	gl.vertexAttribPointer(posAttribB, S, GL_FLOAT, GL_FALSE, 0 /* stride */, DE_NULL);
231 	gl.enableVertexAttribArray(posAttribB);
232 
233 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring input vertex data attrib pointer!");
234 
235 	glw::GLint posAttribC = gl.getAttribLocation(m_po_id, "c");
236 
237 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_c_id);
238 	gl.vertexAttribPointer(posAttribC, S, GL_FLOAT, GL_FALSE, 0 /* stride */, DE_NULL);
239 	gl.enableVertexAttribArray(posAttribC);
240 
241 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring input vertex data attrib pointer!");
242 
243 	/* Configure buffer objects for captured results */
244 	gl.genBuffers(1, &m_vbo_result_fma_id);
245 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_result_fma_id);
246 	gl.bufferData(GL_ARRAY_BUFFER, m_n_elements * S * sizeof(glw::GLfloat), DE_NULL, GL_DYNAMIC_COPY);
247 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring buffer object!");
248 
249 	gl.genBuffers(1, &m_vbo_result_std_id);
250 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_result_std_id);
251 	gl.bufferData(GL_ARRAY_BUFFER, m_n_elements * S * sizeof(glw::GLfloat), DE_NULL, GL_DYNAMIC_COPY);
252 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error configuring buffer object!");
253 
254 	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_vbo_result_fma_id);
255 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding buffer object to transform feedback binding point!");
256 
257 	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1 /* index */, m_vbo_result_std_id);
258 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding buffer object to transform feedback binding point!");
259 }
260 
261 /** Executes the test.
262  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
263  *
264  *  @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again.
265  *
266  *  Note the function throws exception should an error occur!
267  */
268 template <INPUT_DATA_TYPE	S>
iterate(void)269 tcu::TestNode::IterateResult GPUShader5FmaPrecision<S>::iterate(void)
270 {
271 	DE_FENV_ACCESS_ON;
272 
273 	initTest();
274 
275 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
276 
277 	/* Render */
278 	gl.beginTransformFeedback(GL_POINTS);
279 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed");
280 
281 	gl.drawArrays(GL_POINTS, 0, m_n_elements);
282 	GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed!");
283 
284 	gl.endTransformFeedback();
285 	GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed");
286 
287 	/* Retrieve the result data */
288 	glw::GLfloat		resultFma[m_n_elements * S];
289 	glw::GLfloat		resultStd[m_n_elements * S];
290 	const glw::GLfloat* resultTmp = DE_NULL;
291 
292 	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_vbo_result_fma_id);
293 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed");
294 
295 	resultTmp = (const glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
296 													   m_n_elements * S * sizeof(glw::GLfloat), GL_MAP_READ_BIT);
297 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error mapping buffer object's data to client space!");
298 
299 	memcpy(resultFma, resultTmp, m_n_elements * S * sizeof(glw::GLfloat));
300 
301 	gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
302 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error unmapping buffer object's data!");
303 
304 	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_vbo_result_std_id);
305 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed");
306 
307 	resultTmp = (const glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
308 													   m_n_elements * S * sizeof(glw::GLfloat), GL_MAP_READ_BIT);
309 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error mapping buffer object's data to client space!");
310 
311 	memcpy(resultStd, resultTmp, m_n_elements * S * sizeof(glw::GLfloat));
312 
313 	gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
314 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error unmapping buffer object's data!");
315 
316 	/* Execute the algorithm from shader on CPU */
317 	glw::GLfloat resultCPURNE[m_n_elements * S];
318 	glw::GLfloat resultCPURTZ[m_n_elements * S];
319 
320 	deRoundingMode previousRoundingMode = deGetRoundingMode();
321 
322 	deSetRoundingMode(DE_ROUNDINGMODE_TO_NEAREST_EVEN);
323 	for (glw::GLuint i = 0; i < m_n_elements; ++i)
324 	{
325 		for (glw::GLuint j = 0; j < S; ++j)
326 		{
327 			resultCPURNE[i * S + j] = m_data_a[i * S + j] * m_data_b[i * S + j] + m_data_c[i * S + j];
328 		}
329 	}
330 
331 	deSetRoundingMode(DE_ROUNDINGMODE_TO_ZERO);
332 	for (glw::GLuint i = 0; i < m_n_elements; ++i)
333 	{
334 		for (glw::GLuint j = 0; j < S; ++j)
335 		{
336 			resultCPURTZ[i * S + j] = m_data_a[i * S + j] * m_data_b[i * S + j] + m_data_c[i * S + j];
337 		}
338 	}
339 
340 	/* Restore the rounding mode so subsequent tests aren't affected */
341 	deSetRoundingMode(previousRoundingMode);
342 
343 	/* Check results */
344 	const glw::GLfloat* resultsCPU[] = { resultCPURNE, resultCPURTZ };
345 	FloatConverter		cpuU;
346 	FloatConverter		fmaU;
347 	FloatConverter		stdU;
348 	glw::GLboolean		test_failed = true;
349 
350 	for (glw::GLuint roundingMode = 0; test_failed && roundingMode < 2; ++roundingMode)
351 	{
352 		glw::GLboolean rounding_mode_failed = false;
353 		for (glw::GLuint i = 0; i < m_n_elements; ++i)
354 		{
355 			for (int j = 0; j < S; ++j)
356 			{
357 				/* Assign float value to the union */
358 				cpuU.m_float = resultsCPU[roundingMode][i * S + j];
359 				fmaU.m_float = resultFma[i * S + j];
360 				stdU.m_float = resultStd[i * S + j];
361 
362 				/* Convert float to int bitwise */
363 				glw::GLint cpuTemp = cpuU.m_int;
364 				glw::GLint fmaTemp = fmaU.m_int;
365 				glw::GLint stdTemp = stdU.m_int;
366 
367 				glw::GLboolean diffCpuFma = de::abs(cpuTemp - fmaTemp) > 2;
368 				glw::GLboolean diffCpuStd = de::abs(cpuTemp - stdTemp) > 2;
369 				glw::GLboolean diffFmaStd = de::abs(fmaTemp - stdTemp) > 2;
370 
371 				if (diffCpuFma || diffCpuStd || diffFmaStd)
372 				{
373 					rounding_mode_failed = true;
374 					break;
375 				}
376 			}
377 
378 			if (rounding_mode_failed)
379 			{
380 				break;
381 			}
382 			else
383 			{
384 				test_failed = false;
385 			}
386 		} /* for (all elements) */
387 	}	 /* for (all rounding modes) */
388 
389 	if (test_failed)
390 	{
391 		m_testCtx.getLog()
392 			<< tcu::TestLog::Message
393 			<< "The values of resultStd[i] & 0xFFFFFFFE and resultFma[i] & 0xFFFFFFFE and resultCPU[i] & 0xFFFFFFFE "
394 			<< "are not bitwise equal for i = 0..99\n"
395 			<< tcu::TestLog::EndMessage;
396 
397 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
398 	}
399 	else
400 	{
401 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
402 	}
403 
404 	return STOP;
405 }
406 
407 /** Generate random input data */
408 template <INPUT_DATA_TYPE S>
generateData()409 void					  GPUShader5FmaPrecision<S>::generateData()
410 {
411 	/* Intialize with 1, because we want the same sequence of random values everytime we run test */
412 	randomSeed(1);
413 
414 	/* Data generation */
415 	for (unsigned int i = 0; i < m_n_elements; i++)
416 	{
417 		for (int j = 0; j < S; j++)
418 		{
419 			float a, b, c;
420 
421 			a = static_cast<float>(randomFormula(RAND_MAX)) /
422 					(static_cast<float>(static_cast<float>(RAND_MAX) / static_cast<float>(m_amplitude * 2.0f))) -
423 				m_amplitude;
424 			b = static_cast<float>(randomFormula(RAND_MAX)) /
425 					(static_cast<float>(static_cast<float>(RAND_MAX) / static_cast<float>(m_amplitude * 2.0f))) -
426 				m_amplitude;
427 			c = static_cast<float>(randomFormula(RAND_MAX)) /
428 					(static_cast<float>(static_cast<float>(RAND_MAX) / static_cast<float>(m_amplitude * 2.0f))) -
429 				m_amplitude;
430 
431 			// If values are of opposite sign, catastrophic cancellation is possible. 1 LSB of error
432 			// tolerance is relative to the larger intermediate terms, and once you compute a*b+c
433 			// you might get values with smaller exponents. Scale down one of the terms so that either
434 			// |a*b| < 0.5*|c| or |c| < 0.5 * |a*b| so that the result is no smaller than half of the larger of a*b or c.
435 
436 			float axb = a * b;
437 			if (deFloatSign(axb) != deFloatSign(c))
438 			{
439 				if (de::inRange(de::abs(axb), de::abs(c), 2 * de::abs(c)))
440 				{
441 					c /= 2.0f;
442 				}
443 				else if (de::inRange(de::abs(c), de::abs(axb), 2 * de::abs(axb)))
444 				{
445 					a /= 2.0f;
446 				}
447 			}
448 
449 			m_data_a[i * S + j] = a;
450 			m_data_b[i * S + j] = b;
451 			m_data_c[i * S + j] = c;
452 		}
453 	}
454 }
455 
456 /** Returns code for Vertex Shader
457  *
458  *  @return pointer to literal with Vertex Shader code
459  */
460 template <INPUT_DATA_TYPE S>
generateVertexShaderCode()461 std::string				  GPUShader5FmaPrecision<S>::generateVertexShaderCode()
462 {
463 	std::string type;
464 
465 	switch (S)
466 	{
467 	case IDT_FLOAT:
468 	{
469 		type = "float";
470 
471 		break;
472 	}
473 
474 	case IDT_VEC2:
475 	{
476 		type = "vec2";
477 
478 		break;
479 	}
480 
481 	case IDT_VEC3:
482 	{
483 		type = "vec3";
484 
485 		break;
486 	}
487 
488 	case IDT_VEC4:
489 	{
490 		type = "vec4";
491 
492 		break;
493 	}
494 
495 	default:
496 	{
497 		TCU_FAIL("Incorrect variable type!");
498 		break;
499 	}
500 	} /* switch(S) */
501 
502 	/* Generate the vertex shader code */
503 	std::stringstream vsCode;
504 
505 	vsCode << "${VERSION}\n"
506 			  "\n"
507 			  "${GPU_SHADER5_REQUIRE}\n"
508 			  "\n"
509 			  "precision highp float;\n"
510 			  "\n"
511 			  "layout(location = 0) in "
512 		   << type << " a;\n"
513 		   << "layout(location = 1) in " << type << " b;\n"
514 		   << "layout(location = 2) in " << type << " c;\n"
515 		   << "\n"
516 		   << "layout(location = 0) out " << type << " resultFma;\n"
517 		   << "layout(location = 1) precise out " << type << " resultStd;\n"
518 		   << "\n"
519 		   << "\n"
520 		   << "void main()\n"
521 		   << "{\n"
522 		   << "    resultFma = fma(a,b,c);\n"
523 		   << "    resultStd = a * b + c;\n"
524 		   << "}\n";
525 
526 	return vsCode.str();
527 }
528 
529 /** Returns code for Fragment Shader
530  *
531  *  @return pointer to literal with Fragment Shader code
532  */
533 template <INPUT_DATA_TYPE S>
getFragmentShaderCode()534 const char*				  GPUShader5FmaPrecision<S>::getFragmentShaderCode()
535 {
536 	static const char* result = "${VERSION}\n"
537 								"\n"
538 								"${GPU_SHADER5_REQUIRE}\n"
539 								"\n"
540 								"precision highp float;\n"
541 								"\n"
542 								"void main(void)\n"
543 								"{\n"
544 								"}\n";
545 
546 	return result;
547 }
548 
549 } // namespace glcts
550