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 "vkQueryUtil.hpp" 29#include "gluContextInfo.hpp" 30#include "tcuTestLog.hpp" 31#include "tcuFormatUtil.hpp" 32#include "tcuFloat.hpp" 33#include "tcuInterval.hpp" 34#include "tcuFloatFormat.hpp" 35#include "tcuVectorUtil.hpp" 36#include "deRandom.hpp" 37#include "deMath.h" 38#include "deString.h" 39#include "deArrayUtil.hpp" 40#include "deSharedPtr.hpp" 41#include <algorithm> 42 43namespace vkt 44{ 45 46namespace shaderexecutor 47{ 48 49 50using std::vector; 51using std::string; 52using tcu::TestLog; 53 54using tcu::Vec2; 55using tcu::Vec3; 56using tcu::Vec4; 57using tcu::IVec2; 58using tcu::IVec3; 59using tcu::IVec4; 60 61namespace 62{ 63 64// Utilities 65 66template<typename T, int Size> 67struct VecArrayAccess 68{ 69public: 70 VecArrayAccess (const void* ptr) : m_array((tcu::Vector<T, Size>*)ptr) {} 71 ~VecArrayAccess (void) {} 72 73 const tcu::Vector<T, Size>& operator[] (size_t offset) const { return m_array[offset]; } 74 tcu::Vector<T, Size>& operator[] (size_t offset) { return m_array[offset]; } 75 76private: 77 tcu::Vector<T, Size>* m_array; 78}; 79 80template<typename T, int Size> 81static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0) 82{ 83 VecArrayAccess<T, Size> access(dst); 84 for (int ndx = 0; ndx < numValues; ndx++) 85 access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue); 86} 87 88template<typename T> 89static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0) 90{ 91 T* typedPtr = (T*)dst; 92 for (int ndx = 0; ndx < numValues; ndx++) 93 typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue); 94} 95 96inline deUint32 getUlpDiff (float a, float b) 97{ 98 const deUint32 aBits = tcu::Float32(a).bits(); 99 const deUint32 bBits = tcu::Float32(b).bits(); 100 return aBits > bBits ? aBits - bBits : bBits - aBits; 101} 102 103inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b) 104{ 105 if (tcu::Float32(a).isZero()) 106 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b); 107 else if (tcu::Float32(b).isZero()) 108 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat()); 109 else 110 return getUlpDiff(a, b); 111} 112 113inline deUint64 getMaxUlpDiffFromBits (int numAccurateBits, int numTotalBits) 114{ 115 const int numGarbageBits = numTotalBits-numAccurateBits; 116 const deUint64 mask = (1ull<<numGarbageBits)-1ull; 117 118 return mask; 119} 120 121static int getNumMantissaBits (glu::DataType type) 122{ 123 DE_ASSERT(glu::isDataTypeFloatOrVec(type) || glu::isDataTypeDoubleOrDVec(type)); 124 return (glu::isDataTypeFloatOrVec(type) ? 23 : 52); 125} 126 127static int getMinMantissaBits (glu::DataType type, glu::Precision precision) 128{ 129 if (glu::isDataTypeDoubleOrDVec(type)) 130 { 131 return tcu::Float64::MANTISSA_BITS; 132 } 133 134 // Float case. 135 const int bits[] = 136 { 137 7, // lowp 138 tcu::Float16::MANTISSA_BITS, // mediump 139 tcu::Float32::MANTISSA_BITS, // highp 140 }; 141 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST); 142 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits))); 143 return bits[precision]; 144} 145 146static int getExponentBits (glu::DataType type) 147{ 148 DE_ASSERT(glu::isDataTypeFloatOrVec(type) || glu::isDataTypeDoubleOrDVec(type)); 149 return (glu::isDataTypeFloatOrVec(type) ? static_cast<int>(tcu::Float32::EXPONENT_BITS) : static_cast<int>(tcu::Float64::EXPONENT_BITS)); 150} 151 152static deUint32 getExponentMask (int exponentBits) 153{ 154 DE_ASSERT(exponentBits > 0); 155 return ((1u<<exponentBits) - 1u); 156} 157 158static int getComponentByteSize (glu::DataType type) 159{ 160 const glu::DataType scalarType = glu::getDataTypeScalarType(type); 161 162 DE_ASSERT( scalarType == glu::TYPE_FLOAT || 163 scalarType == glu::TYPE_FLOAT16 || 164 scalarType == glu::TYPE_DOUBLE || 165 scalarType == glu::TYPE_INT || 166 scalarType == glu::TYPE_UINT || 167 scalarType == glu::TYPE_INT8 || 168 scalarType == glu::TYPE_UINT8 || 169 scalarType == glu::TYPE_INT16 || 170 scalarType == glu::TYPE_UINT16 || 171 scalarType == glu::TYPE_BOOL ); 172 173 switch (scalarType) 174 { 175 case glu::TYPE_INT8: 176 case glu::TYPE_UINT8: 177 return 1; 178 case glu::TYPE_INT16: 179 case glu::TYPE_UINT16: 180 case glu::TYPE_FLOAT16: 181 return 2; 182 case glu::TYPE_BOOL: 183 case glu::TYPE_INT: 184 case glu::TYPE_UINT: 185 case glu::TYPE_FLOAT: 186 return 4; 187 case glu::TYPE_DOUBLE: 188 return 8; 189 default: 190 DE_ASSERT(false); break; 191 } 192 // Unreachable. 193 return 0; 194} 195 196static vector<int> getScalarSizes (const vector<Symbol>& symbols) 197{ 198 vector<int> sizes(symbols.size()); 199 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx) 200 sizes[ndx] = symbols[ndx].varType.getScalarSize(); 201 return sizes; 202} 203 204static vector<int> getComponentByteSizes (const vector<Symbol>& symbols) 205{ 206 vector<int> sizes; 207 sizes.reserve(symbols.size()); 208 for (const auto& sym : symbols) 209 sizes.push_back(getComponentByteSize(sym.varType.getBasicType())); 210 return sizes; 211} 212 213static int computeTotalByteSize (const vector<Symbol>& symbols) 214{ 215 int totalSize = 0; 216 for (const auto& sym : symbols) 217 totalSize += getComponentByteSize(sym.varType.getBasicType()) * sym.varType.getScalarSize(); 218 return totalSize; 219} 220 221static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint8>& data, const int numValues) 222{ 223 vector<void*> pointers (symbols.size()); 224 int curScalarOffset = 0; 225 226 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx) 227 { 228 const Symbol& var = symbols[varNdx]; 229 const int scalarSize = var.varType.getScalarSize(); 230 const auto componentBytes = getComponentByteSize(var.varType.getBasicType()); 231 232 // Uses planar layout as input/output specs do not support strides. 233 pointers[varNdx] = &data[curScalarOffset]; 234 curScalarOffset += scalarSize*numValues*componentBytes; 235 } 236 237 DE_ASSERT(curScalarOffset == (int)data.size()); 238 239 return pointers; 240} 241 242void checkTypeSupport (Context& context, glu::DataType dataType) 243{ 244 if (glu::isDataTypeDoubleOrDVec(dataType)) 245 { 246 const auto& vki = context.getInstanceInterface(); 247 const auto physicalDevice = context.getPhysicalDevice(); 248 249 const auto features = vk::getPhysicalDeviceFeatures(vki, physicalDevice); 250 if (!features.shaderFloat64) 251 TCU_THROW(NotSupportedError, "64-bit floats not supported by the implementation"); 252 } 253} 254 255// \todo [2013-08-08 pyry] Make generic utility and move to glu? 256 257struct HexFloat 258{ 259 const float value; 260 HexFloat (const float value_) : value(value_) {} 261}; 262 263std::ostream& operator<< (std::ostream& str, const HexFloat& v) 264{ 265 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits()); 266} 267 268struct HexDouble 269{ 270 const double value; 271 HexDouble (const double value_) : value(value_) {} 272}; 273 274std::ostream& operator<< (std::ostream& str, const HexDouble& v) 275{ 276 return str << v.value << " / " << tcu::toHex(tcu::Float64(v.value).bits()); 277} 278 279struct HexBool 280{ 281 const deUint32 value; 282 HexBool (const deUint32 value_) : value(value_) {} 283}; 284 285std::ostream& operator<< (std::ostream& str, const HexBool& v) 286{ 287 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value); 288} 289 290struct VarValue 291{ 292 const glu::VarType& type; 293 const void* value; 294 295 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {} 296}; 297 298std::ostream& operator<< (std::ostream& str, const VarValue& varValue) 299{ 300 DE_ASSERT(varValue.type.isBasicType()); 301 302 const glu::DataType basicType = varValue.type.getBasicType(); 303 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType); 304 const int numComponents = glu::getDataTypeScalarSize(basicType); 305 306 if (numComponents > 1) 307 str << glu::getDataTypeName(basicType) << "("; 308 309 for (int compNdx = 0; compNdx < numComponents; compNdx++) 310 { 311 if (compNdx != 0) 312 str << ", "; 313 314 switch (scalarType) 315 { 316 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break; 317 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break; 318 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break; 319 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break; 320 case glu::TYPE_DOUBLE: str << HexDouble(((const double*)varValue.value)[compNdx]); break; 321 322 default: 323 DE_ASSERT(false); 324 } 325 } 326 327 if (numComponents > 1) 328 str << ")"; 329 330 return str; 331} 332 333static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision) 334{ 335 const bool isDouble = glu::isDataTypeDoubleOrDVec(baseType); 336 return string(glu::getDataTypeName(baseType)) + (isDouble ? "" : getPrecisionPostfix(precision)) + "_compute"; 337} 338 339template<class TestClass> 340static void addFunctionCases (tcu::TestCaseGroup* parent, const char* functionName, const std::vector<glu::DataType>& scalarTypes) 341{ 342 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName); 343 parent->addChild(group); 344 345 for (const auto scalarType : scalarTypes) 346 { 347 const bool isDouble = glu::isDataTypeDoubleOrDVec(scalarType); 348 const int lowestPrec = (isDouble ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP); 349 const int highestPrec = (isDouble ? glu::PRECISION_LAST : glu::PRECISION_HIGHP); 350 351 for (int vecSize = 1; vecSize <= 4; vecSize++) 352 { 353 for (int prec = lowestPrec; prec <= highestPrec; prec++) 354 { 355 group->addChild(new TestClass(parent->getTestContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec))); 356 } 357 } 358 } 359} 360 361// CommonFunctionCase 362 363class CommonFunctionCase : public TestCase 364{ 365public: 366 CommonFunctionCase (tcu::TestContext& testCtx, const char* name); 367 ~CommonFunctionCase (void); 368 virtual void initPrograms (vk::SourceCollections& programCollection) const 369 { 370 generateSources(glu::SHADERTYPE_COMPUTE, m_spec, programCollection); 371 } 372 373 virtual TestInstance* createInstance (Context& context) const = 0; 374 375protected: 376 CommonFunctionCase (const CommonFunctionCase&); 377 CommonFunctionCase& operator= (const CommonFunctionCase&); 378 379 ShaderSpec m_spec; 380 const int m_numValues; 381}; 382 383CommonFunctionCase::CommonFunctionCase (tcu::TestContext& testCtx, const char* name) 384 : TestCase (testCtx, name) 385 , m_numValues (100) 386{ 387} 388 389CommonFunctionCase::~CommonFunctionCase (void) 390{ 391} 392 393// CommonFunctionTestInstance 394 395class CommonFunctionTestInstance : public TestInstance 396{ 397public: 398 CommonFunctionTestInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name) 399 : TestInstance (context) 400 , m_spec (spec) 401 , m_numValues (numValues) 402 , m_name (name) 403 , m_executor (createExecutor(context, glu::SHADERTYPE_COMPUTE, spec)) 404 { 405 } 406 virtual tcu::TestStatus iterate (void); 407 408protected: 409 virtual void getInputValues (int numValues, void* const* values) const = 0; 410 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0; 411 412 const ShaderSpec m_spec; 413 const int m_numValues; 414 415 // \todo [2017-03-07 pyry] Hack used to generate seeds for test cases - get rid of this. 416 const char* m_name; 417 418 std::ostringstream m_failMsg; //!< Comparison failure help message. 419 420 de::UniquePtr<ShaderExecutor> m_executor; 421}; 422 423tcu::TestStatus CommonFunctionTestInstance::iterate (void) 424{ 425 const int numInputBytes = computeTotalByteSize(m_spec.inputs); 426 const int numOutputBytes = computeTotalByteSize(m_spec.outputs); 427 vector<deUint8> inputData (numInputBytes * m_numValues); 428 vector<deUint8> outputData (numOutputBytes * m_numValues); 429 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues); 430 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues); 431 432 // Initialize input data. 433 getInputValues(m_numValues, &inputPointers[0]); 434 435 // Execute shader. 436 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]); 437 438 // Compare results. 439 { 440 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs); 441 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs); 442 const vector<int> inCompByteSizes = getComponentByteSizes(m_spec.inputs); 443 const vector<int> outCompByteSizes = getComponentByteSizes(m_spec.outputs); 444 vector<void*> curInputPtr (inputPointers.size()); 445 vector<void*> curOutputPtr (outputPointers.size()); 446 int numFailed = 0; 447 tcu::TestContext& testCtx = m_context.getTestContext(); 448 449 for (int valNdx = 0; valNdx < m_numValues; valNdx++) 450 { 451 // Set up pointers for comparison. 452 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx) 453 curInputPtr[inNdx] = (deUint8*)inputPointers[inNdx] + inScalarSizes[inNdx]*inCompByteSizes[inNdx]*valNdx; 454 455 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx) 456 curOutputPtr[outNdx] = (deUint8*)outputPointers[outNdx] + outScalarSizes[outNdx]*outCompByteSizes[outNdx]*valNdx; 457 458 if (!compare(&curInputPtr[0], &curOutputPtr[0])) 459 { 460 // \todo [2013-08-08 pyry] We probably want to log reference value as well? 461 462 testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage; 463 464 testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage; 465 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++) 466 testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = " 467 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx]) 468 << TestLog::EndMessage; 469 470 testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage; 471 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++) 472 testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = " 473 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx]) 474 << TestLog::EndMessage; 475 476 m_failMsg.str(""); 477 m_failMsg.clear(); 478 numFailed += 1; 479 } 480 } 481 482 testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage; 483 484 if (numFailed == 0) 485 return tcu::TestStatus::pass("Pass"); 486 else 487 return tcu::TestStatus::fail("Result comparison failed"); 488 } 489} 490 491// Test cases 492 493class AbsCaseInstance : public CommonFunctionTestInstance 494{ 495public: 496 AbsCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name) 497 : CommonFunctionTestInstance (context, spec, numValues, name) 498 { 499 } 500 501 void getInputValues (int numValues, void* const* values) const 502 { 503 const IVec2 intRanges[] = 504 { 505 IVec2(-(1<<7)+1, (1<<7)-1), 506 IVec2(-(1<<15)+1, (1<<15)-1), 507 IVec2(0x80000001, 0x7fffffff) 508 }; 509 510 de::Random rnd (deStringHash(m_name) ^ 0x235facu); 511 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 512 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 513 const int scalarSize = glu::getDataTypeScalarSize(type); 514 515 DE_ASSERT(!glu::isDataTypeFloatOrVec(type)); 516 517 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize); 518 } 519 520 bool compare (const void* const* inputs, const void* const* outputs) 521 { 522 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 523 const int scalarSize = glu::getDataTypeScalarSize(type); 524 525 DE_ASSERT(!glu::isDataTypeFloatOrVec(type)); 526 527 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 528 { 529 const int in0 = ((const int*)inputs[0])[compNdx]; 530 const int out0 = ((const int*)outputs[0])[compNdx]; 531 const int ref0 = de::abs(in0); 532 533 if (out0 != ref0) 534 { 535 m_failMsg << "Expected [" << compNdx << "] = " << ref0; 536 return false; 537 } 538 } 539 540 return true; 541 } 542}; 543 544class AbsCase : public CommonFunctionCase 545{ 546public: 547 AbsCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision) 548 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str()) 549 { 550 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 551 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 552 m_spec.source = "out0 = abs(in0);"; 553 } 554 555 TestInstance* createInstance (Context& ctx) const 556 { 557 return new AbsCaseInstance(ctx, m_spec, m_numValues, getName()); 558 } 559}; 560 561class SignCaseInstance : public CommonFunctionTestInstance 562{ 563public: 564 SignCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name) 565 : CommonFunctionTestInstance (context, spec, numValues, name) 566 { 567 } 568 569 void getInputValues (int numValues, void* const* values) const 570 { 571 const IVec2 intRanges[] = 572 { 573 IVec2(-(1<<7), (1<<7)-1), 574 IVec2(-(1<<15), (1<<15)-1), 575 IVec2(0x80000000, 0x7fffffff) 576 }; 577 578 de::Random rnd (deStringHash(m_name) ^ 0x324u); 579 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 580 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 581 const int scalarSize = glu::getDataTypeScalarSize(type); 582 583 DE_ASSERT(!glu::isDataTypeFloatOrVec(type)); 584 585 std::fill((int*)values[0] + scalarSize*0, (int*)values[0] + scalarSize*1, +1); 586 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1); 587 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0); 588 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize); 589 } 590 591 bool compare (const void* const* inputs, const void* const* outputs) 592 { 593 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 594 const int scalarSize = glu::getDataTypeScalarSize(type); 595 596 DE_ASSERT(!glu::isDataTypeFloatOrVec(type)); 597 598 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 599 { 600 const int in0 = ((const int*)inputs[0])[compNdx]; 601 const int out0 = ((const int*)outputs[0])[compNdx]; 602 const int ref0 = in0 < 0 ? -1 : 603 in0 > 0 ? +1 : 0; 604 605 if (out0 != ref0) 606 { 607 m_failMsg << "Expected [" << compNdx << "] = " << ref0; 608 return false; 609 } 610 } 611 612 return true; 613 } 614}; 615 616class SignCase : public CommonFunctionCase 617{ 618public: 619 SignCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision) 620 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str()) 621 { 622 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 623 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 624 m_spec.source = "out0 = sign(in0);"; 625 } 626 627 TestInstance* createInstance (Context& ctx) const 628 { 629 return new SignCaseInstance(ctx, m_spec, m_numValues, getName()); 630 } 631}; 632 633static void infNanRandomFloats(int numValues, void* const* values, const char *name, const ShaderSpec& spec) 634{ 635 constexpr deUint64 kOne = 1; 636 de::Random rnd (deStringHash(name) ^ 0xc2a39fu); 637 const glu::DataType type = spec.inputs[0].varType.getBasicType(); 638 const glu::Precision precision = spec.inputs[0].varType.getPrecision(); 639 const int scalarSize = glu::getDataTypeScalarSize(type); 640 const int minMantissaBits = getMinMantissaBits(type, precision); 641 const int numMantissaBits = getNumMantissaBits(type); 642 const deUint64 mantissaMask = ~getMaxUlpDiffFromBits(minMantissaBits, numMantissaBits) & ((kOne<<numMantissaBits)-kOne); 643 const int exponentBits = getExponentBits(type); 644 const deUint32 exponentMask = getExponentMask(exponentBits); 645 const bool isDouble = glu::isDataTypeDoubleOrDVec(type); 646 const deUint64 exponentBias = (isDouble ? static_cast<deUint64>(tcu::Float64::EXPONENT_BIAS) : static_cast<deUint64>(tcu::Float32::EXPONENT_BIAS)); 647 648 int numInf = 0; 649 int numNan = 0; 650 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++) 651 { 652 // Roughly 25% chance of each of Inf and NaN 653 const bool isInf = rnd.getFloat() > 0.75f; 654 const bool isNan = !isInf && rnd.getFloat() > 0.66f; 655 const deUint64 m = rnd.getUint64() & mantissaMask; 656 const deUint64 e = static_cast<deUint64>(rnd.getUint32() & exponentMask); 657 const deUint64 sign = static_cast<deUint64>(rnd.getUint32() & 0x1u); 658 // Ensure the 'quiet' bit is set on NaNs (also ensures we don't generate inf by mistake) 659 const deUint64 mantissa = isInf ? 0 : (isNan ? ((kOne<<(numMantissaBits-1)) | m) : m); 660 const deUint64 exp = (isNan || isInf) ? exponentMask : std::min(e, exponentBias); 661 const deUint64 value = (sign << (numMantissaBits + exponentBits)) | (exp << numMantissaBits) | static_cast<deUint32>(mantissa); 662 if (isInf) numInf++; 663 if (isNan) numNan++; 664 665 if (isDouble) 666 { 667 DE_ASSERT(tcu::Float64(value).isInf() == isInf && tcu::Float64(value).isNaN() == isNan); 668 ((deUint64*)values[0])[valNdx] = value; 669 } 670 else 671 { 672 const auto value32 = static_cast<deUint32>(value); 673 DE_ASSERT(tcu::Float32(value32).isInf() == isInf && tcu::Float32(value32).isNaN() == isNan); 674 ((deUint32*)values[0])[valNdx] = value32; 675 } 676 } 677 // Check for minimal coverage of intended cases. 678 DE_ASSERT(0 < numInf); 679 DE_ASSERT(0 < numNan); 680 DE_ASSERT(numInf + numNan < numValues*scalarSize); 681 682 // Release build does not use them 683 DE_UNREF(numInf); 684 DE_UNREF(numNan); 685} 686 687class IsnanCaseInstance : public CommonFunctionTestInstance 688{ 689public: 690 IsnanCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name) 691 : CommonFunctionTestInstance (context, spec, numValues, name) 692 { 693 } 694 695 void getInputValues (int numValues, void* const* values) const 696 { 697 infNanRandomFloats(numValues, values, m_name, m_spec); 698 } 699 700 bool compare (const void* const* inputs, const void* const* outputs) 701 { 702 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 703 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 704 const int scalarSize = glu::getDataTypeScalarSize(type); 705 const bool isDouble = glu::isDataTypeDoubleOrDVec(type); 706 707 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 708 { 709 const bool out0 = reinterpret_cast<const deUint32*>(outputs[0])[compNdx] != 0; 710 bool ok; 711 bool ref; 712 713 if (isDouble) 714 { 715 const double in0 = reinterpret_cast<const double*>(inputs[0])[compNdx]; 716 ref = tcu::Float64(in0).isNaN(); 717 ok = (out0 == ref); 718 } 719 else 720 { 721 const float in0 = reinterpret_cast<const float*>(inputs[0])[compNdx]; 722 ref = tcu::Float32(in0).isNaN(); 723 724 // NaN support only required for highp. Otherwise just check for false positives. 725 if (precision == glu::PRECISION_HIGHP) 726 ok = (out0 == ref); 727 else 728 ok = ref || !out0; 729 } 730 731 if (!ok) 732 { 733 m_failMsg << "Expected [" << compNdx << "] = " << (ref ? "true" : "false"); 734 return false; 735 } 736 } 737 738 return true; 739 } 740}; 741 742class IsnanCase : public CommonFunctionCase 743{ 744public: 745 IsnanCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision) 746 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str()) 747 { 748 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType) || glu::isDataTypeDoubleOrDVec(baseType)); 749 750 const int vecSize = glu::getDataTypeScalarSize(baseType); 751 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL; 752 753 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 754 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST))); 755 m_spec.source = "out0 = isnan(in0);"; 756 } 757 758 void checkSupport (Context& context) const 759 { 760 checkTypeSupport(context, m_spec.inputs[0].varType.getBasicType()); 761 } 762 763 TestInstance* createInstance (Context& ctx) const 764 { 765 return new IsnanCaseInstance(ctx, m_spec, m_numValues, getName()); 766 } 767}; 768 769class IsinfCaseInstance : public CommonFunctionTestInstance 770{ 771public: 772 IsinfCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name) 773 : CommonFunctionTestInstance(context, spec, numValues, name) 774 { 775 } 776 777 void getInputValues (int numValues, void* const* values) const 778 { 779 infNanRandomFloats(numValues, values, m_name, m_spec); 780 } 781 782 bool compare (const void* const* inputs, const void* const* outputs) 783 { 784 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 785 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 786 const int scalarSize = glu::getDataTypeScalarSize(type); 787 const bool isDouble = glu::isDataTypeDoubleOrDVec(type); 788 789 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 790 { 791 const bool out0 = reinterpret_cast<const deUint32*>(outputs[0])[compNdx] != 0; 792 bool ref; 793 bool ok; 794 795 if (isDouble) 796 { 797 const double in0 = reinterpret_cast<const double*>(inputs[0])[compNdx]; 798 ref = tcu::Float64(in0).isInf(); 799 ok = (out0 == ref); 800 } 801 else 802 { 803 const float in0 = reinterpret_cast<const float*>(inputs[0])[compNdx]; 804 if (precision == glu::PRECISION_HIGHP) 805 { 806 // Only highp is required to support inf/nan 807 ref = tcu::Float32(in0).isInf(); 808 ok = (out0 == ref); 809 } 810 else 811 { 812 // Inf support is optional, check that inputs that are not Inf in mediump don't result in true. 813 ref = tcu::Float16(in0).isInf(); 814 ok = (out0 || !ref); 815 } 816 } 817 818 if (!ok) 819 { 820 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref); 821 return false; 822 } 823 } 824 825 return true; 826 } 827}; 828 829class IsinfCase : public CommonFunctionCase 830{ 831public: 832 IsinfCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision) 833 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str()) 834 { 835 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType) || glu::isDataTypeDoubleOrDVec(baseType)); 836 837 const int vecSize = glu::getDataTypeScalarSize(baseType); 838 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL; 839 840 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 841 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST))); 842 m_spec.source = "out0 = isinf(in0);"; 843 } 844 845 void checkSupport (Context& context) const 846 { 847 checkTypeSupport(context, m_spec.inputs[0].varType.getBasicType()); 848 } 849 850 TestInstance* createInstance (Context& ctx) const 851 { 852 return new IsinfCaseInstance(ctx, m_spec, m_numValues, getName()); 853 } 854}; 855 856class FloatBitsToUintIntCaseInstance : public CommonFunctionTestInstance 857{ 858public: 859 FloatBitsToUintIntCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name) 860 : CommonFunctionTestInstance (context, spec, numValues, name) 861 { 862 } 863 864 void getInputValues (int numValues, void* const* values) const 865 { 866 const Vec2 ranges[] = 867 { 868 Vec2(-2.0f, 2.0f), // lowp 869 Vec2(-1e3f, 1e3f), // mediump 870 Vec2(-1e7f, 1e7f) // highp 871 }; 872 873 de::Random rnd (deStringHash(m_name) ^ 0x2790au); 874 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 875 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 876 const int scalarSize = glu::getDataTypeScalarSize(type); 877 878 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize); 879 } 880 881 bool compare (const void* const* inputs, const void* const* outputs) 882 { 883 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 884 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 885 const int scalarSize = glu::getDataTypeScalarSize(type); 886 887 const int minMantissaBits = getMinMantissaBits(type, precision); 888 const int numMantissaBits = getNumMantissaBits(type); 889 const int maxUlpDiff = static_cast<int>(getMaxUlpDiffFromBits(minMantissaBits, numMantissaBits)); 890 891 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 892 { 893 const float in0 = ((const float*)inputs[0])[compNdx]; 894 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx]; 895 const deUint32 refOut0 = tcu::Float32(in0).bits(); 896 const int ulpDiff = de::abs((int)out0 - (int)refOut0); 897 898 if (ulpDiff > maxUlpDiff) 899 { 900 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold " 901 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff); 902 return false; 903 } 904 } 905 906 return true; 907 } 908}; 909 910class FloatBitsToUintIntCase : public CommonFunctionCase 911{ 912public: 913 FloatBitsToUintIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision, bool outIsSigned) 914 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, precision).c_str()) 915 { 916 const int vecSize = glu::getDataTypeScalarSize(baseType); 917 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT) 918 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT); 919 920 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 921 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP))); 922 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);"; 923 } 924 925 TestInstance* createInstance (Context& ctx) const 926 { 927 return new FloatBitsToUintIntCaseInstance(ctx, m_spec, m_numValues, getName()); 928 } 929}; 930 931class FloatBitsToIntCase : public FloatBitsToUintIntCase 932{ 933public: 934 FloatBitsToIntCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision) 935 : FloatBitsToUintIntCase (testCtx, baseType, precision, true) 936 { 937 } 938 939}; 940 941class FloatBitsToUintCase : public FloatBitsToUintIntCase 942{ 943public: 944 FloatBitsToUintCase (tcu::TestContext& testCtx, glu::DataType baseType, glu::Precision precision) 945 : FloatBitsToUintIntCase (testCtx, baseType, precision, false) 946 { 947 } 948}; 949 950class BitsToFloatCaseInstance : public CommonFunctionTestInstance 951{ 952public: 953 BitsToFloatCaseInstance (Context& context, const ShaderSpec& spec, int numValues, const char* name) 954 : CommonFunctionTestInstance (context, spec, numValues, name) 955 { 956 } 957 958 void getInputValues (int numValues, void* const* values) const 959 { 960 de::Random rnd (deStringHash(m_name) ^ 0xbbb225u); 961 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 962 const int scalarSize = glu::getDataTypeScalarSize(type); 963 const Vec2 range (-1e8f, +1e8f); 964 965 // \note Filled as floats. 966 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize); 967 } 968 969 bool compare (const void* const* inputs, const void* const* outputs) 970 { 971 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 972 const int scalarSize = glu::getDataTypeScalarSize(type); 973 const deUint32 maxUlpDiff = 0; 974 975 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 976 { 977 const float in0 = ((const float*)inputs[0])[compNdx]; 978 const float out0 = ((const float*)outputs[0])[compNdx]; 979 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(in0, out0); 980 981 if (ulpDiff > maxUlpDiff) 982 { 983 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold " 984 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff); 985 return false; 986 } 987 } 988 989 return true; 990 } 991}; 992 993class BitsToFloatCase : public CommonFunctionCase 994{ 995public: 996 BitsToFloatCase (tcu::TestContext& testCtx, glu::DataType baseType) 997 : CommonFunctionCase (testCtx, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP).c_str()) 998 { 999 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType); 1000 const int vecSize = glu::getDataTypeScalarSize(baseType); 1001 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT; 1002 1003 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP))); 1004 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP))); 1005 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);"; 1006 } 1007 1008 TestInstance* createInstance (Context& ctx) const 1009 { 1010 return new BitsToFloatCaseInstance(ctx, m_spec, m_numValues, getName()); 1011 } 1012}; 1013 1014} // anonymous 1015 1016ShaderCommonFunctionTests::ShaderCommonFunctionTests (tcu::TestContext& testCtx) 1017 : tcu::TestCaseGroup (testCtx, "common") 1018{ 1019} 1020 1021ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void) 1022{ 1023} 1024 1025void ShaderCommonFunctionTests::init (void) 1026{ 1027 static const std::vector<glu::DataType> kIntOnly (1u, glu::TYPE_INT); 1028 static const std::vector<glu::DataType> kFloatOnly (1u, glu::TYPE_FLOAT); 1029 static const std::vector<glu::DataType> kFloatAndDouble {glu::TYPE_FLOAT, glu::TYPE_DOUBLE}; 1030 1031 addFunctionCases<AbsCase> (this, "abs", kIntOnly); 1032 addFunctionCases<SignCase> (this, "sign", kIntOnly); 1033 addFunctionCases<IsnanCase> (this, "isnan", kFloatAndDouble); 1034 addFunctionCases<IsinfCase> (this, "isinf", kFloatAndDouble); 1035 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", kFloatOnly); 1036 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", kFloatOnly); 1037 1038 // (u)intBitsToFloat() 1039 { 1040 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat"); 1041 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat"); 1042 1043 addChild(intGroup); 1044 addChild(uintGroup); 1045 1046 for (int vecSize = 1; vecSize < 4; vecSize++) 1047 { 1048 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT; 1049 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT; 1050 1051 intGroup->addChild(new BitsToFloatCase(getTestContext(), intType)); 1052 uintGroup->addChild(new BitsToFloatCase(getTestContext(), uintType)); 1053 } 1054 } 1055} 1056 1057} // shaderexecutor 1058} // vkt 1059