1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.1 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 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 Common built-in function tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es31fShaderCommonFunctionTests.hpp" 25 #include "gluContextInfo.hpp" 26 #include "glsShaderExecUtil.hpp" 27 #include "tcuTestLog.hpp" 28 #include "tcuFormatUtil.hpp" 29 #include "tcuFloat.hpp" 30 #include "tcuInterval.hpp" 31 #include "tcuFloatFormat.hpp" 32 #include "deRandom.hpp" 33 #include "deMath.h" 34 #include "deString.h" 35 #include "deArrayUtil.hpp" 36 37 namespace deqp 38 { 39 namespace gles31 40 { 41 namespace Functional 42 { 43 44 using std::vector; 45 using std::string; 46 using tcu::TestLog; 47 using namespace gls::ShaderExecUtil; 48 49 using tcu::Vec2; 50 using tcu::Vec3; 51 using tcu::Vec4; 52 using tcu::IVec2; 53 using tcu::IVec3; 54 using tcu::IVec4; 55 56 // Utilities 57 58 template<typename T, int Size> 59 struct VecArrayAccess 60 { 61 public: 62 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {} 63 ~VecArrayAccess (void) {} 64 65 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; } 66 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; } 67 68 private: 69 tcu::Vector<T, Size>* m_array; 70 }; 71 72 template<typename T> T randomScalar (de::Random& rnd, T minValue, T maxValue); 73 template<> inline float randomScalar (de::Random& rnd, float minValue, float maxValue) { return rnd.getFloat(minValue, maxValue); } 74 template<> inline deInt32 randomScalar (de::Random& rnd, deInt32 minValue, deInt32 maxValue) { return rnd.getInt(minValue, maxValue); } 75 template<> inline deUint32 randomScalar (de::Random& rnd, deUint32 minValue, deUint32 maxValue) { return minValue + rnd.getUint32() % (maxValue - minValue + 1); } 76 77 template<typename T, int Size> 78 inline tcu::Vector<T, Size> randomVector (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue) 79 { 80 tcu::Vector<T, Size> res; 81 for (int ndx = 0; ndx < Size; ndx++) 82 res[ndx] = randomScalar<T>(rnd, minValue[ndx], maxValue[ndx]); 83 return res; 84 } 85 86 template<typename T, int Size> 87 static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0) 88 { 89 VecArrayAccess<T, Size> access(dst); 90 for (int ndx = 0; ndx < numValues; ndx++) 91 access[offset + ndx] = randomVector<T, Size>(rnd, minValue, maxValue); 92 } 93 94 template<typename T> 95 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0) 96 { 97 T* typedPtr = (T*)dst; 98 for (int ndx = 0; ndx < numValues; ndx++) 99 typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue); 100 } 101 102 inline int numBitsLostInOp (float input, float output) 103 { 104 const int inExp = tcu::Float32(input).exponent(); 105 const int outExp = tcu::Float32(output).exponent(); 106 107 return de::max(0, inExp-outExp); // Lost due to mantissa shift. 108 } 109 110 inline deUint32 getUlpDiff (float a, float b) 111 { 112 const deUint32 aBits = tcu::Float32(a).bits(); 113 const deUint32 bBits = tcu::Float32(b).bits(); 114 return aBits > bBits ? aBits - bBits : bBits - aBits; 115 } 116 117 inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b) 118 { 119 if (tcu::Float32(a).isZero()) 120 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b); 121 else if (tcu::Float32(b).isZero()) 122 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat()); 123 else 124 return getUlpDiff(a, b); 125 } 126 127 inline bool supportsSignedZero (glu::Precision precision) 128 { 129 // \note GLSL ES 3.1 doesn't really require support for -0, but we require it for highp 130 // as it is very widely supported. 131 return precision == glu::PRECISION_HIGHP; 132 } 133 134 inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff) 135 { 136 const int exp = tcu::Float32(value).exponent(); 137 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat(); 138 } 139 140 inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits) 141 { 142 const int numGarbageBits = 23-numAccurateBits; 143 const deUint32 mask = (1u<<numGarbageBits)-1u; 144 145 return mask; 146 } 147 148 inline float getEpsFromBits (float value, int numAccurateBits) 149 { 150 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits)); 151 } 152 153 static int getMinMantissaBits (glu::Precision precision) 154 { 155 const int bits[] = 156 { 157 7, // lowp 158 10, // mediump 159 23 // highp 160 }; 161 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST); 162 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits))); 163 return bits[precision]; 164 } 165 166 static int getMaxNormalizedValueExponent (glu::Precision precision) 167 { 168 const int exponent[] = 169 { 170 0, // lowp 171 13, // mediump 172 127 // highp 173 }; 174 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST); 175 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent))); 176 return exponent[precision]; 177 } 178 179 static int getMinNormalizedValueExponent (glu::Precision precision) 180 { 181 const int exponent[] = 182 { 183 -7, // lowp 184 -13, // mediump 185 -126 // highp 186 }; 187 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(exponent) == glu::PRECISION_LAST); 188 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(exponent))); 189 return exponent[precision]; 190 } 191 192 static float makeFloatRepresentable (float f, glu::Precision precision) 193 { 194 if (precision == glu::PRECISION_HIGHP) 195 { 196 // \note: assuming f is not extended-precision 197 return f; 198 } 199 else 200 { 201 const int numMantissaBits = getMinMantissaBits(precision); 202 const int maxNormalizedValueExponent = getMaxNormalizedValueExponent(precision); 203 const int minNormalizedValueExponent = getMinNormalizedValueExponent(precision); 204 const deUint32 representableMantissaMask = ((deUint32(1) << numMantissaBits) - 1) << (23 - (deUint32)numMantissaBits); 205 const float largestRepresentableValue = tcu::Float32::constructBits(+1, maxNormalizedValueExponent, ((1u << numMantissaBits) - 1u) << (23u - (deUint32)numMantissaBits)).asFloat(); 206 const bool zeroNotRepresentable = (precision == glu::PRECISION_LOWP); 207 208 // if zero is not required to be representable, use smallest positive non-subnormal value 209 const float zeroValue = (zeroNotRepresentable) ? (tcu::Float32::constructBits(+1, minNormalizedValueExponent, 1).asFloat()) : (0.0f); 210 211 const tcu::Float32 float32Representation (f); 212 213 if (float32Representation.exponent() < minNormalizedValueExponent) 214 { 215 // flush too small values to zero 216 return zeroValue; 217 } 218 else if (float32Representation.exponent() > maxNormalizedValueExponent) 219 { 220 // clamp too large values 221 return (float32Representation.sign() == +1) ? (largestRepresentableValue) : (-largestRepresentableValue); 222 } 223 else 224 { 225 // remove unrepresentable mantissa bits 226 const tcu::Float32 targetRepresentation(tcu::Float32::constructBits(float32Representation.sign(), 227 float32Representation.exponent(), 228 float32Representation.mantissaBits() & representableMantissaMask)); 229 230 return targetRepresentation.asFloat(); 231 } 232 } 233 } 234 235 // CommonFunctionCase 236 237 class CommonFunctionCase : public TestCase 238 { 239 public: 240 CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType); 241 ~CommonFunctionCase (void); 242 243 void init (void); 244 void deinit (void); 245 IterateResult iterate (void); 246 247 protected: 248 CommonFunctionCase (const CommonFunctionCase& other); 249 CommonFunctionCase& operator= (const CommonFunctionCase& other); 250 251 virtual void getInputValues (int numValues, void* const* values) const = 0; 252 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0; 253 254 glu::ShaderType m_shaderType; 255 ShaderSpec m_spec; 256 int m_numValues; 257 258 std::ostringstream m_failMsg; //!< Comparison failure help message. 259 260 private: 261 ShaderExecutor* m_executor; 262 }; 263 264 CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType) 265 : TestCase (context, name, description) 266 , m_shaderType (shaderType) 267 , m_numValues (100) 268 , m_executor (DE_NULL) 269 { 270 } 271 272 CommonFunctionCase::~CommonFunctionCase (void) 273 { 274 CommonFunctionCase::deinit(); 275 } 276 277 void CommonFunctionCase::init (void) 278 { 279 DE_ASSERT(!m_executor); 280 281 m_spec.version = contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ? glu::GLSL_VERSION_320_ES : glu::GLSL_VERSION_310_ES; 282 283 m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec); 284 m_testCtx.getLog() << m_executor; 285 286 if (!m_executor->isOk()) 287 throw tcu::TestError("Compile failed"); 288 } 289 290 void CommonFunctionCase::deinit (void) 291 { 292 delete m_executor; 293 m_executor = DE_NULL; 294 } 295 296 static vector<int> getScalarSizes (const vector<Symbol>& symbols) 297 { 298 vector<int> sizes(symbols.size()); 299 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx) 300 sizes[ndx] = symbols[ndx].varType.getScalarSize(); 301 return sizes; 302 } 303 304 static int computeTotalScalarSize (const vector<Symbol>& symbols) 305 { 306 int totalSize = 0; 307 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym) 308 totalSize += sym->varType.getScalarSize(); 309 return totalSize; 310 } 311 312 static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues) 313 { 314 vector<void*> pointers (symbols.size()); 315 int curScalarOffset = 0; 316 317 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx) 318 { 319 const Symbol& var = symbols[varNdx]; 320 const int scalarSize = var.varType.getScalarSize(); 321 322 // Uses planar layout as input/output specs do not support strides. 323 pointers[varNdx] = &data[curScalarOffset]; 324 curScalarOffset += scalarSize*numValues; 325 } 326 327 DE_ASSERT(curScalarOffset == (int)data.size()); 328 329 return pointers; 330 } 331 332 // \todo [2013-08-08 pyry] Make generic utility and move to glu? 333 334 struct HexFloat 335 { 336 const float value; 337 HexFloat (const float value_) : value(value_) {} 338 }; 339 340 std::ostream& operator<< (std::ostream& str, const HexFloat& v) 341 { 342 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits()); 343 } 344 345 struct HexBool 346 { 347 const deUint32 value; 348 HexBool (const deUint32 value_) : value(value_) {} 349 }; 350 351 std::ostream& operator<< (std::ostream& str, const HexBool& v) 352 { 353 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value); 354 } 355 356 struct VarValue 357 { 358 const glu::VarType& type; 359 const void* value; 360 361 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {} 362 }; 363 364 std::ostream& operator<< (std::ostream& str, const VarValue& varValue) 365 { 366 DE_ASSERT(varValue.type.isBasicType()); 367 368 const glu::DataType basicType = varValue.type.getBasicType(); 369 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType); 370 const int numComponents = glu::getDataTypeScalarSize(basicType); 371 372 if (numComponents > 1) 373 str << glu::getDataTypeName(basicType) << "("; 374 375 for (int compNdx = 0; compNdx < numComponents; compNdx++) 376 { 377 if (compNdx != 0) 378 str << ", "; 379 380 switch (scalarType) 381 { 382 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break; 383 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break; 384 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break; 385 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break; 386 387 default: 388 DE_ASSERT(false); 389 } 390 } 391 392 if (numComponents > 1) 393 str << ")"; 394 395 return str; 396 } 397 398 CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void) 399 { 400 const int numInputScalars = computeTotalScalarSize(m_spec.inputs); 401 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs); 402 vector<deUint32> inputData (numInputScalars * m_numValues); 403 vector<deUint32> outputData (numOutputScalars * m_numValues); 404 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues); 405 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues); 406 407 // Initialize input data. 408 getInputValues(m_numValues, &inputPointers[0]); 409 410 // Execute shader. 411 m_executor->useProgram(); 412 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]); 413 414 // Compare results. 415 { 416 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs); 417 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs); 418 vector<void*> curInputPtr (inputPointers.size()); 419 vector<void*> curOutputPtr (outputPointers.size()); 420 int numFailed = 0; 421 422 for (int valNdx = 0; valNdx < m_numValues; valNdx++) 423 { 424 // Set up pointers for comparison. 425 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx) 426 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx; 427 428 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx) 429 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx; 430 431 if (!compare(&curInputPtr[0], &curOutputPtr[0])) 432 { 433 // \todo [2013-08-08 pyry] We probably want to log reference value as well? 434 435 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage; 436 437 m_testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage; 438 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++) 439 m_testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = " 440 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx]) 441 << TestLog::EndMessage; 442 443 m_testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage; 444 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++) 445 m_testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = " 446 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx]) 447 << TestLog::EndMessage; 448 449 m_failMsg.str(""); 450 m_failMsg.clear(); 451 numFailed += 1; 452 } 453 } 454 455 m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage; 456 457 m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, 458 numFailed == 0 ? "Pass" : "Result comparison failed"); 459 } 460 461 return STOP; 462 } 463 464 static const char* getPrecisionPostfix (glu::Precision precision) 465 { 466 static const char* s_postfix[] = 467 { 468 "_lowp", 469 "_mediump", 470 "_highp" 471 }; 472 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_postfix) == glu::PRECISION_LAST); 473 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(s_postfix))); 474 return s_postfix[precision]; 475 } 476 477 static const char* getShaderTypePostfix (glu::ShaderType shaderType) 478 { 479 static const char* s_postfix[] = 480 { 481 "_vertex", 482 "_fragment", 483 "_geometry", 484 "_tess_control", 485 "_tess_eval", 486 "_compute" 487 }; 488 DE_ASSERT(de::inBounds<int>(shaderType, 0, DE_LENGTH_OF_ARRAY(s_postfix))); 489 return s_postfix[shaderType]; 490 } 491 492 static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 493 { 494 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType); 495 } 496 497 class AbsCase : public CommonFunctionCase 498 { 499 public: 500 AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 501 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType) 502 { 503 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 504 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 505 m_spec.source = "out0 = abs(in0);"; 506 } 507 508 void getInputValues (int numValues, void* const* values) const 509 { 510 const Vec2 floatRanges[] = 511 { 512 Vec2(-2.0f, 2.0f), // lowp 513 Vec2(-1e3f, 1e3f), // mediump 514 Vec2(-1e7f, 1e7f) // highp 515 }; 516 const IVec2 intRanges[] = 517 { 518 IVec2(-(1<<7)+1, (1<<7)-1), 519 IVec2(-(1<<15)+1, (1<<15)-1), 520 IVec2(0x80000001, 0x7fffffff) 521 }; 522 523 de::Random rnd (deStringHash(getName()) ^ 0x235facu); 524 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 525 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 526 const int scalarSize = glu::getDataTypeScalarSize(type); 527 528 if (glu::isDataTypeFloatOrVec(type)) 529 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize); 530 else 531 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize); 532 } 533 534 bool compare (const void* const* inputs, const void* const* outputs) 535 { 536 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 537 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 538 const int scalarSize = glu::getDataTypeScalarSize(type); 539 540 if (glu::isDataTypeFloatOrVec(type)) 541 { 542 const int mantissaBits = getMinMantissaBits(precision); 543 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u; 544 545 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 546 { 547 const float in0 = ((const float*)inputs[0])[compNdx]; 548 const float out0 = ((const float*)outputs[0])[compNdx]; 549 const float ref0 = de::abs(in0); 550 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0); 551 552 if (ulpDiff0 > maxUlpDiff) 553 { 554 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0; 555 return false; 556 } 557 } 558 } 559 else 560 { 561 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 562 { 563 const int in0 = ((const int*)inputs[0])[compNdx]; 564 const int out0 = ((const int*)outputs[0])[compNdx]; 565 const int ref0 = de::abs(in0); 566 567 if (out0 != ref0) 568 { 569 m_failMsg << "Expected [" << compNdx << "] = " << ref0; 570 return false; 571 } 572 } 573 } 574 575 return true; 576 } 577 }; 578 579 class SignCase : public CommonFunctionCase 580 { 581 public: 582 SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 583 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType) 584 { 585 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 586 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 587 m_spec.source = "out0 = sign(in0);"; 588 } 589 590 void getInputValues (int numValues, void* const* values) const 591 { 592 const Vec2 floatRanges[] = 593 { 594 Vec2(-2.0f, 2.0f), // lowp 595 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf 596 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf 597 }; 598 const IVec2 intRanges[] = 599 { 600 IVec2(-(1<<7), (1<<7)-1), 601 IVec2(-(1<<15), (1<<15)-1), 602 IVec2(0x80000000, 0x7fffffff) 603 }; 604 605 de::Random rnd (deStringHash(getName()) ^ 0x324u); 606 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 607 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 608 const int scalarSize = glu::getDataTypeScalarSize(type); 609 610 if (glu::isDataTypeFloatOrVec(type)) 611 { 612 // Special cases. 613 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f); 614 std::fill((float*)values[0] + scalarSize*1, (float*)values[0] + scalarSize*2, -1.0f); 615 std::fill((float*)values[0] + scalarSize*2, (float*)values[0] + scalarSize*3, 0.0f); 616 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize); 617 } 618 else 619 { 620 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1); 621 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1); 622 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0); 623 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize); 624 } 625 } 626 627 bool compare (const void* const* inputs, const void* const* outputs) 628 { 629 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 630 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 631 const int scalarSize = glu::getDataTypeScalarSize(type); 632 633 if (glu::isDataTypeFloatOrVec(type)) 634 { 635 // Both highp and mediump should be able to represent -1, 0, and +1 exactly 636 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0; 637 638 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 639 { 640 const float in0 = ((const float*)inputs[0])[compNdx]; 641 const float out0 = ((const float*)outputs[0])[compNdx]; 642 const float ref0 = in0 < 0.0f ? -1.0f : 643 in0 > 0.0f ? +1.0f : 0.0f; 644 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0); 645 646 if (ulpDiff0 > maxUlpDiff) 647 { 648 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0; 649 return false; 650 } 651 } 652 } 653 else 654 { 655 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 656 { 657 const int in0 = ((const int*)inputs[0])[compNdx]; 658 const int out0 = ((const int*)outputs[0])[compNdx]; 659 const int ref0 = in0 < 0 ? -1 : 660 in0 > 0 ? +1 : 0; 661 662 if (out0 != ref0) 663 { 664 m_failMsg << "Expected [" << compNdx << "] = " << ref0; 665 return false; 666 } 667 } 668 } 669 670 return true; 671 } 672 }; 673 674 static float roundEven (float v) 675 { 676 const float q = deFloatFrac(v); 677 const int truncated = int(v-q); 678 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up 679 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5 680 truncated; // Rounded down 681 682 return float(rounded); 683 } 684 685 class RoundEvenCase : public CommonFunctionCase 686 { 687 public: 688 RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 689 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType) 690 { 691 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 692 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 693 m_spec.source = "out0 = roundEven(in0);"; 694 } 695 696 void getInputValues (int numValues, void* const* values) const 697 { 698 const Vec2 ranges[] = 699 { 700 Vec2(-2.0f, 2.0f), // lowp 701 Vec2(-1e3f, 1e3f), // mediump 702 Vec2(-1e7f, 1e7f) // highp 703 }; 704 705 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 706 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 707 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 708 const int scalarSize = glu::getDataTypeScalarSize(type); 709 int numSpecialCases = 0; 710 711 // Special cases. 712 if (precision != glu::PRECISION_LOWP) 713 { 714 DE_ASSERT(numValues >= 20); 715 for (int ndx = 0; ndx < 20; ndx++) 716 { 717 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y()); 718 std::fill((float*)values[0], (float*)values[0] + scalarSize, v); 719 numSpecialCases += 1; 720 } 721 } 722 723 // Random cases. 724 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize); 725 726 // If precision is mediump, make sure values can be represented in fp16 exactly 727 if (precision == glu::PRECISION_MEDIUMP) 728 { 729 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 730 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 731 } 732 } 733 734 bool compare (const void* const* inputs, const void* const* outputs) 735 { 736 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 737 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 738 const bool hasSignedZero = supportsSignedZero(precision); 739 const int scalarSize = glu::getDataTypeScalarSize(type); 740 741 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 742 { 743 // Require exact rounding result. 744 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 745 { 746 const float in0 = ((const float*)inputs[0])[compNdx]; 747 const float out0 = ((const float*)outputs[0])[compNdx]; 748 const float ref = roundEven(in0); 749 750 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 751 752 if (ulpDiff > 0) 753 { 754 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 755 return false; 756 } 757 } 758 } 759 else 760 { 761 const int mantissaBits = getMinMantissaBits(precision); 762 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 763 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 764 765 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 766 { 767 const float in0 = ((const float*)inputs[0])[compNdx]; 768 const float out0 = ((const float*)outputs[0])[compNdx]; 769 const int minRes = int(roundEven(in0-eps)); 770 const int maxRes = int(roundEven(in0+eps)); 771 bool anyOk = false; 772 773 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 774 { 775 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 776 777 if (ulpDiff <= maxUlpDiff) 778 { 779 anyOk = true; 780 break; 781 } 782 } 783 784 if (!anyOk) 785 { 786 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 787 return false; 788 } 789 } 790 } 791 792 return true; 793 } 794 }; 795 796 class ModfCase : public CommonFunctionCase 797 { 798 public: 799 ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 800 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType) 801 { 802 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 803 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 804 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision))); 805 m_spec.source = "out0 = modf(in0, out1);"; 806 } 807 808 void getInputValues (int numValues, void* const* values) const 809 { 810 const Vec2 ranges[] = 811 { 812 Vec2(-2.0f, 2.0f), // lowp 813 Vec2(-1e3f, 1e3f), // mediump 814 Vec2(-1e7f, 1e7f) // highp 815 }; 816 817 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 818 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 819 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 820 const int scalarSize = glu::getDataTypeScalarSize(type); 821 822 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize); 823 } 824 825 bool compare (const void* const* inputs, const void* const* outputs) 826 { 827 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 828 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 829 const bool hasZeroSign = supportsSignedZero(precision); 830 const int scalarSize = glu::getDataTypeScalarSize(type); 831 832 const int mantissaBits = getMinMantissaBits(precision); 833 834 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 835 { 836 const float in0 = ((const float*)inputs[0])[compNdx]; 837 const float out0 = ((const float*)outputs[0])[compNdx]; 838 const float out1 = ((const float*)outputs[1])[compNdx]; 839 840 const float refOut1 = float(int(in0)); 841 const float refOut0 = in0 - refOut1; 842 843 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0; 844 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0)); 845 846 const float resSum = out0 + out1; 847 848 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0); 849 850 if (ulpDiff > maxUlpDiff) 851 { 852 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold " 853 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff); 854 return false; 855 } 856 } 857 858 return true; 859 } 860 }; 861 862 class IsnanCase : public CommonFunctionCase 863 { 864 public: 865 IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 866 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType) 867 { 868 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType)); 869 870 const int vecSize = glu::getDataTypeScalarSize(baseType); 871 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL; 872 873 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 874 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST))); 875 m_spec.source = "out0 = isnan(in0);"; 876 } 877 878 void getInputValues (int numValues, void* const* values) const 879 { 880 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu); 881 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 882 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 883 const int scalarSize = glu::getDataTypeScalarSize(type); 884 const int mantissaBits = getMinMantissaBits(precision); 885 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u); 886 887 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++) 888 { 889 const bool isNan = rnd.getFloat() > 0.3f; 890 const bool isInf = !isNan && rnd.getFloat() > 0.4f; 891 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0; 892 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu; 893 const deUint32 sign = rnd.getUint32() & 0x1u; 894 const deUint32 value = (sign << 31) | (exp << 23) | mantissa; 895 896 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan); 897 898 ((deUint32*)values[0])[valNdx] = value; 899 } 900 } 901 902 bool compare (const void* const* inputs, const void* const* outputs) 903 { 904 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 905 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 906 const int scalarSize = glu::getDataTypeScalarSize(type); 907 908 if (precision == glu::PRECISION_HIGHP) 909 { 910 // Only highp is required to support inf/nan 911 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 912 { 913 const float in0 = ((const float*)inputs[0])[compNdx]; 914 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0; 915 const bool ref = tcu::Float32(in0).isNaN(); 916 917 if (out0 != ref) 918 { 919 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false"); 920 return false; 921 } 922 } 923 } 924 else if (precision == glu::PRECISION_MEDIUMP || precision == glu::PRECISION_LOWP) 925 { 926 // NaN support is optional, check that inputs that are not NaN don't result in true. 927 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 928 { 929 const float in0 = ((const float*)inputs[0])[compNdx]; 930 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0; 931 const bool ref = tcu::Float32(in0).isNaN(); 932 933 if (!ref && out0) 934 { 935 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false"); 936 return false; 937 } 938 } 939 } 940 941 return true; 942 } 943 }; 944 945 class IsinfCase : public CommonFunctionCase 946 { 947 public: 948 IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 949 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType) 950 { 951 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType)); 952 953 const int vecSize = glu::getDataTypeScalarSize(baseType); 954 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL; 955 956 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 957 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST))); 958 m_spec.source = "out0 = isinf(in0);"; 959 } 960 961 void getInputValues (int numValues, void* const* values) const 962 { 963 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu); 964 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 965 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 966 const int scalarSize = glu::getDataTypeScalarSize(type); 967 const int mantissaBits = getMinMantissaBits(precision); 968 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u); 969 970 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++) 971 { 972 const bool isInf = rnd.getFloat() > 0.3f; 973 const bool isNan = !isInf && rnd.getFloat() > 0.4f; 974 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0; 975 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu; 976 const deUint32 sign = rnd.getUint32() & 0x1u; 977 const deUint32 value = (sign << 31) | (exp << 23) | mantissa; 978 979 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan); 980 981 ((deUint32*)values[0])[valNdx] = value; 982 } 983 } 984 985 bool compare (const void* const* inputs, const void* const* outputs) 986 { 987 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 988 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 989 const int scalarSize = glu::getDataTypeScalarSize(type); 990 991 if (precision == glu::PRECISION_HIGHP) 992 { 993 // Only highp is required to support inf/nan 994 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 995 { 996 const float in0 = ((const float*)inputs[0])[compNdx]; 997 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0; 998 const bool ref = tcu::Float32(in0).isInf(); 999 1000 if (out0 != ref) 1001 { 1002 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref); 1003 return false; 1004 } 1005 } 1006 } 1007 else if (precision == glu::PRECISION_MEDIUMP) 1008 { 1009 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true. 1010 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1011 { 1012 const float in0 = ((const float*)inputs[0])[compNdx]; 1013 const bool out0 = ((const deUint32*)outputs[0])[compNdx] != 0; 1014 const bool ref = tcu::Float16(in0).isInf(); 1015 1016 if (!ref && out0) 1017 { 1018 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false"); 1019 return false; 1020 } 1021 } 1022 } 1023 // else: no verification can be performed 1024 1025 return true; 1026 } 1027 }; 1028 1029 class FloatBitsToUintIntCase : public CommonFunctionCase 1030 { 1031 public: 1032 FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned) 1033 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType) 1034 { 1035 const int vecSize = glu::getDataTypeScalarSize(baseType); 1036 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT) 1037 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT); 1038 1039 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1040 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP))); 1041 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);"; 1042 } 1043 1044 void getInputValues (int numValues, void* const* values) const 1045 { 1046 const Vec2 ranges[] = 1047 { 1048 Vec2(-2.0f, 2.0f), // lowp 1049 Vec2(-1e3f, 1e3f), // mediump 1050 Vec2(-1e7f, 1e7f) // highp 1051 }; 1052 1053 de::Random rnd (deStringHash(getName()) ^ 0x2790au); 1054 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1055 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1056 const int scalarSize = glu::getDataTypeScalarSize(type); 1057 1058 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize); 1059 } 1060 1061 bool compare (const void* const* inputs, const void* const* outputs) 1062 { 1063 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1064 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1065 const int scalarSize = glu::getDataTypeScalarSize(type); 1066 1067 const int mantissaBits = getMinMantissaBits(precision); 1068 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); 1069 1070 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1071 { 1072 const float in0 = ((const float*)inputs[0])[compNdx]; 1073 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx]; 1074 const deUint32 refOut0 = tcu::Float32(in0).bits(); 1075 const int ulpDiff = de::abs((int)out0 - (int)refOut0); 1076 1077 if (ulpDiff > maxUlpDiff) 1078 { 1079 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold " 1080 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff); 1081 return false; 1082 } 1083 } 1084 1085 return true; 1086 } 1087 }; 1088 1089 class FloatBitsToIntCase : public FloatBitsToUintIntCase 1090 { 1091 public: 1092 FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1093 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true) 1094 { 1095 } 1096 }; 1097 1098 class FloatBitsToUintCase : public FloatBitsToUintIntCase 1099 { 1100 public: 1101 FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1102 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false) 1103 { 1104 } 1105 }; 1106 1107 class BitsToFloatCase : public CommonFunctionCase 1108 { 1109 public: 1110 BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType) 1111 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType) 1112 { 1113 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType); 1114 const int vecSize = glu::getDataTypeScalarSize(baseType); 1115 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT; 1116 1117 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP))); 1118 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP))); 1119 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);"; 1120 } 1121 1122 void getInputValues (int numValues, void* const* values) const 1123 { 1124 de::Random rnd (deStringHash(getName()) ^ 0xbbb225u); 1125 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1126 const int scalarSize = glu::getDataTypeScalarSize(type); 1127 const Vec2 range (-1e8f, +1e8f); 1128 1129 // \note Filled as floats. 1130 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize); 1131 } 1132 1133 bool compare (const void* const* inputs, const void* const* outputs) 1134 { 1135 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1136 const int scalarSize = glu::getDataTypeScalarSize(type); 1137 const deUint32 maxUlpDiff = 0; 1138 1139 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1140 { 1141 const float in0 = ((const float*)inputs[0])[compNdx]; 1142 const float out0 = ((const float*)outputs[0])[compNdx]; 1143 const deUint32 ulpDiff = getUlpDiff(in0, out0); 1144 1145 if (ulpDiff > maxUlpDiff) 1146 { 1147 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold " 1148 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff); 1149 return false; 1150 } 1151 } 1152 1153 return true; 1154 } 1155 }; 1156 1157 class FloorCase : public CommonFunctionCase 1158 { 1159 public: 1160 FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1161 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType) 1162 { 1163 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1164 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1165 m_spec.source = "out0 = floor(in0);"; 1166 } 1167 1168 void getInputValues (int numValues, void* const* values) const 1169 { 1170 const Vec2 ranges[] = 1171 { 1172 Vec2(-2.0f, 2.0f), // lowp 1173 Vec2(-1e3f, 1e3f), // mediump 1174 Vec2(-1e7f, 1e7f) // highp 1175 }; 1176 1177 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1178 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1179 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1180 const int scalarSize = glu::getDataTypeScalarSize(type); 1181 // Random cases. 1182 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize); 1183 1184 // If precision is mediump, make sure values can be represented in fp16 exactly 1185 if (precision == glu::PRECISION_MEDIUMP) 1186 { 1187 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1188 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1189 } 1190 } 1191 1192 bool compare (const void* const* inputs, const void* const* outputs) 1193 { 1194 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1195 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1196 const int scalarSize = glu::getDataTypeScalarSize(type); 1197 1198 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1199 { 1200 // Require exact result. 1201 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1202 { 1203 const float in0 = ((const float*)inputs[0])[compNdx]; 1204 const float out0 = ((const float*)outputs[0])[compNdx]; 1205 const float ref = deFloatFloor(in0); 1206 1207 const deUint32 ulpDiff = getUlpDiff(out0, ref); 1208 1209 if (ulpDiff > 0) 1210 { 1211 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1212 return false; 1213 } 1214 } 1215 } 1216 else 1217 { 1218 const int mantissaBits = getMinMantissaBits(precision); 1219 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1220 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1221 1222 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1223 { 1224 const float in0 = ((const float*)inputs[0])[compNdx]; 1225 const float out0 = ((const float*)outputs[0])[compNdx]; 1226 const int minRes = int(deFloatFloor(in0-eps)); 1227 const int maxRes = int(deFloatFloor(in0+eps)); 1228 bool anyOk = false; 1229 1230 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1231 { 1232 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal)); 1233 1234 if (ulpDiff <= maxUlpDiff) 1235 { 1236 anyOk = true; 1237 break; 1238 } 1239 } 1240 1241 if (!anyOk) 1242 { 1243 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1244 return false; 1245 } 1246 } 1247 } 1248 1249 return true; 1250 } 1251 }; 1252 1253 class TruncCase : public CommonFunctionCase 1254 { 1255 public: 1256 TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1257 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType) 1258 { 1259 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1260 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1261 m_spec.source = "out0 = trunc(in0);"; 1262 } 1263 1264 void getInputValues (int numValues, void* const* values) const 1265 { 1266 const Vec2 ranges[] = 1267 { 1268 Vec2(-2.0f, 2.0f), // lowp 1269 Vec2(-1e3f, 1e3f), // mediump 1270 Vec2(-1e7f, 1e7f) // highp 1271 }; 1272 1273 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1274 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1275 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1276 const int scalarSize = glu::getDataTypeScalarSize(type); 1277 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f }; 1278 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases); 1279 1280 // Special cases 1281 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++) 1282 { 1283 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) 1284 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx]; 1285 } 1286 1287 // Random cases. 1288 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize); 1289 1290 // If precision is mediump, make sure values can be represented in fp16 exactly 1291 if (precision == glu::PRECISION_MEDIUMP) 1292 { 1293 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1294 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1295 } 1296 } 1297 1298 bool compare (const void* const* inputs, const void* const* outputs) 1299 { 1300 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1301 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1302 const int scalarSize = glu::getDataTypeScalarSize(type); 1303 1304 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1305 { 1306 // Require exact result. 1307 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1308 { 1309 const float in0 = ((const float*)inputs[0])[compNdx]; 1310 const float out0 = ((const float*)outputs[0])[compNdx]; 1311 const bool isNeg = tcu::Float32(in0).sign() < 0; 1312 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0)); 1313 1314 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero. 1315 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref); 1316 1317 if (ulpDiff > 0) 1318 { 1319 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1320 return false; 1321 } 1322 } 1323 } 1324 else 1325 { 1326 const int mantissaBits = getMinMantissaBits(precision); 1327 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1328 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1329 1330 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1331 { 1332 const float in0 = ((const float*)inputs[0])[compNdx]; 1333 const float out0 = ((const float*)outputs[0])[compNdx]; 1334 const int minRes = int(in0-eps); 1335 const int maxRes = int(in0+eps); 1336 bool anyOk = false; 1337 1338 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1339 { 1340 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 1341 1342 if (ulpDiff <= maxUlpDiff) 1343 { 1344 anyOk = true; 1345 break; 1346 } 1347 } 1348 1349 if (!anyOk) 1350 { 1351 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1352 return false; 1353 } 1354 } 1355 } 1356 1357 return true; 1358 } 1359 }; 1360 1361 class RoundCase : public CommonFunctionCase 1362 { 1363 public: 1364 RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1365 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType) 1366 { 1367 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1368 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1369 m_spec.source = "out0 = round(in0);"; 1370 } 1371 1372 void getInputValues (int numValues, void* const* values) const 1373 { 1374 const Vec2 ranges[] = 1375 { 1376 Vec2(-2.0f, 2.0f), // lowp 1377 Vec2(-1e3f, 1e3f), // mediump 1378 Vec2(-1e7f, 1e7f) // highp 1379 }; 1380 1381 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1382 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1383 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1384 const int scalarSize = glu::getDataTypeScalarSize(type); 1385 int numSpecialCases = 0; 1386 1387 // Special cases. 1388 if (precision != glu::PRECISION_LOWP) 1389 { 1390 DE_ASSERT(numValues >= 10); 1391 for (int ndx = 0; ndx < 10; ndx++) 1392 { 1393 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y()); 1394 std::fill((float*)values[0], (float*)values[0] + scalarSize, v); 1395 numSpecialCases += 1; 1396 } 1397 } 1398 1399 // Random cases. 1400 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize); 1401 1402 // If precision is mediump, make sure values can be represented in fp16 exactly 1403 if (precision == glu::PRECISION_MEDIUMP) 1404 { 1405 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1406 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1407 } 1408 } 1409 1410 bool compare (const void* const* inputs, const void* const* outputs) 1411 { 1412 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1413 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1414 const bool hasZeroSign = supportsSignedZero(precision); 1415 const int scalarSize = glu::getDataTypeScalarSize(type); 1416 1417 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1418 { 1419 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1420 { 1421 const float in0 = ((const float*)inputs[0])[compNdx]; 1422 const float out0 = ((const float*)outputs[0])[compNdx]; 1423 1424 if (deFloatFrac(in0) == 0.5f) 1425 { 1426 // Allow both ceil(in) and floor(in) 1427 const float ref0 = deFloatFloor(in0); 1428 const float ref1 = deFloatCeil(in0); 1429 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0); 1430 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1); 1431 1432 if (ulpDiff0 > 0 && ulpDiff1 > 0) 1433 { 1434 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1)); 1435 return false; 1436 } 1437 } 1438 else 1439 { 1440 // Require exact result 1441 const float ref = roundEven(in0); 1442 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 1443 1444 if (ulpDiff > 0) 1445 { 1446 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1447 return false; 1448 } 1449 } 1450 } 1451 } 1452 else 1453 { 1454 const int mantissaBits = getMinMantissaBits(precision); 1455 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1456 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1457 1458 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1459 { 1460 const float in0 = ((const float*)inputs[0])[compNdx]; 1461 const float out0 = ((const float*)outputs[0])[compNdx]; 1462 const int minRes = int(roundEven(in0-eps)); 1463 const int maxRes = int(roundEven(in0+eps)); 1464 bool anyOk = false; 1465 1466 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1467 { 1468 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 1469 1470 if (ulpDiff <= maxUlpDiff) 1471 { 1472 anyOk = true; 1473 break; 1474 } 1475 } 1476 1477 if (!anyOk) 1478 { 1479 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1480 return false; 1481 } 1482 } 1483 } 1484 1485 return true; 1486 } 1487 }; 1488 1489 class CeilCase : public CommonFunctionCase 1490 { 1491 public: 1492 CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1493 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType) 1494 { 1495 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1496 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1497 m_spec.source = "out0 = ceil(in0);"; 1498 } 1499 1500 void getInputValues (int numValues, void* const* values) const 1501 { 1502 const Vec2 ranges[] = 1503 { 1504 Vec2(-2.0f, 2.0f), // lowp 1505 Vec2(-1e3f, 1e3f), // mediump 1506 Vec2(-1e7f, 1e7f) // highp 1507 }; 1508 1509 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1510 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1511 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1512 const int scalarSize = glu::getDataTypeScalarSize(type); 1513 1514 // Random cases. 1515 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize); 1516 1517 // If precision is mediump, make sure values can be represented in fp16 exactly 1518 if (precision == glu::PRECISION_MEDIUMP) 1519 { 1520 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1521 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1522 } 1523 } 1524 1525 bool compare (const void* const* inputs, const void* const* outputs) 1526 { 1527 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1528 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1529 const bool hasZeroSign = supportsSignedZero(precision); 1530 const int scalarSize = glu::getDataTypeScalarSize(type); 1531 1532 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1533 { 1534 // Require exact result. 1535 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1536 { 1537 const float in0 = ((const float*)inputs[0])[compNdx]; 1538 const float out0 = ((const float*)outputs[0])[compNdx]; 1539 const float ref = deFloatCeil(in0); 1540 1541 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 1542 1543 if (ulpDiff > 0) 1544 { 1545 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1546 return false; 1547 } 1548 } 1549 } 1550 else 1551 { 1552 const int mantissaBits = getMinMantissaBits(precision); 1553 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1554 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1555 1556 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1557 { 1558 const float in0 = ((const float*)inputs[0])[compNdx]; 1559 const float out0 = ((const float*)outputs[0])[compNdx]; 1560 const int minRes = int(deFloatCeil(in0-eps)); 1561 const int maxRes = int(deFloatCeil(in0+eps)); 1562 bool anyOk = false; 1563 1564 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1565 { 1566 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 1567 1568 if (ulpDiff <= maxUlpDiff) 1569 { 1570 anyOk = true; 1571 break; 1572 } 1573 } 1574 1575 if (!anyOk && de::inRange(0, minRes, maxRes)) 1576 { 1577 // Allow -0 as well. 1578 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u); 1579 anyOk = ((deUint32)ulpDiff <= maxUlpDiff); 1580 } 1581 1582 if (!anyOk) 1583 { 1584 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1585 return false; 1586 } 1587 } 1588 } 1589 1590 return true; 1591 } 1592 }; 1593 1594 class FractCase : public CommonFunctionCase 1595 { 1596 public: 1597 FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1598 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType) 1599 { 1600 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1601 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1602 m_spec.source = "out0 = fract(in0);"; 1603 } 1604 1605 void getInputValues (int numValues, void* const* values) const 1606 { 1607 const Vec2 ranges[] = 1608 { 1609 Vec2(-2.0f, 2.0f), // lowp 1610 Vec2(-1e3f, 1e3f), // mediump 1611 Vec2(-1e7f, 1e7f) // highp 1612 }; 1613 1614 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1615 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1616 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1617 const int scalarSize = glu::getDataTypeScalarSize(type); 1618 int numSpecialCases = 0; 1619 1620 // Special cases. 1621 if (precision != glu::PRECISION_LOWP) 1622 { 1623 DE_ASSERT(numValues >= 10); 1624 for (int ndx = 0; ndx < 10; ndx++) 1625 { 1626 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y()); 1627 std::fill((float*)values[0], (float*)values[0] + scalarSize, v); 1628 numSpecialCases += 1; 1629 } 1630 } 1631 1632 // Random cases. 1633 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize); 1634 1635 // If precision is mediump, make sure values can be represented in fp16 exactly 1636 if (precision == glu::PRECISION_MEDIUMP) 1637 { 1638 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1639 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1640 } 1641 } 1642 1643 bool compare (const void* const* inputs, const void* const* outputs) 1644 { 1645 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1646 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1647 const bool hasZeroSign = supportsSignedZero(precision); 1648 const int scalarSize = glu::getDataTypeScalarSize(type); 1649 1650 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1651 { 1652 // Require exact result. 1653 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1654 { 1655 const float in0 = ((const float*)inputs[0])[compNdx]; 1656 const float out0 = ((const float*)outputs[0])[compNdx]; 1657 const float ref = deFloatFrac(in0); 1658 1659 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 1660 1661 if (ulpDiff > 0) 1662 { 1663 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1664 return false; 1665 } 1666 } 1667 } 1668 else 1669 { 1670 const int mantissaBits = getMinMantissaBits(precision); 1671 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1672 1673 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1674 { 1675 const float in0 = ((const float*)inputs[0])[compNdx]; 1676 const float out0 = ((const float*)outputs[0])[compNdx]; 1677 1678 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps))) 1679 { 1680 const float ref = deFloatFrac(in0); 1681 const int bitsLost = numBitsLostInOp(in0, ref); 1682 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value. 1683 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref); 1684 1685 if (ulpDiff > maxUlpDiff) 1686 { 1687 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff); 1688 return false; 1689 } 1690 } 1691 else 1692 { 1693 if (out0 >= 1.0f) 1694 { 1695 m_failMsg << "Expected [" << compNdx << "] < 1.0"; 1696 return false; 1697 } 1698 } 1699 } 1700 } 1701 1702 return true; 1703 } 1704 }; 1705 1706 static inline void frexp (float in, float* significand, int* exponent) 1707 { 1708 const tcu::Float32 fpValue(in); 1709 1710 if (!fpValue.isZero()) 1711 { 1712 // Construct float that has exactly the mantissa, and exponent of -1. 1713 *significand = tcu::Float32::construct(fpValue.sign(), -1, fpValue.mantissa()).asFloat(); 1714 *exponent = fpValue.exponent()+1; 1715 } 1716 else 1717 { 1718 *significand = fpValue.sign() < 0 ? -0.0f : 0.0f; 1719 *exponent = 0; 1720 } 1721 } 1722 1723 static inline float ldexp (float significand, int exponent) 1724 { 1725 const tcu::Float32 mant(significand); 1726 1727 if (exponent == 0 && mant.isZero()) 1728 { 1729 return mant.sign() < 0 ? -0.0f : 0.0f; 1730 } 1731 else 1732 { 1733 return tcu::Float32::construct(mant.sign(), exponent+mant.exponent(), mant.mantissa()).asFloat(); 1734 } 1735 } 1736 1737 class FrexpCase : public CommonFunctionCase 1738 { 1739 public: 1740 FrexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1741 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "frexp", shaderType) 1742 { 1743 const int vecSize = glu::getDataTypeScalarSize(baseType); 1744 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT; 1745 1746 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1747 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP))); 1748 m_spec.outputs.push_back(Symbol("out1", glu::VarType(intType, glu::PRECISION_HIGHP))); 1749 m_spec.source = "out0 = frexp(in0, out1);"; 1750 } 1751 1752 void getInputValues (int numValues, void* const* values) const 1753 { 1754 const Vec2 ranges[] = 1755 { 1756 Vec2(-2.0f, 2.0f), // lowp 1757 Vec2(-1e3f, 1e3f), // mediump 1758 Vec2(-1e7f, 1e7f) // highp 1759 }; 1760 1761 de::Random rnd (deStringHash(getName()) ^ 0x2790au); 1762 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1763 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1764 const int scalarSize = glu::getDataTypeScalarSize(type); 1765 1766 // Special cases 1767 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1768 { 1769 ((float*)values[0])[scalarSize*0 + compNdx] = 0.0f; 1770 ((float*)values[0])[scalarSize*1 + compNdx] = -0.0f; 1771 ((float*)values[0])[scalarSize*2 + compNdx] = 0.5f; 1772 ((float*)values[0])[scalarSize*3 + compNdx] = -0.5f; 1773 ((float*)values[0])[scalarSize*4 + compNdx] = 1.0f; 1774 ((float*)values[0])[scalarSize*5 + compNdx] = -1.0f; 1775 ((float*)values[0])[scalarSize*6 + compNdx] = 2.0f; 1776 ((float*)values[0])[scalarSize*7 + compNdx] = -2.0f; 1777 } 1778 1779 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + 8*scalarSize, (numValues-8)*scalarSize); 1780 1781 // Make sure the values are representable in the target format 1782 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx) 1783 { 1784 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) 1785 { 1786 float* const valuePtr = &((float*)values[0])[caseNdx * scalarSize + scalarNdx]; 1787 1788 *valuePtr = makeFloatRepresentable(*valuePtr, precision); 1789 } 1790 } 1791 } 1792 1793 bool compare (const void* const* inputs, const void* const* outputs) 1794 { 1795 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1796 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1797 const int scalarSize = glu::getDataTypeScalarSize(type); 1798 const bool signedZero = false; 1799 1800 const int mantissaBits = getMinMantissaBits(precision); 1801 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); 1802 1803 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1804 { 1805 const float in0 = ((const float*)inputs[0])[compNdx]; 1806 const float out0 = ((const float*)outputs[0])[compNdx]; 1807 const int out1 = ((const int*)outputs[1])[compNdx]; 1808 1809 float refOut0; 1810 int refOut1; 1811 1812 frexp(in0, &refOut0, &refOut1); 1813 1814 const deUint32 ulpDiff0 = signedZero ? getUlpDiff(out0, refOut0) : getUlpDiffIgnoreZeroSign(out0, refOut0); 1815 1816 if (ulpDiff0 > maxUlpDiff || out1 != refOut1) 1817 { 1818 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", " << refOut1 << " with ULP threshold " 1819 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff0); 1820 return false; 1821 } 1822 } 1823 1824 return true; 1825 } 1826 }; 1827 1828 class LdexpCase : public CommonFunctionCase 1829 { 1830 public: 1831 LdexpCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1832 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ldexp", shaderType) 1833 { 1834 const int vecSize = glu::getDataTypeScalarSize(baseType); 1835 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT; 1836 1837 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1838 m_spec.inputs.push_back(Symbol("in1", glu::VarType(intType, glu::PRECISION_HIGHP))); 1839 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, glu::PRECISION_HIGHP))); 1840 m_spec.source = "out0 = ldexp(in0, in1);"; 1841 } 1842 1843 void getInputValues (int numValues, void* const* values) const 1844 { 1845 const Vec2 ranges[] = 1846 { 1847 Vec2(-2.0f, 2.0f), // lowp 1848 Vec2(-1e3f, 1e3f), // mediump 1849 Vec2(-1e7f, 1e7f) // highp 1850 }; 1851 1852 de::Random rnd (deStringHash(getName()) ^ 0x2790au); 1853 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1854 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1855 const int scalarSize = glu::getDataTypeScalarSize(type); 1856 int valueNdx = 0; 1857 1858 { 1859 const float easySpecialCases[] = { 0.0f, -0.0f, 0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f }; 1860 1861 DE_ASSERT(valueNdx + DE_LENGTH_OF_ARRAY(easySpecialCases) <= numValues); 1862 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(easySpecialCases); caseNdx++) 1863 { 1864 float in0; 1865 int in1; 1866 1867 frexp(easySpecialCases[caseNdx], &in0, &in1); 1868 1869 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1870 { 1871 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0; 1872 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1; 1873 } 1874 1875 valueNdx += 1; 1876 } 1877 } 1878 1879 { 1880 // \note lowp and mediump can not necessarily fit the values in hard cases, so we'll use only easy ones. 1881 const int numEasyRandomCases = precision == glu::PRECISION_HIGHP ? 50 : (numValues-valueNdx); 1882 1883 DE_ASSERT(valueNdx + numEasyRandomCases <= numValues); 1884 for (int caseNdx = 0; caseNdx < numEasyRandomCases; caseNdx++) 1885 { 1886 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1887 { 1888 const float in = rnd.getFloat(ranges[precision].x(), ranges[precision].y()); 1889 float in0; 1890 int in1; 1891 1892 frexp(in, &in0, &in1); 1893 1894 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0; 1895 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1; 1896 } 1897 1898 valueNdx += 1; 1899 } 1900 } 1901 1902 { 1903 const int numHardRandomCases = numValues-valueNdx; 1904 DE_ASSERT(numHardRandomCases >= 0 && valueNdx + numHardRandomCases <= numValues); 1905 1906 for (int caseNdx = 0; caseNdx < numHardRandomCases; caseNdx++) 1907 { 1908 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1909 { 1910 const int fpExp = rnd.getInt(-126, 127); 1911 const int sign = rnd.getBool() ? -1 : +1; 1912 const deUint32 mantissa = (1u<<23) | (rnd.getUint32() & ((1u<<23)-1)); 1913 const int in1 = rnd.getInt(de::max(-126, -126-fpExp), de::min(127, 127-fpExp)); 1914 const float in0 = tcu::Float32::construct(sign, fpExp, mantissa).asFloat(); 1915 1916 DE_ASSERT(de::inRange(in1, -126, 127)); // See Khronos bug 11180 1917 DE_ASSERT(de::inRange(in1+fpExp, -126, 127)); 1918 1919 const float out = ldexp(in0, in1); 1920 1921 DE_ASSERT(!tcu::Float32(out).isInf() && !tcu::Float32(out).isDenorm()); 1922 DE_UNREF(out); 1923 1924 ((float*)values[0])[valueNdx*scalarSize + compNdx] = in0; 1925 ((int*)values[1])[valueNdx*scalarSize + compNdx] = in1; 1926 } 1927 1928 valueNdx += 1; 1929 } 1930 } 1931 } 1932 1933 bool compare (const void* const* inputs, const void* const* outputs) 1934 { 1935 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1936 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1937 const int scalarSize = glu::getDataTypeScalarSize(type); 1938 1939 const int mantissaBits = getMinMantissaBits(precision); 1940 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); 1941 1942 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1943 { 1944 const float in0 = ((const float*)inputs[0])[compNdx]; 1945 const int in1 = ((const int*)inputs[1])[compNdx]; 1946 const float out0 = ((const float*)outputs[0])[compNdx]; 1947 const float refOut0 = ldexp(in0, in1); 1948 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, refOut0); 1949 1950 const int inExp = tcu::Float32(in0).exponent(); 1951 1952 if (ulpDiff > maxUlpDiff) 1953 { 1954 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(refOut0) << ", (exp = " << inExp << ") with ULP threshold " 1955 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff); 1956 return false; 1957 } 1958 } 1959 1960 return true; 1961 } 1962 }; 1963 1964 class FmaCase : public CommonFunctionCase 1965 { 1966 public: 1967 FmaCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1968 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fma", shaderType) 1969 { 1970 m_spec.inputs.push_back(Symbol("a", glu::VarType(baseType, precision))); 1971 m_spec.inputs.push_back(Symbol("b", glu::VarType(baseType, precision))); 1972 m_spec.inputs.push_back(Symbol("c", glu::VarType(baseType, precision))); 1973 m_spec.outputs.push_back(Symbol("res", glu::VarType(baseType, precision))); 1974 m_spec.source = "res = fma(a, b, c);"; 1975 m_spec.globalDeclarations = "#extension GL_EXT_gpu_shader5 : require\n"; 1976 } 1977 1978 void init (void) 1979 { 1980 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5")) 1981 throw tcu::NotSupportedError("GL_EXT_gpu_shader5 not supported"); 1982 1983 CommonFunctionCase::init(); 1984 } 1985 1986 void getInputValues (int numValues, void* const* values) const 1987 { 1988 const Vec2 ranges[] = 1989 { 1990 Vec2(-2.0f, 2.0f), // lowp 1991 Vec2(-127.f, 127.f), // mediump 1992 Vec2(-1e7f, 1e7f) // highp 1993 }; 1994 1995 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1996 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1997 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1998 const int scalarSize = glu::getDataTypeScalarSize(type); 1999 const float specialCases[][3] = 2000 { 2001 // a b c 2002 { 0.0f, 0.0f, 0.0f }, 2003 { 0.0f, 1.0f, 0.0f }, 2004 { 0.0f, 0.0f, -1.0f }, 2005 { 1.0f, 1.0f, 0.0f }, 2006 { 1.0f, 1.0f, 1.0f }, 2007 { -1.0f, 1.0f, 0.0f }, 2008 { 1.0f, -1.0f, 0.0f }, 2009 { -1.0f, -1.0f, 0.0f }, 2010 { -0.0f, 1.0f, 0.0f }, 2011 { 1.0f, -0.0f, 0.0f } 2012 }; 2013 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases); 2014 2015 // Special cases 2016 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++) 2017 { 2018 for (int inputNdx = 0; inputNdx < 3; inputNdx++) 2019 { 2020 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) 2021 ((float*)values[inputNdx])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx][inputNdx]; 2022 } 2023 } 2024 2025 // Random cases. 2026 { 2027 const int numScalars = (numValues-numSpecialCases)*scalarSize; 2028 const int offs = scalarSize*numSpecialCases; 2029 2030 for (int inputNdx = 0; inputNdx < 3; inputNdx++) 2031 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[inputNdx] + offs, numScalars); 2032 } 2033 2034 // Make sure the values are representable in the target format 2035 for (int inputNdx = 0; inputNdx < 3; inputNdx++) 2036 { 2037 for (int caseNdx = 0; caseNdx < numValues; ++caseNdx) 2038 { 2039 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) 2040 { 2041 float* const valuePtr = &((float*)values[inputNdx])[caseNdx * scalarSize + scalarNdx]; 2042 2043 *valuePtr = makeFloatRepresentable(*valuePtr, precision); 2044 } 2045 } 2046 } 2047 } 2048 2049 static tcu::Interval fma (glu::Precision precision, float a, float b, float c) 2050 { 2051 const tcu::FloatFormat formats[] = 2052 { 2053 // minExp maxExp mantissa exact, subnormals infinities NaN 2054 tcu::FloatFormat(0, 0, 7, false, tcu::YES, tcu::MAYBE, tcu::MAYBE), 2055 tcu::FloatFormat(-13, 13, 9, false, tcu::MAYBE, tcu::MAYBE, tcu::MAYBE), 2056 tcu::FloatFormat(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE) 2057 }; 2058 const tcu::FloatFormat& format = de::getSizedArrayElement<glu::PRECISION_LAST>(formats, precision); 2059 const tcu::Interval ia = format.convert(a); 2060 const tcu::Interval ib = format.convert(b); 2061 const tcu::Interval ic = format.convert(c); 2062 tcu::Interval prod0; 2063 tcu::Interval prod1; 2064 tcu::Interval prod2; 2065 tcu::Interval prod3; 2066 tcu::Interval prod; 2067 tcu::Interval res; 2068 2069 TCU_SET_INTERVAL(prod0, tmp, tmp = ia.lo() * ib.lo()); 2070 TCU_SET_INTERVAL(prod1, tmp, tmp = ia.lo() * ib.hi()); 2071 TCU_SET_INTERVAL(prod2, tmp, tmp = ia.hi() * ib.lo()); 2072 TCU_SET_INTERVAL(prod3, tmp, tmp = ia.hi() * ib.hi()); 2073 2074 prod = format.convert(format.roundOut(prod0 | prod1 | prod2 | prod3, ia.isFinite() && ib.isFinite())); 2075 2076 TCU_SET_INTERVAL_BOUNDS(res, tmp, 2077 tmp = prod.lo() + ic.lo(), 2078 tmp = prod.hi() + ic.hi()); 2079 2080 return format.convert(format.roundOut(res, prod.isFinite() && ic.isFinite())); 2081 } 2082 2083 bool compare (const void* const* inputs, const void* const* outputs) 2084 { 2085 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 2086 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 2087 const int scalarSize = glu::getDataTypeScalarSize(type); 2088 2089 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 2090 { 2091 const float a = ((const float*)inputs[0])[compNdx]; 2092 const float b = ((const float*)inputs[1])[compNdx]; 2093 const float c = ((const float*)inputs[2])[compNdx]; 2094 const float res = ((const float*)outputs[0])[compNdx]; 2095 const tcu::Interval ref = fma(precision, a, b, c); 2096 2097 if (!ref.contains(res)) 2098 { 2099 m_failMsg << "Expected [" << compNdx << "] = " << ref; 2100 return false; 2101 } 2102 } 2103 2104 return true; 2105 } 2106 }; 2107 2108 ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context) 2109 : TestCaseGroup(context, "common", "Common function tests") 2110 { 2111 } 2112 2113 ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void) 2114 { 2115 } 2116 2117 template<class TestClass> 2118 static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes, deUint32 shaderBits) 2119 { 2120 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName); 2121 parent->addChild(group); 2122 2123 const glu::DataType scalarTypes[] = 2124 { 2125 glu::TYPE_FLOAT, 2126 glu::TYPE_INT, 2127 glu::TYPE_UINT 2128 }; 2129 2130 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++) 2131 { 2132 const glu::DataType scalarType = scalarTypes[scalarTypeNdx]; 2133 2134 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) || 2135 (!intTypes && scalarType == glu::TYPE_INT) || 2136 (!uintTypes && scalarType == glu::TYPE_UINT)) 2137 continue; 2138 2139 for (int vecSize = 1; vecSize <= 4; vecSize++) 2140 { 2141 for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++) 2142 { 2143 for (int shaderTypeNdx = 0; shaderTypeNdx < glu::SHADERTYPE_LAST; shaderTypeNdx++) 2144 { 2145 if (shaderBits & (1<<shaderTypeNdx)) 2146 group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderTypeNdx))); 2147 } 2148 } 2149 } 2150 } 2151 } 2152 2153 void ShaderCommonFunctionTests::init (void) 2154 { 2155 enum 2156 { 2157 VS = (1<<glu::SHADERTYPE_VERTEX), 2158 TC = (1<<glu::SHADERTYPE_TESSELLATION_CONTROL), 2159 TE = (1<<glu::SHADERTYPE_TESSELLATION_EVALUATION), 2160 GS = (1<<glu::SHADERTYPE_GEOMETRY), 2161 FS = (1<<glu::SHADERTYPE_FRAGMENT), 2162 CS = (1<<glu::SHADERTYPE_COMPUTE), 2163 2164 ALL_SHADERS = VS|TC|TE|GS|FS|CS, 2165 NEW_SHADERS = TC|TE|GS|CS, 2166 }; 2167 2168 // Float? Int? Uint? Shaders 2169 addFunctionCases<AbsCase> (this, "abs", true, true, false, NEW_SHADERS); 2170 addFunctionCases<SignCase> (this, "sign", true, true, false, NEW_SHADERS); 2171 addFunctionCases<FloorCase> (this, "floor", true, false, false, NEW_SHADERS); 2172 addFunctionCases<TruncCase> (this, "trunc", true, false, false, NEW_SHADERS); 2173 addFunctionCases<RoundCase> (this, "round", true, false, false, NEW_SHADERS); 2174 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false, NEW_SHADERS); 2175 addFunctionCases<CeilCase> (this, "ceil", true, false, false, NEW_SHADERS); 2176 addFunctionCases<FractCase> (this, "fract", true, false, false, NEW_SHADERS); 2177 // mod 2178 addFunctionCases<ModfCase> (this, "modf", true, false, false, NEW_SHADERS); 2179 // min 2180 // max 2181 // clamp 2182 // mix 2183 // step 2184 // smoothstep 2185 addFunctionCases<IsnanCase> (this, "isnan", true, false, false, NEW_SHADERS); 2186 addFunctionCases<IsinfCase> (this, "isinf", true, false, false, NEW_SHADERS); 2187 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false, NEW_SHADERS); 2188 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false, NEW_SHADERS); 2189 2190 addFunctionCases<FrexpCase> (this, "frexp", true, false, false, ALL_SHADERS); 2191 addFunctionCases<LdexpCase> (this, "ldexp", true, false, false, ALL_SHADERS); 2192 addFunctionCases<FmaCase> (this, "fma", true, false, false, ALL_SHADERS); 2193 2194 // (u)intBitsToFloat() 2195 { 2196 const deUint32 shaderBits = NEW_SHADERS; 2197 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests"); 2198 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests"); 2199 2200 addChild(intGroup); 2201 addChild(uintGroup); 2202 2203 for (int vecSize = 1; vecSize < 4; vecSize++) 2204 { 2205 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT; 2206 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT; 2207 2208 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++) 2209 { 2210 if (shaderBits & (1<<shaderType)) 2211 { 2212 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType))); 2213 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType))); 2214 } 2215 } 2216 } 2217 } 2218 } 2219 2220 } // Functional 2221 } // gles31 2222 } // deqp 2223