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