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