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