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 #include "esextcGPUShader5FmaAccuracy.hpp"
25
26 #include "gluContextInfo.hpp"
27 #include "glwEnums.hpp"
28 #include "glwFunctions.hpp"
29 #include "tcuTestLog.hpp"
30
31 #include <vector>
32
33 namespace glcts
34 {
35 /* Fragment Shader */
36 const glw::GLchar* const GPUShader5FmaAccuracyTest::m_fragment_shader_code =
37 "${VERSION}\n"
38 "\n"
39 "${GPU_SHADER5_REQUIRE}\n"
40 "\n"
41 "precision highp float;\n"
42 "\n"
43 "layout(location = 0) out vec4 fs_out_color;\n"
44 "\n"
45 "void main()\n"
46 "{\n"
47 " fs_out_color = vec4(1, 1, 1, 1);\n"
48 "}\n";
49
50 /* Vertex Shader for fma pass */
51 const glw::GLchar* const GPUShader5FmaAccuracyTest::m_vertex_shader_code_for_fma_pass =
52 "${VERSION}\n"
53 "\n"
54 "${GPU_SHADER5_REQUIRE}\n"
55 "\n"
56 "precision highp float;\n"
57 "\n"
58 "uniform uint uni_number_of_steps;\n"
59 "out float vs_fs_result;\n"
60 "\n"
61 "void main()\n"
62 "{\n"
63 " float border_case = 1.0;\n"
64 " float h = 1.0 / float(uni_number_of_steps);\n"
65 " float current_value = border_case;\n"
66 "\n"
67 " for (uint step = 0u; step < uni_number_of_steps; ++step)\n"
68 " {\n"
69 " float next_value = fma(h, current_value, current_value);\n"
70 "\n"
71 " current_value = next_value;\n"
72 " }\n"
73 "\n"
74 " vs_fs_result = current_value;\n"
75 "}\n";
76
77 /* Vertex Shader for float pass */
78 const glw::GLchar* const GPUShader5FmaAccuracyTest::m_vertex_shader_code_for_float_pass =
79 "${VERSION}\n"
80 "\n"
81 "${GPU_SHADER5_REQUIRE}\n"
82 "\n"
83 "precision highp float;\n"
84 "\n"
85 "uniform uint uni_number_of_steps;\n"
86 "out float vs_fs_result;\n"
87 "\n"
88 "void main()\n"
89 "{\n"
90 " float border_case = 1.0;\n"
91 " float h = 1.0 / float(uni_number_of_steps);\n"
92 " float current_value = border_case;\n"
93 "\n"
94 " for (uint step = 0u; step < uni_number_of_steps; ++step)\n"
95 " {\n"
96 " float next_value = h * current_value + current_value;\n"
97 "\n"
98 " current_value = next_value;\n"
99 " }\n"
100 "\n"
101 " vs_fs_result = current_value;\n"
102 "}\n";
103
104 /* Constants */
105 const glw::GLuint GPUShader5FmaAccuracyTest::m_buffer_size = sizeof(glw::GLfloat);
106 const unsigned int GPUShader5FmaAccuracyTest::m_n_draw_call_executions = 10;
107 const float GPUShader5FmaAccuracyTest::m_expected_result = 2.71828f;
108
109 /** Constructor
110 *
111 * @param context Test context
112 * @param name Test case's name
113 * @param description Test case's description
114 **/
GPUShader5FmaAccuracyTest(Context & context,const ExtParameters & extParams,const char * name,const char * description)115 GPUShader5FmaAccuracyTest::GPUShader5FmaAccuracyTest(Context& context, const ExtParameters& extParams, const char* name,
116 const char* description)
117 : TestCaseBase(context, extParams, name, description)
118 , m_fragment_shader_id(0)
119 , m_program_object_id_for_float_pass(0)
120 , m_program_object_id_for_fma_pass(0)
121 , m_vertex_shader_id_for_float_pass(0)
122 , m_vertex_shader_id_for_fma_pass(0)
123 , m_buffer_object_id(0)
124 , m_vertex_array_object_id(0)
125 {
126 /* Nothing to be done here */
127 }
128
129 /** Initializes GLES objects used during the test.
130 *
131 **/
initTest()132 void GPUShader5FmaAccuracyTest::initTest()
133 {
134 /* This test should only run if EXT_gpu_shader5 is supported */
135 if (!m_is_gpu_shader5_supported)
136 {
137 throw tcu::NotSupportedError(GPU_SHADER5_EXTENSION_NOT_SUPPORTED, "", __FILE__, __LINE__);
138 }
139
140 /* Retrieve ES entry points. */
141 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
142
143 /* Create programs and shaders */
144 m_program_object_id_for_fma_pass = gl.createProgram();
145 m_program_object_id_for_float_pass = gl.createProgram();
146
147 m_fragment_shader_id = gl.createShader(GL_FRAGMENT_SHADER);
148 m_vertex_shader_id_for_fma_pass = gl.createShader(GL_VERTEX_SHADER);
149 m_vertex_shader_id_for_float_pass = gl.createShader(GL_VERTEX_SHADER);
150
151 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create program or shader object(s)");
152
153 /* Declare a name of a varying we will be capturing via TF */
154 const glw::GLchar* const captured_varying_name = "vs_fs_result";
155
156 /* Set up transform feedback for fma pass*/
157 gl.transformFeedbackVaryings(m_program_object_id_for_fma_pass, 1 /* count */, &captured_varying_name,
158 GL_INTERLEAVED_ATTRIBS);
159
160 /* Build program for fma pass */
161 if (false == buildProgram(m_program_object_id_for_fma_pass, m_fragment_shader_id, 1 /* number of FS parts */,
162 &m_fragment_shader_code, m_vertex_shader_id_for_fma_pass, 1 /* number of VS parts */,
163 &m_vertex_shader_code_for_fma_pass))
164 {
165 TCU_FAIL("Could not create program from a valid vertex/fragment shader");
166 }
167
168 /* Set up transform feedback for float pass*/
169 gl.transformFeedbackVaryings(m_program_object_id_for_float_pass, 1 /* count */, &captured_varying_name,
170 GL_INTERLEAVED_ATTRIBS);
171
172 /* Build program for float pass */
173 if (false == buildProgram(m_program_object_id_for_float_pass, m_fragment_shader_id, 1 /* number of FS parts */,
174 &m_fragment_shader_code, m_vertex_shader_id_for_float_pass, 1 /* number of VS parts */,
175 &m_vertex_shader_code_for_float_pass))
176 {
177 TCU_FAIL("Could not create program from valid vertex/fragment shader");
178 }
179
180 /* Generate and bind VAO */
181 gl.genVertexArrays(1, &m_vertex_array_object_id);
182 gl.bindVertexArray(m_vertex_array_object_id);
183
184 GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create and bind vertex array object");
185
186 /* Generate, bind and allocate buffer */
187 gl.genBuffers(1, &m_buffer_object_id);
188 gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer_object_id);
189 gl.bufferData(GL_ARRAY_BUFFER, m_buffer_size, DE_NULL /* undefined start data */, GL_STATIC_DRAW);
190
191 GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create vertex array object");
192 }
193
194 /** Executes the test.
195 * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
196 *
197 * Note the function throws exception should an error occur!
198 *
199 * @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again.
200 *
201 **/
iterate()202 tcu::TestCase::IterateResult GPUShader5FmaAccuracyTest::iterate()
203 {
204 initTest();
205
206 /* Storage space for result values */
207 std::vector<glw::GLfloat> results_of_float_pass;
208 std::vector<glw::GLfloat> results_of_fma_pass;
209
210 results_of_fma_pass.resize(m_n_draw_call_executions);
211 results_of_float_pass.resize(m_n_draw_call_executions);
212
213 /* Execute fma pass */
214 executePass(m_program_object_id_for_fma_pass, &results_of_fma_pass[0]);
215
216 /* Execute float pass */
217 executePass(m_program_object_id_for_float_pass, &results_of_float_pass[0]);
218
219 /* Storage space for relative errors */
220 std::vector<glw::GLfloat> relative_errors_for_float_pass;
221 std::vector<glw::GLfloat> relative_errors_for_fma_pass;
222
223 relative_errors_for_fma_pass.resize(m_n_draw_call_executions);
224 relative_errors_for_float_pass.resize(m_n_draw_call_executions);
225
226 /* Calculate relative errors */
227 for (glw::GLuint i = 0; i < m_n_draw_call_executions; ++i)
228 {
229 calculateRelativeError(results_of_fma_pass[i], m_expected_result, relative_errors_for_fma_pass[i]);
230 calculateRelativeError(results_of_float_pass[i], m_expected_result, relative_errors_for_float_pass[i]);
231 }
232
233 /* Sum relative errors */
234 glw::GLfloat relative_error_sum_for_fma_pass = 0.0f;
235 glw::GLfloat relative_error_sum_for_float_pass = 0.0f;
236
237 for (glw::GLuint i = 0; i < m_n_draw_call_executions; ++i)
238 {
239 relative_error_sum_for_fma_pass += relative_errors_for_fma_pass[i];
240 relative_error_sum_for_float_pass += relative_errors_for_float_pass[i];
241 }
242
243 if (relative_error_sum_for_fma_pass <= relative_error_sum_for_float_pass)
244 {
245 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
246 }
247 else
248 {
249 m_testCtx.getLog() << tcu::TestLog::Message << "Routine fma has lower accuracy than a * b + c !"
250 << tcu::TestLog::EndMessage;
251
252 /* Log sum of relative errors */
253 m_testCtx.getLog() << tcu::TestLog::Section("Sum of relative error", "");
254 m_testCtx.getLog() << tcu::TestLog::Message << "fma " << relative_error_sum_for_fma_pass
255 << tcu::TestLog::EndMessage;
256 m_testCtx.getLog() << tcu::TestLog::Message << "a * b + c " << relative_error_sum_for_float_pass
257 << tcu::TestLog::EndMessage;
258 m_testCtx.getLog() << tcu::TestLog::EndSection;
259
260 /* Log relative errors */
261 m_testCtx.getLog() << tcu::TestLog::Section("Relative errors", "");
262 logArray("fma", &relative_errors_for_fma_pass[0], m_n_draw_call_executions);
263 logArray("a * b + c", &relative_errors_for_float_pass[0], m_n_draw_call_executions);
264 m_testCtx.getLog() << tcu::TestLog::EndSection;
265
266 /* Log results */
267 m_testCtx.getLog() << tcu::TestLog::Section("Results", "");
268 logArray("fma", &results_of_fma_pass[0], m_n_draw_call_executions);
269 logArray("a * b + c", &results_of_float_pass[0], m_n_draw_call_executions);
270 m_testCtx.getLog() << tcu::TestLog::EndSection;
271
272 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
273 }
274
275 return STOP;
276 }
277
278 /** Deinitializes GLES objects created during the test.
279 *
280 */
deinit()281 void GPUShader5FmaAccuracyTest::deinit()
282 {
283 /* GL */
284 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
285
286 /* Bind default values */
287 gl.useProgram(0);
288 gl.bindVertexArray(0);
289 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, 0 /* id */);
290 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
291
292 /* Delete everything */
293 if (0 != m_vertex_array_object_id)
294 {
295 gl.deleteVertexArrays(1, &m_vertex_array_object_id);
296
297 m_vertex_array_object_id = 0;
298 }
299
300 if (0 != m_buffer_object_id)
301 {
302 gl.deleteBuffers(1, &m_buffer_object_id);
303
304 m_buffer_object_id = 0;
305 }
306
307 if (0 != m_program_object_id_for_fma_pass)
308 {
309 gl.deleteProgram(m_program_object_id_for_fma_pass);
310
311 m_program_object_id_for_fma_pass = 0;
312 }
313
314 if (0 != m_program_object_id_for_float_pass)
315 {
316 gl.deleteProgram(m_program_object_id_for_float_pass);
317
318 m_program_object_id_for_float_pass = 0;
319 }
320
321 if (0 != m_fragment_shader_id)
322 {
323 gl.deleteShader(m_fragment_shader_id);
324
325 m_fragment_shader_id = 0;
326 }
327
328 if (0 != m_vertex_shader_id_for_fma_pass)
329 {
330 gl.deleteShader(m_vertex_shader_id_for_fma_pass);
331
332 m_vertex_shader_id_for_fma_pass = 0;
333 }
334
335 if (0 != m_vertex_shader_id_for_float_pass)
336 {
337 gl.deleteShader(m_vertex_shader_id_for_float_pass);
338
339 m_vertex_shader_id_for_float_pass = 0;
340 }
341
342 /* Call base class' deinit() */
343 TestCaseBase::deinit();
344 }
345
346 /** Calculate relative error for given result and expected value.
347 *
348 * rel_err = delta / expected_result
349 * delta = | expected_result - result |
350 *
351 * @param result Result value
352 * @param expected_result Expected value
353 * @param relative_error Set to value of relative error
354 **/
calculateRelativeError(glw::GLfloat result,glw::GLfloat expected_result,glw::GLfloat & relative_error)355 void GPUShader5FmaAccuracyTest::calculateRelativeError(glw::GLfloat result, glw::GLfloat expected_result,
356 glw::GLfloat& relative_error)
357 {
358 const glw::GLfloat delta = de::abs(expected_result - result);
359
360 relative_error = delta / expected_result;
361 }
362
363 /** Executes single pass of test.
364 *
365 * @param program_object_id Gpu program that will be used during draw calls
366 * @param results Storage used for result values. It is expected to have capacity for at least m_n_draw_call_executions elements
367 **/
executePass(glw::GLuint program_object_id,glw::GLfloat * results)368 void GPUShader5FmaAccuracyTest::executePass(glw::GLuint program_object_id, glw::GLfloat* results)
369 {
370 /* GL */
371 const glw::Functions& gl = m_context.getRenderContext().getFunctions();
372
373 /* Uniform location */
374 glw::GLint uniform_location = gl.getUniformLocation(program_object_id, "uni_number_of_steps");
375
376 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get uniform location");
377
378 if (-1 == uniform_location)
379 {
380 TCU_FAIL("Unexpected inactive uniform location");
381 }
382
383 /* Setup transform feedback */
384 gl.enable(GL_RASTERIZER_DISCARD);
385 GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) call failed");
386
387 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */
388 m_buffer_object_id);
389 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to bind buffer object id to transform feedback binding point");
390
391 /* Setup draw call */
392 gl.useProgram(program_object_id);
393 GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed");
394
395 for (unsigned int i = 0; i < m_n_draw_call_executions; ++i)
396 {
397 /* Get number of steps for the Euler iterative algorithm */
398 glw::GLuint number_of_steps = getNumberOfStepsForIndex(i);
399
400 /* Set uniform data */
401 gl.uniform1ui(uniform_location, number_of_steps);
402 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set uniform data");
403
404 /* Start transform feedback */
405 gl.beginTransformFeedback(GL_POINTS);
406 {
407 /* Draw */
408 gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* one point */);
409 }
410 /* Stop transform feedback */
411 gl.endTransformFeedback();
412
413 GLU_EXPECT_NO_ERROR(gl.getError(), "Error doing a draw call");
414
415 /* Map transfrom feedback results */
416 const glw::GLfloat* transform_feedback_data = (const glw::GLfloat*)gl.mapBufferRange(
417 GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, m_buffer_size, GL_MAP_READ_BIT);
418
419 GLU_EXPECT_NO_ERROR(gl.getError(), "Could not map the buffer object into process space");
420
421 /* Copy data to results array */
422 results[i] = *transform_feedback_data;
423
424 /* Unmap transform feedback buffer */
425 gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
426
427 GLU_EXPECT_NO_ERROR(gl.getError(), "Error unmapping the buffer object");
428 }
429 }
430
431 /** Get number of steps that will be used by vertex shader to compute its result for given index of iteration
432 *
433 * @param index Index of iteration
434 *
435 * @return Number of steps
436 **/
getNumberOfStepsForIndex(glw::GLuint index)437 glw::GLuint GPUShader5FmaAccuracyTest::getNumberOfStepsForIndex(glw::GLuint index)
438 {
439 glw::GLuint number_of_steps = 10;
440
441 for (glw::GLuint i = 0; i < index; ++i)
442 {
443 number_of_steps *= 2;
444 }
445
446 return number_of_steps;
447 }
448
449 /** Logs contents of array
450 *
451 * @param description Description of data
452 * @param data Data to log
453 * @param length Number of values to log
454 **/
logArray(const char * description,glw::GLfloat * data,glw::GLuint length)455 void GPUShader5FmaAccuracyTest::logArray(const char* description, glw::GLfloat* data, glw::GLuint length)
456 {
457 m_testCtx.getLog() << tcu::TestLog::Message << description << tcu::TestLog::EndMessage;
458
459 tcu::MessageBuilder message = m_testCtx.getLog() << tcu::TestLog::Message;
460
461 message << "| ";
462
463 for (glw::GLuint i = 0; i < length; ++i)
464 {
465 message << data[i] << " | ";
466 }
467
468 message << tcu::TestLog::EndMessage;
469 }
470 } /* glcts */
471