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