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