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