• 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 	initTest();
272 
273 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
274 
275 	/* Render */
276 	gl.beginTransformFeedback(GL_POINTS);
277 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed");
278 
279 	gl.drawArrays(GL_POINTS, 0, m_n_elements);
280 	GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed!");
281 
282 	gl.endTransformFeedback();
283 	GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed");
284 
285 	/* Retrieve the result data */
286 	glw::GLfloat		resultFma[m_n_elements * S];
287 	glw::GLfloat		resultStd[m_n_elements * S];
288 	const glw::GLfloat* resultTmp = DE_NULL;
289 
290 	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_vbo_result_fma_id);
291 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed");
292 
293 	resultTmp = (const glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
294 													   m_n_elements * S * sizeof(glw::GLfloat), GL_MAP_READ_BIT);
295 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error mapping buffer object's data to client space!");
296 
297 	memcpy(resultFma, resultTmp, m_n_elements * S * sizeof(glw::GLfloat));
298 
299 	gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
300 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error unmapping buffer object's data!");
301 
302 	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_vbo_result_std_id);
303 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed");
304 
305 	resultTmp = (const glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
306 													   m_n_elements * S * sizeof(glw::GLfloat), GL_MAP_READ_BIT);
307 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error mapping buffer object's data to client space!");
308 
309 	memcpy(resultStd, resultTmp, m_n_elements * S * sizeof(glw::GLfloat));
310 
311 	gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
312 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error unmapping buffer object's data!");
313 
314 	/* Execute the algorithm from shader on CPU */
315 	glw::GLfloat resultCPURNE[m_n_elements * S];
316 	glw::GLfloat resultCPURTZ[m_n_elements * S];
317 
318 	deRoundingMode previousRoundingMode = deGetRoundingMode();
319 
320 	deSetRoundingMode(DE_ROUNDINGMODE_TO_NEAREST_EVEN);
321 	for (glw::GLuint i = 0; i < m_n_elements; ++i)
322 	{
323 		for (glw::GLuint j = 0; j < S; ++j)
324 		{
325 			resultCPURNE[i * S + j] = m_data_a[i * S + j] * m_data_b[i * S + j] + m_data_c[i * S + j];
326 		}
327 	}
328 
329 	deSetRoundingMode(DE_ROUNDINGMODE_TO_ZERO);
330 	for (glw::GLuint i = 0; i < m_n_elements; ++i)
331 	{
332 		for (glw::GLuint j = 0; j < S; ++j)
333 		{
334 			resultCPURTZ[i * S + j] = m_data_a[i * S + j] * m_data_b[i * S + j] + m_data_c[i * S + j];
335 		}
336 	}
337 
338 	/* Restore the rounding mode so subsequent tests aren't affected */
339 	deSetRoundingMode(previousRoundingMode);
340 
341 	/* Check results */
342 	const glw::GLfloat* resultsCPU[] = { resultCPURNE, resultCPURTZ };
343 	FloatConverter		cpuU;
344 	FloatConverter		fmaU;
345 	FloatConverter		stdU;
346 	glw::GLboolean		test_failed = true;
347 
348 	for (glw::GLuint roundingMode = 0; test_failed && roundingMode < 2; ++roundingMode)
349 	{
350 		glw::GLboolean rounding_mode_failed = false;
351 		for (glw::GLuint i = 0; i < m_n_elements; ++i)
352 		{
353 			for (int j = 0; j < S; ++j)
354 			{
355 				/* Assign float value to the union */
356 				cpuU.m_float = resultsCPU[roundingMode][i * S + j];
357 				fmaU.m_float = resultFma[i * S + j];
358 				stdU.m_float = resultStd[i * S + j];
359 
360 				/* Convert float to int bitwise */
361 				glw::GLint cpuTemp = cpuU.m_int;
362 				glw::GLint fmaTemp = fmaU.m_int;
363 				glw::GLint stdTemp = stdU.m_int;
364 
365 				glw::GLboolean diffCpuFma = de::abs(cpuTemp - fmaTemp) > 2;
366 				glw::GLboolean diffCpuStd = de::abs(cpuTemp - stdTemp) > 2;
367 				glw::GLboolean diffFmaStd = de::abs(fmaTemp - stdTemp) > 2;
368 
369 				if (diffCpuFma || diffCpuStd || diffFmaStd)
370 				{
371 					rounding_mode_failed = true;
372 					break;
373 				}
374 			}
375 
376 			if (rounding_mode_failed)
377 			{
378 				break;
379 			}
380 			else
381 			{
382 				test_failed = false;
383 			}
384 		} /* for (all elements) */
385 	}	 /* for (all rounding modes) */
386 
387 	if (test_failed)
388 	{
389 		m_testCtx.getLog()
390 			<< tcu::TestLog::Message
391 			<< "The values of resultStd[i] & 0xFFFFFFFE and resultFma[i] & 0xFFFFFFFE and resultCPU[i] & 0xFFFFFFFE "
392 			<< "are not bitwise equal for i = 0..99\n"
393 			<< tcu::TestLog::EndMessage;
394 
395 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
396 	}
397 	else
398 	{
399 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
400 	}
401 
402 	return STOP;
403 }
404 
405 /** Generate random input data */
406 template <INPUT_DATA_TYPE S>
generateData()407 void					  GPUShader5FmaPrecision<S>::generateData()
408 {
409 	/* Intialize with 1, because we want the same sequence of random values everytime we run test */
410 	randomSeed(1);
411 
412 	/* Data generation */
413 	for (unsigned int i = 0; i < m_n_elements; i++)
414 	{
415 		for (int j = 0; j < S; j++)
416 		{
417 			float a, b, c;
418 
419 			a = static_cast<float>(randomFormula(RAND_MAX)) /
420 					(static_cast<float>(static_cast<float>(RAND_MAX) / static_cast<float>(m_amplitude * 2.0f))) -
421 				m_amplitude;
422 			b = static_cast<float>(randomFormula(RAND_MAX)) /
423 					(static_cast<float>(static_cast<float>(RAND_MAX) / static_cast<float>(m_amplitude * 2.0f))) -
424 				m_amplitude;
425 			c = static_cast<float>(randomFormula(RAND_MAX)) /
426 					(static_cast<float>(static_cast<float>(RAND_MAX) / static_cast<float>(m_amplitude * 2.0f))) -
427 				m_amplitude;
428 
429 			// If values are of opposite sign, catastrophic cancellation is possible. 1 LSB of error
430 			// tolerance is relative to the larger intermediate terms, and once you compute a*b+c
431 			// you might get values with smaller exponents. Scale down one of the terms so that either
432 			// |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.
433 
434 			float axb = a * b;
435 			if (deFloatSign(axb) != deFloatSign(c))
436 			{
437 				if (de::inRange(de::abs(axb), de::abs(c), 2 * de::abs(c)))
438 				{
439 					c /= 2.0f;
440 				}
441 				else if (de::inRange(de::abs(c), de::abs(axb), 2 * de::abs(axb)))
442 				{
443 					a /= 2.0f;
444 				}
445 			}
446 
447 			m_data_a[i * S + j] = a;
448 			m_data_b[i * S + j] = b;
449 			m_data_c[i * S + j] = c;
450 		}
451 	}
452 }
453 
454 /** Returns code for Vertex Shader
455  *
456  *  @return pointer to literal with Vertex Shader code
457  */
458 template <INPUT_DATA_TYPE S>
generateVertexShaderCode()459 std::string				  GPUShader5FmaPrecision<S>::generateVertexShaderCode()
460 {
461 	std::string type;
462 
463 	switch (S)
464 	{
465 	case IDT_FLOAT:
466 	{
467 		type = "float";
468 
469 		break;
470 	}
471 
472 	case IDT_VEC2:
473 	{
474 		type = "vec2";
475 
476 		break;
477 	}
478 
479 	case IDT_VEC3:
480 	{
481 		type = "vec3";
482 
483 		break;
484 	}
485 
486 	case IDT_VEC4:
487 	{
488 		type = "vec4";
489 
490 		break;
491 	}
492 
493 	default:
494 	{
495 		TCU_FAIL("Incorrect variable type!");
496 		break;
497 	}
498 	} /* switch(S) */
499 
500 	/* Generate the vertex shader code */
501 	std::stringstream vsCode;
502 
503 	vsCode << "${VERSION}\n"
504 			  "\n"
505 			  "${GPU_SHADER5_REQUIRE}\n"
506 			  "\n"
507 			  "precision highp float;\n"
508 			  "\n"
509 			  "layout(location = 0) in "
510 		   << type << " a;\n"
511 		   << "layout(location = 1) in " << type << " b;\n"
512 		   << "layout(location = 2) in " << type << " c;\n"
513 		   << "\n"
514 		   << "layout(location = 0) out " << type << " resultFma;\n"
515 		   << "layout(location = 1) precise out " << type << " resultStd;\n"
516 		   << "\n"
517 		   << "\n"
518 		   << "void main()\n"
519 		   << "{\n"
520 		   << "    resultFma = fma(a,b,c);\n"
521 		   << "    resultStd = a * b + c;\n"
522 		   << "}\n";
523 
524 	return vsCode.str();
525 }
526 
527 /** Returns code for Fragment Shader
528  *
529  *  @return pointer to literal with Fragment Shader code
530  */
531 template <INPUT_DATA_TYPE S>
getFragmentShaderCode()532 const char*				  GPUShader5FmaPrecision<S>::getFragmentShaderCode()
533 {
534 	static const char* result = "${VERSION}\n"
535 								"\n"
536 								"${GPU_SHADER5_REQUIRE}\n"
537 								"\n"
538 								"precision highp float;\n"
539 								"\n"
540 								"void main(void)\n"
541 								"{\n"
542 								"}\n";
543 
544 	return result;
545 }
546 
547 } // namespace glcts
548