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
40namespace 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 */
48template <INPUT_DATA_TYPE S>
49GPUShader5FmaPrecision<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 */
69template <INPUT_DATA_TYPE S>
70void					  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 */
151template <INPUT_DATA_TYPE S>
152void					  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 */
268template <INPUT_DATA_TYPE	S>
269tcu::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 */
408template <INPUT_DATA_TYPE S>
409void					  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 */
460template <INPUT_DATA_TYPE S>
461std::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 */
533template <INPUT_DATA_TYPE S>
534const 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