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