1/*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.0 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 "es3fShaderCommonFunctionTests.hpp" 25#include "glsShaderExecUtil.hpp" 26#include "tcuTestLog.hpp" 27#include "tcuFormatUtil.hpp" 28#include "tcuVectorUtil.hpp" 29#include "tcuFloat.hpp" 30#include "deRandom.hpp" 31#include "deMath.h" 32#include "deString.h" 33 34namespace deqp 35{ 36namespace gles3 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, int Size> 70static void fillRandomVectors (de::Random& rnd, const tcu::Vector<T, Size>& minValue, const tcu::Vector<T, Size>& maxValue, void* dst, int numValues, int offset = 0) 71{ 72 VecArrayAccess<T, Size> access(dst); 73 for (int ndx = 0; ndx < numValues; ndx++) 74 access[offset + ndx] = tcu::randomVector<T, Size>(rnd, minValue, maxValue); 75} 76 77template<typename T> 78static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0) 79{ 80 T* typedPtr = (T*)dst; 81 for (int ndx = 0; ndx < numValues; ndx++) 82 typedPtr[offset + ndx] = de::randomScalar<T>(rnd, minValue, maxValue); 83} 84 85inline int numBitsLostInOp (float input, float output) 86{ 87 const int inExp = tcu::Float32(input).exponent(); 88 const int outExp = tcu::Float32(output).exponent(); 89 90 return de::max(0, inExp-outExp); // Lost due to mantissa shift. 91} 92 93inline deUint32 getUlpDiff (float a, float b) 94{ 95 const deUint32 aBits = tcu::Float32(a).bits(); 96 const deUint32 bBits = tcu::Float32(b).bits(); 97 return aBits > bBits ? aBits - bBits : bBits - aBits; 98} 99 100inline deUint32 getUlpDiffIgnoreZeroSign (float a, float b) 101{ 102 if (tcu::Float32(a).isZero()) 103 return getUlpDiff(tcu::Float32::construct(tcu::Float32(b).sign(), 0, 0).asFloat(), b); 104 else if (tcu::Float32(b).isZero()) 105 return getUlpDiff(a, tcu::Float32::construct(tcu::Float32(a).sign(), 0, 0).asFloat()); 106 else 107 return getUlpDiff(a, b); 108} 109 110inline bool supportsSignedZero (glu::Precision precision) 111{ 112 // \note GLSL ES 3.0 doesn't really require support for -0, but we require it for highp 113 // as it is very widely supported. 114 return precision == glu::PRECISION_HIGHP; 115} 116 117inline float getEpsFromMaxUlpDiff (float value, deUint32 ulpDiff) 118{ 119 const int exp = tcu::Float32(value).exponent(); 120 return tcu::Float32::construct(+1, exp, (1u<<23) | ulpDiff).asFloat() - tcu::Float32::construct(+1, exp, 1u<<23).asFloat(); 121} 122 123inline deUint32 getMaxUlpDiffFromBits (int numAccurateBits) 124{ 125 const int numGarbageBits = 23-numAccurateBits; 126 const deUint32 mask = (1u<<numGarbageBits)-1u; 127 128 return mask; 129} 130 131inline float getEpsFromBits (float value, int numAccurateBits) 132{ 133 return getEpsFromMaxUlpDiff(value, getMaxUlpDiffFromBits(numAccurateBits)); 134} 135 136static int getMinMantissaBits (glu::Precision precision) 137{ 138 const int bits[] = 139 { 140 7, // lowp 141 10, // mediump 142 23 // highp 143 }; 144 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(bits) == glu::PRECISION_LAST); 145 DE_ASSERT(de::inBounds<int>(precision, 0, DE_LENGTH_OF_ARRAY(bits))); 146 return bits[precision]; 147} 148 149// CommonFunctionCase 150 151class CommonFunctionCase : public TestCase 152{ 153public: 154 CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType); 155 ~CommonFunctionCase (void); 156 157 void init (void); 158 void deinit (void); 159 IterateResult iterate (void); 160 161protected: 162 CommonFunctionCase (const CommonFunctionCase& other); 163 CommonFunctionCase& operator= (const CommonFunctionCase& other); 164 165 virtual void getInputValues (int numValues, void* const* values) const = 0; 166 virtual bool compare (const void* const* inputs, const void* const* outputs) = 0; 167 168 glu::ShaderType m_shaderType; 169 ShaderSpec m_spec; 170 int m_numValues; 171 172 std::ostringstream m_failMsg; //!< Comparison failure help message. 173 174private: 175 ShaderExecutor* m_executor; 176}; 177 178CommonFunctionCase::CommonFunctionCase (Context& context, const char* name, const char* description, glu::ShaderType shaderType) 179 : TestCase (context, name, description) 180 , m_shaderType (shaderType) 181 , m_numValues (100) 182 , m_executor (DE_NULL) 183{ 184 m_spec.version = glu::GLSL_VERSION_300_ES; 185} 186 187CommonFunctionCase::~CommonFunctionCase (void) 188{ 189 CommonFunctionCase::deinit(); 190} 191 192void CommonFunctionCase::init (void) 193{ 194 DE_ASSERT(!m_executor); 195 196 m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec); 197 m_testCtx.getLog() << m_executor; 198 199 if (!m_executor->isOk()) 200 throw tcu::TestError("Compile failed"); 201} 202 203void CommonFunctionCase::deinit (void) 204{ 205 delete m_executor; 206 m_executor = DE_NULL; 207} 208 209static vector<int> getScalarSizes (const vector<Symbol>& symbols) 210{ 211 vector<int> sizes(symbols.size()); 212 for (int ndx = 0; ndx < (int)symbols.size(); ++ndx) 213 sizes[ndx] = symbols[ndx].varType.getScalarSize(); 214 return sizes; 215} 216 217static int computeTotalScalarSize (const vector<Symbol>& symbols) 218{ 219 int totalSize = 0; 220 for (vector<Symbol>::const_iterator sym = symbols.begin(); sym != symbols.end(); ++sym) 221 totalSize += sym->varType.getScalarSize(); 222 return totalSize; 223} 224 225static vector<void*> getInputOutputPointers (const vector<Symbol>& symbols, vector<deUint32>& data, const int numValues) 226{ 227 vector<void*> pointers (symbols.size()); 228 int curScalarOffset = 0; 229 230 for (int varNdx = 0; varNdx < (int)symbols.size(); ++varNdx) 231 { 232 const Symbol& var = symbols[varNdx]; 233 const int scalarSize = var.varType.getScalarSize(); 234 235 // Uses planar layout as input/output specs do not support strides. 236 pointers[varNdx] = &data[curScalarOffset]; 237 curScalarOffset += scalarSize*numValues; 238 } 239 240 DE_ASSERT(curScalarOffset == (int)data.size()); 241 242 return pointers; 243} 244 245// \todo [2013-08-08 pyry] Make generic utility and move to glu? 246 247struct HexFloat 248{ 249 const float value; 250 HexFloat (const float value_) : value(value_) {} 251}; 252 253std::ostream& operator<< (std::ostream& str, const HexFloat& v) 254{ 255 return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits()); 256} 257 258struct HexBool 259{ 260 const deUint32 value; 261 HexBool (const deUint32 value_) : value(value_) {} 262}; 263 264std::ostream& operator<< (std::ostream& str, const HexBool& v) 265{ 266 return str << (v.value ? "true" : "false") << " / " << tcu::toHex(v.value); 267} 268 269struct VarValue 270{ 271 const glu::VarType& type; 272 const void* value; 273 274 VarValue (const glu::VarType& type_, const void* value_) : type(type_), value(value_) {} 275}; 276 277std::ostream& operator<< (std::ostream& str, const VarValue& varValue) 278{ 279 DE_ASSERT(varValue.type.isBasicType()); 280 281 const glu::DataType basicType = varValue.type.getBasicType(); 282 const glu::DataType scalarType = glu::getDataTypeScalarType(basicType); 283 const int numComponents = glu::getDataTypeScalarSize(basicType); 284 285 if (numComponents > 1) 286 str << glu::getDataTypeName(basicType) << "("; 287 288 for (int compNdx = 0; compNdx < numComponents; compNdx++) 289 { 290 if (compNdx != 0) 291 str << ", "; 292 293 switch (scalarType) 294 { 295 case glu::TYPE_FLOAT: str << HexFloat(((const float*)varValue.value)[compNdx]); break; 296 case glu::TYPE_INT: str << ((const deInt32*)varValue.value)[compNdx]; break; 297 case glu::TYPE_UINT: str << tcu::toHex(((const deUint32*)varValue.value)[compNdx]); break; 298 case glu::TYPE_BOOL: str << HexBool(((const deUint32*)varValue.value)[compNdx]); break; 299 300 default: 301 DE_ASSERT(false); 302 } 303 } 304 305 if (numComponents > 1) 306 str << ")"; 307 308 return str; 309} 310 311CommonFunctionCase::IterateResult CommonFunctionCase::iterate (void) 312{ 313 const int numInputScalars = computeTotalScalarSize(m_spec.inputs); 314 const int numOutputScalars = computeTotalScalarSize(m_spec.outputs); 315 vector<deUint32> inputData (numInputScalars * m_numValues); 316 vector<deUint32> outputData (numOutputScalars * m_numValues); 317 const vector<void*> inputPointers = getInputOutputPointers(m_spec.inputs, inputData, m_numValues); 318 const vector<void*> outputPointers = getInputOutputPointers(m_spec.outputs, outputData, m_numValues); 319 320 // Initialize input data. 321 getInputValues(m_numValues, &inputPointers[0]); 322 323 // Execute shader. 324 m_executor->useProgram(); 325 m_executor->execute(m_numValues, &inputPointers[0], &outputPointers[0]); 326 327 // Compare results. 328 { 329 const vector<int> inScalarSizes = getScalarSizes(m_spec.inputs); 330 const vector<int> outScalarSizes = getScalarSizes(m_spec.outputs); 331 vector<void*> curInputPtr (inputPointers.size()); 332 vector<void*> curOutputPtr (outputPointers.size()); 333 int numFailed = 0; 334 335 for (int valNdx = 0; valNdx < m_numValues; valNdx++) 336 { 337 // Set up pointers for comparison. 338 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); ++inNdx) 339 curInputPtr[inNdx] = (deUint32*)inputPointers[inNdx] + inScalarSizes[inNdx]*valNdx; 340 341 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); ++outNdx) 342 curOutputPtr[outNdx] = (deUint32*)outputPointers[outNdx] + outScalarSizes[outNdx]*valNdx; 343 344 if (!compare(&curInputPtr[0], &curOutputPtr[0])) 345 { 346 // \todo [2013-08-08 pyry] We probably want to log reference value as well? 347 348 m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed for value " << valNdx << ":\n " << m_failMsg.str() << TestLog::EndMessage; 349 350 m_testCtx.getLog() << TestLog::Message << " inputs:" << TestLog::EndMessage; 351 for (int inNdx = 0; inNdx < (int)curInputPtr.size(); inNdx++) 352 m_testCtx.getLog() << TestLog::Message << " " << m_spec.inputs[inNdx].name << " = " 353 << VarValue(m_spec.inputs[inNdx].varType, curInputPtr[inNdx]) 354 << TestLog::EndMessage; 355 356 m_testCtx.getLog() << TestLog::Message << " outputs:" << TestLog::EndMessage; 357 for (int outNdx = 0; outNdx < (int)curOutputPtr.size(); outNdx++) 358 m_testCtx.getLog() << TestLog::Message << " " << m_spec.outputs[outNdx].name << " = " 359 << VarValue(m_spec.outputs[outNdx].varType, curOutputPtr[outNdx]) 360 << TestLog::EndMessage; 361 362 m_failMsg.str(""); 363 m_failMsg.clear(); 364 numFailed += 1; 365 } 366 } 367 368 m_testCtx.getLog() << TestLog::Message << (m_numValues - numFailed) << " / " << m_numValues << " values passed" << TestLog::EndMessage; 369 370 m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, 371 numFailed == 0 ? "Pass" : "Result comparison failed"); 372 } 373 374 return STOP; 375} 376 377static std::string getCommonFuncCaseName (glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 378{ 379 return string(glu::getDataTypeName(baseType)) + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType); 380} 381 382class AbsCase : public CommonFunctionCase 383{ 384public: 385 AbsCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 386 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "abs", shaderType) 387 { 388 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 389 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 390 m_spec.source = "out0 = abs(in0);"; 391 } 392 393 void getInputValues (int numValues, void* const* values) const 394 { 395 const Vec2 floatRanges[] = 396 { 397 Vec2(-2.0f, 2.0f), // lowp 398 Vec2(-1e3f, 1e3f), // mediump 399 Vec2(-1e7f, 1e7f) // highp 400 }; 401 const IVec2 intRanges[] = 402 { 403 IVec2(-(1<<7)+1, (1<<7)-1), 404 IVec2(-(1<<15)+1, (1<<15)-1), 405 IVec2(0x80000001, 0x7fffffff) 406 }; 407 408 de::Random rnd (deStringHash(getName()) ^ 0x235facu); 409 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 410 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 411 const int scalarSize = glu::getDataTypeScalarSize(type); 412 413 if (glu::isDataTypeFloatOrVec(type)) 414 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), values[0], numValues*scalarSize); 415 else 416 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), values[0], numValues*scalarSize); 417 } 418 419 bool compare (const void* const* inputs, const void* const* outputs) 420 { 421 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 422 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 423 const int scalarSize = glu::getDataTypeScalarSize(type); 424 425 if (glu::isDataTypeFloatOrVec(type)) 426 { 427 const int mantissaBits = getMinMantissaBits(precision); 428 const deUint32 maxUlpDiff = (1u<<(23-mantissaBits))-1u; 429 430 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 431 { 432 const float in0 = ((const float*)inputs[0])[compNdx]; 433 const float out0 = ((const float*)outputs[0])[compNdx]; 434 const float ref0 = de::abs(in0); 435 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0); 436 437 if (ulpDiff0 > maxUlpDiff) 438 { 439 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0; 440 return false; 441 } 442 } 443 } 444 else 445 { 446 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 447 { 448 const int in0 = ((const int*)inputs[0])[compNdx]; 449 const int out0 = ((const int*)outputs[0])[compNdx]; 450 const int ref0 = de::abs(in0); 451 452 if (out0 != ref0) 453 { 454 m_failMsg << "Expected [" << compNdx << "] = " << ref0; 455 return false; 456 } 457 } 458 } 459 460 return true; 461 } 462}; 463 464class SignCase : public CommonFunctionCase 465{ 466public: 467 SignCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 468 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "sign", shaderType) 469 { 470 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 471 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 472 m_spec.source = "out0 = sign(in0);"; 473 } 474 475 void getInputValues (int numValues, void* const* values) const 476 { 477 const Vec2 floatRanges[] = 478 { 479 Vec2(-2.0f, 2.0f), // lowp 480 Vec2(-1e4f, 1e4f), // mediump - note: may end up as inf 481 Vec2(-1e8f, 1e8f) // highp - note: may end up as inf 482 }; 483 const IVec2 intRanges[] = 484 { 485 IVec2(-(1<<7), (1<<7)-1), 486 IVec2(-(1<<15), (1<<15)-1), 487 IVec2(0x80000000, 0x7fffffff) 488 }; 489 490 de::Random rnd (deStringHash(getName()) ^ 0x324u); 491 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 492 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 493 const int scalarSize = glu::getDataTypeScalarSize(type); 494 495 if (glu::isDataTypeFloatOrVec(type)) 496 { 497 // Special cases. 498 std::fill((float*)values[0], (float*)values[0] + scalarSize, +1.0f); 499 std::fill((float*)values[0] + scalarSize*1, (float*)values[0] + scalarSize*2, -1.0f); 500 std::fill((float*)values[0] + scalarSize*2, (float*)values[0] + scalarSize*3, 0.0f); 501 fillRandomScalars(rnd, floatRanges[precision].x(), floatRanges[precision].y(), (float*)values[0] + scalarSize*3, (numValues-3)*scalarSize); 502 } 503 else 504 { 505 std::fill((int*)values[0], (int*)values[0] + scalarSize, +1); 506 std::fill((int*)values[0] + scalarSize*1, (int*)values[0] + scalarSize*2, -1); 507 std::fill((int*)values[0] + scalarSize*2, (int*)values[0] + scalarSize*3, 0); 508 fillRandomScalars(rnd, intRanges[precision].x(), intRanges[precision].y(), (int*)values[0] + scalarSize*3, (numValues-3)*scalarSize); 509 } 510 } 511 512 bool compare (const void* const* inputs, const void* const* outputs) 513 { 514 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 515 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 516 const int scalarSize = glu::getDataTypeScalarSize(type); 517 518 if (glu::isDataTypeFloatOrVec(type)) 519 { 520 // Both highp and mediump should be able to represent -1, 0, and +1 exactly 521 const deUint32 maxUlpDiff = precision == glu::PRECISION_LOWP ? getMaxUlpDiffFromBits(getMinMantissaBits(precision)) : 0; 522 523 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 524 { 525 const float in0 = ((const float*)inputs[0])[compNdx]; 526 const float out0 = ((const float*)outputs[0])[compNdx]; 527 const float ref0 = in0 < 0.0f ? -1.0f : 528 in0 > 0.0f ? +1.0f : 0.0f; 529 const deUint32 ulpDiff0 = getUlpDiff(out0, ref0); 530 531 if (ulpDiff0 > maxUlpDiff) 532 { 533 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " with ULP threshold " << maxUlpDiff << ", got ULP diff " << ulpDiff0; 534 return false; 535 } 536 } 537 } 538 else 539 { 540 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 541 { 542 const int in0 = ((const int*)inputs[0])[compNdx]; 543 const int out0 = ((const int*)outputs[0])[compNdx]; 544 const int ref0 = in0 < 0 ? -1 : 545 in0 > 0 ? +1 : 0; 546 547 if (out0 != ref0) 548 { 549 m_failMsg << "Expected [" << compNdx << "] = " << ref0; 550 return false; 551 } 552 } 553 } 554 555 return true; 556 } 557}; 558 559static float roundEven (float v) 560{ 561 const float q = deFloatFrac(v); 562 const int truncated = int(v-q); 563 const int rounded = (q > 0.5f) ? (truncated + 1) : // Rounded up 564 (q == 0.5f && (truncated % 2 != 0)) ? (truncated + 1) : // Round to nearest even at 0.5 565 truncated; // Rounded down 566 567 return float(rounded); 568} 569 570class RoundEvenCase : public CommonFunctionCase 571{ 572public: 573 RoundEvenCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 574 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "roundEven", shaderType) 575 { 576 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 577 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 578 m_spec.source = "out0 = roundEven(in0);"; 579 } 580 581 void getInputValues (int numValues, void* const* values) const 582 { 583 const Vec2 ranges[] = 584 { 585 Vec2(-2.0f, 2.0f), // lowp 586 Vec2(-1e3f, 1e3f), // mediump 587 Vec2(-1e7f, 1e7f) // highp 588 }; 589 590 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 591 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 592 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 593 const int scalarSize = glu::getDataTypeScalarSize(type); 594 int numSpecialCases = 0; 595 596 // Special cases. 597 if (precision != glu::PRECISION_LOWP) 598 { 599 DE_ASSERT(numValues >= 20); 600 for (int ndx = 0; ndx < 20; ndx++) 601 { 602 const float v = de::clamp(float(ndx) - 10.5f, ranges[precision].x(), ranges[precision].y()); 603 std::fill((float*)values[0], (float*)values[0] + scalarSize, v); 604 numSpecialCases += 1; 605 } 606 } 607 608 // Random cases. 609 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize); 610 611 // If precision is mediump, make sure values can be represented in fp16 exactly 612 if (precision == glu::PRECISION_MEDIUMP) 613 { 614 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 615 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 616 } 617 } 618 619 bool compare (const void* const* inputs, const void* const* outputs) 620 { 621 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 622 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 623 const bool hasSignedZero = supportsSignedZero(precision); 624 const int scalarSize = glu::getDataTypeScalarSize(type); 625 626 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 627 { 628 // Require exact rounding result. 629 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 630 { 631 const float in0 = ((const float*)inputs[0])[compNdx]; 632 const float out0 = ((const float*)outputs[0])[compNdx]; 633 const float ref = roundEven(in0); 634 635 const deUint32 ulpDiff = hasSignedZero ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 636 637 if (ulpDiff > 0) 638 { 639 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 640 return false; 641 } 642 } 643 } 644 else 645 { 646 const int mantissaBits = getMinMantissaBits(precision); 647 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 648 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 649 650 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 651 { 652 const float in0 = ((const float*)inputs[0])[compNdx]; 653 const float out0 = ((const float*)outputs[0])[compNdx]; 654 const int minRes = int(roundEven(in0-eps)); 655 const int maxRes = int(roundEven(in0+eps)); 656 bool anyOk = false; 657 658 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 659 { 660 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 661 662 if (ulpDiff <= maxUlpDiff) 663 { 664 anyOk = true; 665 break; 666 } 667 } 668 669 if (!anyOk) 670 { 671 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 672 return false; 673 } 674 } 675 } 676 677 return true; 678 } 679}; 680 681class ModfCase : public CommonFunctionCase 682{ 683public: 684 ModfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 685 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "modf", shaderType) 686 { 687 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 688 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 689 m_spec.outputs.push_back(Symbol("out1", glu::VarType(baseType, precision))); 690 m_spec.source = "out0 = modf(in0, out1);"; 691 } 692 693 void getInputValues (int numValues, void* const* values) const 694 { 695 const Vec2 ranges[] = 696 { 697 Vec2(-2.0f, 2.0f), // lowp 698 Vec2(-1e3f, 1e3f), // mediump 699 Vec2(-1e7f, 1e7f) // highp 700 }; 701 702 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 703 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 704 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 705 const int scalarSize = glu::getDataTypeScalarSize(type); 706 707 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize); 708 } 709 710 bool compare (const void* const* inputs, const void* const* outputs) 711 { 712 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 713 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 714 const bool hasZeroSign = supportsSignedZero(precision); 715 const int scalarSize = glu::getDataTypeScalarSize(type); 716 717 const int mantissaBits = getMinMantissaBits(precision); 718 719 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 720 { 721 const float in0 = ((const float*)inputs[0])[compNdx]; 722 const float out0 = ((const float*)outputs[0])[compNdx]; 723 const float out1 = ((const float*)outputs[1])[compNdx]; 724 725 const float refOut1 = float(int(in0)); 726 const float refOut0 = in0 - refOut1; 727 728 const int bitsLost = precision != glu::PRECISION_HIGHP ? numBitsLostInOp(in0, refOut0) : 0; 729 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(mantissaBits - bitsLost, 0)); 730 731 const float resSum = out0 + out1; 732 733 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(resSum, in0) : getUlpDiffIgnoreZeroSign(resSum, in0); 734 735 if (ulpDiff > maxUlpDiff) 736 { 737 m_failMsg << "Expected [" << compNdx << "] = (" << HexFloat(refOut0) << ") + (" << HexFloat(refOut1) << ") = " << HexFloat(in0) << " with ULP threshold " 738 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff); 739 return false; 740 } 741 } 742 743 return true; 744 } 745}; 746 747class IsnanCase : public CommonFunctionCase 748{ 749public: 750 IsnanCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 751 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isnan", shaderType) 752 { 753 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType)); 754 755 const int vecSize = glu::getDataTypeScalarSize(baseType); 756 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL; 757 758 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 759 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST))); 760 m_spec.source = "out0 = isnan(in0);"; 761 } 762 763 void getInputValues (int numValues, void* const* values) const 764 { 765 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu); 766 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 767 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 768 const int scalarSize = glu::getDataTypeScalarSize(type); 769 const int mantissaBits = getMinMantissaBits(precision); 770 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u); 771 772 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++) 773 { 774 const bool isNan = rnd.getFloat() > 0.3f; 775 const bool isInf = !isNan && rnd.getFloat() > 0.4f; 776 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0; 777 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu; 778 const deUint32 sign = rnd.getUint32() & 0x1u; 779 const deUint32 value = (sign << 31) | (exp << 23) | mantissa; 780 781 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan); 782 783 ((deUint32*)values[0])[valNdx] = value; 784 } 785 } 786 787 bool compare (const void* const* inputs, const void* const* outputs) 788 { 789 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 790 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 791 const int scalarSize = glu::getDataTypeScalarSize(type); 792 793 if (precision == glu::PRECISION_HIGHP) 794 { 795 // Only highp is required to support inf/nan 796 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 797 { 798 const float in0 = ((const float*)inputs[0])[compNdx]; 799 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx]; 800 const deUint32 ref = tcu::Float32(in0).isNaN() ? 1u : 0u; 801 802 if (out0 != ref) 803 { 804 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref); 805 return false; 806 } 807 } 808 } 809 else 810 { 811 // Value can be either 0 or 1 812 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 813 { 814 const int out0 = ((const int*)outputs[0])[compNdx]; 815 816 if (out0 != 0 && out0 != 1) 817 { 818 m_failMsg << "Expected [" << compNdx << "] = 0 / 1"; 819 return false; 820 } 821 } 822 } 823 824 return true; 825 } 826}; 827 828class IsinfCase : public CommonFunctionCase 829{ 830public: 831 IsinfCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 832 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "isinf", shaderType) 833 { 834 DE_ASSERT(glu::isDataTypeFloatOrVec(baseType)); 835 836 const int vecSize = glu::getDataTypeScalarSize(baseType); 837 const glu::DataType boolType = vecSize > 1 ? glu::getDataTypeBoolVec(vecSize) : glu::TYPE_BOOL; 838 839 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 840 m_spec.outputs.push_back(Symbol("out0", glu::VarType(boolType, glu::PRECISION_LAST))); 841 m_spec.source = "out0 = isinf(in0);"; 842 } 843 844 void getInputValues (int numValues, void* const* values) const 845 { 846 de::Random rnd (deStringHash(getName()) ^ 0xc2a39fu); 847 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 848 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 849 const int scalarSize = glu::getDataTypeScalarSize(type); 850 const int mantissaBits = getMinMantissaBits(precision); 851 const deUint32 mantissaMask = ~getMaxUlpDiffFromBits(mantissaBits) & ((1u<<23)-1u); 852 853 for (int valNdx = 0; valNdx < numValues*scalarSize; valNdx++) 854 { 855 const bool isInf = rnd.getFloat() > 0.3f; 856 const bool isNan = !isInf && rnd.getFloat() > 0.4f; 857 const deUint32 mantissa = !isInf ? ((1u<<22) | (rnd.getUint32() & mantissaMask)) : 0; 858 const deUint32 exp = !isNan && !isInf ? (rnd.getUint32() & 0x7fu) : 0xffu; 859 const deUint32 sign = rnd.getUint32() & 0x1u; 860 const deUint32 value = (sign << 31) | (exp << 23) | mantissa; 861 862 DE_ASSERT(tcu::Float32(value).isInf() == isInf && tcu::Float32(value).isNaN() == isNan); 863 864 ((deUint32*)values[0])[valNdx] = value; 865 } 866 } 867 868 bool compare (const void* const* inputs, const void* const* outputs) 869 { 870 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 871 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 872 const int scalarSize = glu::getDataTypeScalarSize(type); 873 874 if (precision == glu::PRECISION_HIGHP) 875 { 876 // Only highp is required to support inf/nan 877 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 878 { 879 const float in0 = ((const float*)inputs[0])[compNdx]; 880 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx]; 881 const deUint32 ref = tcu::Float32(in0).isInf() ? 1u : 0u; 882 883 if (out0 != ref) 884 { 885 m_failMsg << "Expected [" << compNdx << "] = " << HexBool(ref); 886 return false; 887 } 888 } 889 } 890 else 891 { 892 // Value can be either 0 or 1 893 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 894 { 895 const int out0 = ((const int*)outputs[0])[compNdx]; 896 897 if (out0 != 0 && out0 != 1) 898 { 899 m_failMsg << "Expected [" << compNdx << "] = 0 / 1"; 900 return false; 901 } 902 } 903 } 904 905 return true; 906 } 907}; 908 909class FloatBitsToUintIntCase : public CommonFunctionCase 910{ 911public: 912 FloatBitsToUintIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType, bool outIsSigned) 913 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), outIsSigned ? "floatBitsToInt" : "floatBitsToUint", shaderType) 914 { 915 const int vecSize = glu::getDataTypeScalarSize(baseType); 916 const glu::DataType intType = outIsSigned ? (vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT) 917 : (vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT); 918 919 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 920 m_spec.outputs.push_back(Symbol("out0", glu::VarType(intType, glu::PRECISION_HIGHP))); 921 m_spec.source = outIsSigned ? "out0 = floatBitsToInt(in0);" : "out0 = floatBitsToUint(in0);"; 922 } 923 924 void getInputValues (int numValues, void* const* values) const 925 { 926 const Vec2 ranges[] = 927 { 928 Vec2(-2.0f, 2.0f), // lowp 929 Vec2(-1e3f, 1e3f), // mediump 930 Vec2(-1e7f, 1e7f) // highp 931 }; 932 933 de::Random rnd (deStringHash(getName()) ^ 0x2790au); 934 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 935 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 936 const int scalarSize = glu::getDataTypeScalarSize(type); 937 938 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), values[0], numValues*scalarSize); 939 } 940 941 bool compare (const void* const* inputs, const void* const* outputs) 942 { 943 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 944 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 945 const int scalarSize = glu::getDataTypeScalarSize(type); 946 947 const int mantissaBits = getMinMantissaBits(precision); 948 const int maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); 949 950 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 951 { 952 const float in0 = ((const float*)inputs[0])[compNdx]; 953 const deUint32 out0 = ((const deUint32*)outputs[0])[compNdx]; 954 const deUint32 refOut0 = tcu::Float32(in0).bits(); 955 const int ulpDiff = de::abs((int)out0 - (int)refOut0); 956 957 if (ulpDiff > maxUlpDiff) 958 { 959 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(refOut0) << " with threshold " 960 << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff); 961 return false; 962 } 963 } 964 965 return true; 966 } 967}; 968 969class FloatBitsToIntCase : public FloatBitsToUintIntCase 970{ 971public: 972 FloatBitsToIntCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 973 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, true) 974 { 975 } 976}; 977 978class FloatBitsToUintCase : public FloatBitsToUintIntCase 979{ 980public: 981 FloatBitsToUintCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 982 : FloatBitsToUintIntCase(context, baseType, precision, shaderType, false) 983 { 984 } 985}; 986 987class BitsToFloatCase : public CommonFunctionCase 988{ 989public: 990 BitsToFloatCase (Context& context, glu::DataType baseType, glu::ShaderType shaderType) 991 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, glu::PRECISION_HIGHP, shaderType).c_str(), glu::isDataTypeIntOrIVec(baseType) ? "intBitsToFloat" : "uintBitsToFloat", shaderType) 992 { 993 const bool inIsSigned = glu::isDataTypeIntOrIVec(baseType); 994 const int vecSize = glu::getDataTypeScalarSize(baseType); 995 const glu::DataType floatType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT; 996 997 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, glu::PRECISION_HIGHP))); 998 m_spec.outputs.push_back(Symbol("out0", glu::VarType(floatType, glu::PRECISION_HIGHP))); 999 m_spec.source = inIsSigned ? "out0 = intBitsToFloat(in0);" : "out0 = uintBitsToFloat(in0);"; 1000 } 1001 1002 void getInputValues (int numValues, void* const* values) const 1003 { 1004 de::Random rnd (deStringHash(getName()) ^ 0xbbb225u); 1005 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1006 const int scalarSize = glu::getDataTypeScalarSize(type); 1007 const Vec2 range (-1e8f, +1e8f); 1008 1009 // \note Filled as floats. 1010 fillRandomScalars(rnd, range.x(), range.y(), values[0], numValues*scalarSize); 1011 } 1012 1013 bool compare (const void* const* inputs, const void* const* outputs) 1014 { 1015 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1016 const int scalarSize = glu::getDataTypeScalarSize(type); 1017 const deUint32 maxUlpDiff = 0; 1018 1019 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1020 { 1021 const float in0 = ((const float*)inputs[0])[compNdx]; 1022 const float out0 = ((const float*)outputs[0])[compNdx]; 1023 const deUint32 ulpDiff = getUlpDiff(in0, out0); 1024 1025 if (ulpDiff > maxUlpDiff) 1026 { 1027 m_failMsg << "Expected [" << compNdx << "] = " << tcu::toHex(tcu::Float32(in0).bits()) << " with ULP threshold " 1028 << tcu::toHex(maxUlpDiff) << ", got ULP diff " << tcu::toHex(ulpDiff); 1029 return false; 1030 } 1031 } 1032 1033 return true; 1034 } 1035}; 1036 1037class FloorCase : public CommonFunctionCase 1038{ 1039public: 1040 FloorCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1041 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "floor", shaderType) 1042 { 1043 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1044 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1045 m_spec.source = "out0 = floor(in0);"; 1046 } 1047 1048 void getInputValues (int numValues, void* const* values) const 1049 { 1050 const Vec2 ranges[] = 1051 { 1052 Vec2(-2.0f, 2.0f), // lowp 1053 Vec2(-1e3f, 1e3f), // mediump 1054 Vec2(-1e7f, 1e7f) // highp 1055 }; 1056 1057 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1058 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1059 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1060 const int scalarSize = glu::getDataTypeScalarSize(type); 1061 // Random cases. 1062 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize); 1063 1064 // If precision is mediump, make sure values can be represented in fp16 exactly 1065 if (precision == glu::PRECISION_MEDIUMP) 1066 { 1067 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1068 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1069 } 1070 } 1071 1072 bool compare (const void* const* inputs, const void* const* outputs) 1073 { 1074 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1075 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1076 const int scalarSize = glu::getDataTypeScalarSize(type); 1077 1078 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1079 { 1080 // Require exact result. 1081 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1082 { 1083 const float in0 = ((const float*)inputs[0])[compNdx]; 1084 const float out0 = ((const float*)outputs[0])[compNdx]; 1085 const float ref = deFloatFloor(in0); 1086 1087 const deUint32 ulpDiff = getUlpDiff(out0, ref); 1088 1089 if (ulpDiff > 0) 1090 { 1091 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1092 return false; 1093 } 1094 } 1095 } 1096 else 1097 { 1098 const int mantissaBits = getMinMantissaBits(precision); 1099 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1100 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1101 1102 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1103 { 1104 const float in0 = ((const float*)inputs[0])[compNdx]; 1105 const float out0 = ((const float*)outputs[0])[compNdx]; 1106 const int minRes = int(deFloatFloor(in0-eps)); 1107 const int maxRes = int(deFloatFloor(in0+eps)); 1108 bool anyOk = false; 1109 1110 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1111 { 1112 const deUint32 ulpDiff = getUlpDiff(out0, float(roundedVal)); 1113 1114 if (ulpDiff <= maxUlpDiff) 1115 { 1116 anyOk = true; 1117 break; 1118 } 1119 } 1120 1121 if (!anyOk) 1122 { 1123 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1124 return false; 1125 } 1126 } 1127 } 1128 1129 return true; 1130 } 1131}; 1132 1133class TruncCase : public CommonFunctionCase 1134{ 1135public: 1136 TruncCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1137 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "trunc", shaderType) 1138 { 1139 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1140 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1141 m_spec.source = "out0 = trunc(in0);"; 1142 } 1143 1144 void getInputValues (int numValues, void* const* values) const 1145 { 1146 const Vec2 ranges[] = 1147 { 1148 Vec2(-2.0f, 2.0f), // lowp 1149 Vec2(-1e3f, 1e3f), // mediump 1150 Vec2(-1e7f, 1e7f) // highp 1151 }; 1152 1153 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1154 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1155 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1156 const int scalarSize = glu::getDataTypeScalarSize(type); 1157 const float specialCases[] = { 0.0f, -0.0f, -0.9f, 0.9f, 1.0f, -1.0f }; 1158 const int numSpecialCases = DE_LENGTH_OF_ARRAY(specialCases); 1159 1160 // Special cases 1161 for (int caseNdx = 0; caseNdx < numSpecialCases; caseNdx++) 1162 { 1163 for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++) 1164 ((float*)values[0])[caseNdx*scalarSize + scalarNdx] = specialCases[caseNdx]; 1165 } 1166 1167 // Random cases. 1168 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + scalarSize*numSpecialCases, (numValues-numSpecialCases)*scalarSize); 1169 1170 // If precision is mediump, make sure values can be represented in fp16 exactly 1171 if (precision == glu::PRECISION_MEDIUMP) 1172 { 1173 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1174 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1175 } 1176 } 1177 1178 bool compare (const void* const* inputs, const void* const* outputs) 1179 { 1180 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1181 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1182 const int scalarSize = glu::getDataTypeScalarSize(type); 1183 1184 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1185 { 1186 // Require exact result. 1187 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1188 { 1189 const float in0 = ((const float*)inputs[0])[compNdx]; 1190 const float out0 = ((const float*)outputs[0])[compNdx]; 1191 const bool isNeg = tcu::Float32(in0).sign() < 0; 1192 const float ref = isNeg ? (-float(int(-in0))) : float(int(in0)); 1193 1194 // \note: trunc() function definition is a bit broad on negative zeros. Ignore result sign if zero. 1195 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref); 1196 1197 if (ulpDiff > 0) 1198 { 1199 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1200 return false; 1201 } 1202 } 1203 } 1204 else 1205 { 1206 const int mantissaBits = getMinMantissaBits(precision); 1207 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1208 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1209 1210 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1211 { 1212 const float in0 = ((const float*)inputs[0])[compNdx]; 1213 const float out0 = ((const float*)outputs[0])[compNdx]; 1214 const int minRes = int(in0-eps); 1215 const int maxRes = int(in0+eps); 1216 bool anyOk = false; 1217 1218 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1219 { 1220 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 1221 1222 if (ulpDiff <= maxUlpDiff) 1223 { 1224 anyOk = true; 1225 break; 1226 } 1227 } 1228 1229 if (!anyOk) 1230 { 1231 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1232 return false; 1233 } 1234 } 1235 } 1236 1237 return true; 1238 } 1239}; 1240 1241class RoundCase : public CommonFunctionCase 1242{ 1243public: 1244 RoundCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1245 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "round", shaderType) 1246 { 1247 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1248 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1249 m_spec.source = "out0 = round(in0);"; 1250 } 1251 1252 void getInputValues (int numValues, void* const* values) const 1253 { 1254 const Vec2 ranges[] = 1255 { 1256 Vec2(-2.0f, 2.0f), // lowp 1257 Vec2(-1e3f, 1e3f), // mediump 1258 Vec2(-1e7f, 1e7f) // highp 1259 }; 1260 1261 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1262 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1263 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1264 const int scalarSize = glu::getDataTypeScalarSize(type); 1265 int numSpecialCases = 0; 1266 1267 // Special cases. 1268 if (precision != glu::PRECISION_LOWP) 1269 { 1270 DE_ASSERT(numValues >= 10); 1271 for (int ndx = 0; ndx < 10; ndx++) 1272 { 1273 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y()); 1274 std::fill((float*)values[0], (float*)values[0] + scalarSize, v); 1275 numSpecialCases += 1; 1276 } 1277 } 1278 1279 // Random cases. 1280 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize); 1281 1282 // If precision is mediump, make sure values can be represented in fp16 exactly 1283 if (precision == glu::PRECISION_MEDIUMP) 1284 { 1285 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1286 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1287 } 1288 } 1289 1290 bool compare (const void* const* inputs, const void* const* outputs) 1291 { 1292 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1293 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1294 const bool hasZeroSign = supportsSignedZero(precision); 1295 const int scalarSize = glu::getDataTypeScalarSize(type); 1296 1297 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1298 { 1299 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1300 { 1301 const float in0 = ((const float*)inputs[0])[compNdx]; 1302 const float out0 = ((const float*)outputs[0])[compNdx]; 1303 1304 if (deFloatFrac(in0) == 0.5f) 1305 { 1306 // Allow both ceil(in) and floor(in) 1307 const float ref0 = deFloatFloor(in0); 1308 const float ref1 = deFloatCeil(in0); 1309 const deUint32 ulpDiff0 = hasZeroSign ? getUlpDiff(out0, ref0) : getUlpDiffIgnoreZeroSign(out0, ref0); 1310 const deUint32 ulpDiff1 = hasZeroSign ? getUlpDiff(out0, ref1) : getUlpDiffIgnoreZeroSign(out0, ref1); 1311 1312 if (ulpDiff0 > 0 && ulpDiff1 > 0) 1313 { 1314 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref0) << " or " << HexFloat(ref1) << ", got ULP diff " << tcu::toHex(de::min(ulpDiff0, ulpDiff1)); 1315 return false; 1316 } 1317 } 1318 else 1319 { 1320 // Require exact result 1321 const float ref = roundEven(in0); 1322 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 1323 1324 if (ulpDiff > 0) 1325 { 1326 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1327 return false; 1328 } 1329 } 1330 } 1331 } 1332 else 1333 { 1334 const int mantissaBits = getMinMantissaBits(precision); 1335 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1336 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1337 1338 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1339 { 1340 const float in0 = ((const float*)inputs[0])[compNdx]; 1341 const float out0 = ((const float*)outputs[0])[compNdx]; 1342 const int minRes = int(roundEven(in0-eps)); 1343 const int maxRes = int(roundEven(in0+eps)); 1344 bool anyOk = false; 1345 1346 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1347 { 1348 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 1349 1350 if (ulpDiff <= maxUlpDiff) 1351 { 1352 anyOk = true; 1353 break; 1354 } 1355 } 1356 1357 if (!anyOk) 1358 { 1359 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1360 return false; 1361 } 1362 } 1363 } 1364 1365 return true; 1366 } 1367}; 1368 1369class CeilCase : public CommonFunctionCase 1370{ 1371public: 1372 CeilCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1373 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "ceil", shaderType) 1374 { 1375 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1376 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1377 m_spec.source = "out0 = ceil(in0);"; 1378 } 1379 1380 void getInputValues (int numValues, void* const* values) const 1381 { 1382 const Vec2 ranges[] = 1383 { 1384 Vec2(-2.0f, 2.0f), // lowp 1385 Vec2(-1e3f, 1e3f), // mediump 1386 Vec2(-1e7f, 1e7f) // highp 1387 }; 1388 1389 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1390 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1391 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1392 const int scalarSize = glu::getDataTypeScalarSize(type); 1393 1394 // Random cases. 1395 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0], numValues*scalarSize); 1396 1397 // If precision is mediump, make sure values can be represented in fp16 exactly 1398 if (precision == glu::PRECISION_MEDIUMP) 1399 { 1400 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1401 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1402 } 1403 } 1404 1405 bool compare (const void* const* inputs, const void* const* outputs) 1406 { 1407 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1408 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1409 const bool hasZeroSign = supportsSignedZero(precision); 1410 const int scalarSize = glu::getDataTypeScalarSize(type); 1411 1412 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1413 { 1414 // Require exact result. 1415 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1416 { 1417 const float in0 = ((const float*)inputs[0])[compNdx]; 1418 const float out0 = ((const float*)outputs[0])[compNdx]; 1419 const float ref = deFloatCeil(in0); 1420 1421 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 1422 1423 if (ulpDiff > 0) 1424 { 1425 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1426 return false; 1427 } 1428 } 1429 } 1430 else 1431 { 1432 const int mantissaBits = getMinMantissaBits(precision); 1433 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(mantissaBits); // ULP diff for rounded integer value. 1434 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1435 1436 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1437 { 1438 const float in0 = ((const float*)inputs[0])[compNdx]; 1439 const float out0 = ((const float*)outputs[0])[compNdx]; 1440 const int minRes = int(deFloatCeil(in0-eps)); 1441 const int maxRes = int(deFloatCeil(in0+eps)); 1442 bool anyOk = false; 1443 1444 for (int roundedVal = minRes; roundedVal <= maxRes; roundedVal++) 1445 { 1446 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, float(roundedVal)); 1447 1448 if (ulpDiff <= maxUlpDiff) 1449 { 1450 anyOk = true; 1451 break; 1452 } 1453 } 1454 1455 if (!anyOk && de::inRange(0, minRes, maxRes)) 1456 { 1457 // Allow -0 as well. 1458 const int ulpDiff = de::abs((int)tcu::Float32(out0).bits() - (int)0x80000000u); 1459 anyOk = ((deUint32)ulpDiff <= maxUlpDiff); 1460 } 1461 1462 if (!anyOk) 1463 { 1464 m_failMsg << "Expected [" << compNdx << "] = [" << minRes << ", " << maxRes << "] with ULP threshold " << tcu::toHex(maxUlpDiff); 1465 return false; 1466 } 1467 } 1468 } 1469 1470 return true; 1471 } 1472}; 1473 1474class FractCase : public CommonFunctionCase 1475{ 1476public: 1477 FractCase (Context& context, glu::DataType baseType, glu::Precision precision, glu::ShaderType shaderType) 1478 : CommonFunctionCase(context, getCommonFuncCaseName(baseType, precision, shaderType).c_str(), "fract", shaderType) 1479 { 1480 m_spec.inputs.push_back(Symbol("in0", glu::VarType(baseType, precision))); 1481 m_spec.outputs.push_back(Symbol("out0", glu::VarType(baseType, precision))); 1482 m_spec.source = "out0 = fract(in0);"; 1483 } 1484 1485 void getInputValues (int numValues, void* const* values) const 1486 { 1487 const Vec2 ranges[] = 1488 { 1489 Vec2(-2.0f, 2.0f), // lowp 1490 Vec2(-1e3f, 1e3f), // mediump 1491 Vec2(-1e7f, 1e7f) // highp 1492 }; 1493 1494 de::Random rnd (deStringHash(getName()) ^ 0xac23fu); 1495 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1496 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1497 const int scalarSize = glu::getDataTypeScalarSize(type); 1498 int numSpecialCases = 0; 1499 1500 // Special cases. 1501 if (precision != glu::PRECISION_LOWP) 1502 { 1503 DE_ASSERT(numValues >= 10); 1504 for (int ndx = 0; ndx < 10; ndx++) 1505 { 1506 const float v = de::clamp(float(ndx) - 5.5f, ranges[precision].x(), ranges[precision].y()); 1507 std::fill((float*)values[0], (float*)values[0] + scalarSize, v); 1508 numSpecialCases += 1; 1509 } 1510 } 1511 1512 // Random cases. 1513 fillRandomScalars(rnd, ranges[precision].x(), ranges[precision].y(), (float*)values[0] + numSpecialCases*scalarSize, (numValues-numSpecialCases)*scalarSize); 1514 1515 // If precision is mediump, make sure values can be represented in fp16 exactly 1516 if (precision == glu::PRECISION_MEDIUMP) 1517 { 1518 for (int ndx = 0; ndx < numValues*scalarSize; ndx++) 1519 ((float*)values[0])[ndx] = tcu::Float16(((float*)values[0])[ndx]).asFloat(); 1520 } 1521 } 1522 1523 bool compare (const void* const* inputs, const void* const* outputs) 1524 { 1525 const glu::DataType type = m_spec.inputs[0].varType.getBasicType(); 1526 const glu::Precision precision = m_spec.inputs[0].varType.getPrecision(); 1527 const bool hasZeroSign = supportsSignedZero(precision); 1528 const int scalarSize = glu::getDataTypeScalarSize(type); 1529 1530 if (precision == glu::PRECISION_HIGHP || precision == glu::PRECISION_MEDIUMP) 1531 { 1532 // Require exact result. 1533 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1534 { 1535 const float in0 = ((const float*)inputs[0])[compNdx]; 1536 const float out0 = ((const float*)outputs[0])[compNdx]; 1537 const float ref = deFloatFrac(in0); 1538 1539 const deUint32 ulpDiff = hasZeroSign ? getUlpDiff(out0, ref) : getUlpDiffIgnoreZeroSign(out0, ref); 1540 1541 if (ulpDiff > 0) 1542 { 1543 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << ", got ULP diff " << tcu::toHex(ulpDiff); 1544 return false; 1545 } 1546 } 1547 } 1548 else 1549 { 1550 const int mantissaBits = getMinMantissaBits(precision); 1551 const float eps = getEpsFromBits(1.0f, mantissaBits); // epsilon for rounding bounds 1552 1553 for (int compNdx = 0; compNdx < scalarSize; compNdx++) 1554 { 1555 const float in0 = ((const float*)inputs[0])[compNdx]; 1556 const float out0 = ((const float*)outputs[0])[compNdx]; 1557 1558 if (int(deFloatFloor(in0-eps)) == int(deFloatFloor(in0+eps))) 1559 { 1560 const float ref = deFloatFrac(in0); 1561 const int bitsLost = numBitsLostInOp(in0, ref); 1562 const deUint32 maxUlpDiff = getMaxUlpDiffFromBits(de::max(0, mantissaBits-bitsLost)); // ULP diff for rounded integer value. 1563 const deUint32 ulpDiff = getUlpDiffIgnoreZeroSign(out0, ref); 1564 1565 if (ulpDiff > maxUlpDiff) 1566 { 1567 m_failMsg << "Expected [" << compNdx << "] = " << HexFloat(ref) << " with ULP threshold " << tcu::toHex(maxUlpDiff) << ", got diff " << tcu::toHex(ulpDiff); 1568 return false; 1569 } 1570 } 1571 else 1572 { 1573 if (out0 >= 1.0f) 1574 { 1575 m_failMsg << "Expected [" << compNdx << "] < 1.0"; 1576 return false; 1577 } 1578 } 1579 } 1580 } 1581 1582 return true; 1583 } 1584}; 1585 1586ShaderCommonFunctionTests::ShaderCommonFunctionTests (Context& context) 1587 : TestCaseGroup(context, "common", "Common function tests") 1588{ 1589} 1590 1591ShaderCommonFunctionTests::~ShaderCommonFunctionTests (void) 1592{ 1593} 1594 1595template<class TestClass> 1596static void addFunctionCases (TestCaseGroup* parent, const char* functionName, bool floatTypes, bool intTypes, bool uintTypes) 1597{ 1598 tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), functionName, functionName); 1599 parent->addChild(group); 1600 1601 const glu::DataType scalarTypes[] = 1602 { 1603 glu::TYPE_FLOAT, 1604 glu::TYPE_INT, 1605 glu::TYPE_UINT 1606 }; 1607 1608 for (int scalarTypeNdx = 0; scalarTypeNdx < DE_LENGTH_OF_ARRAY(scalarTypes); scalarTypeNdx++) 1609 { 1610 const glu::DataType scalarType = scalarTypes[scalarTypeNdx]; 1611 1612 if ((!floatTypes && scalarType == glu::TYPE_FLOAT) || 1613 (!intTypes && scalarType == glu::TYPE_INT) || 1614 (!uintTypes && scalarType == glu::TYPE_UINT)) 1615 continue; 1616 1617 for (int vecSize = 1; vecSize <= 4; vecSize++) 1618 { 1619 for (int prec = glu::PRECISION_LOWP; prec <= glu::PRECISION_HIGHP; prec++) 1620 { 1621 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++) 1622 group->addChild(new TestClass(parent->getContext(), glu::DataType(scalarType + vecSize - 1), glu::Precision(prec), glu::ShaderType(shaderType))); 1623 } 1624 } 1625 } 1626} 1627 1628void ShaderCommonFunctionTests::init (void) 1629{ 1630 // Float? Int? Uint? 1631 addFunctionCases<AbsCase> (this, "abs", true, true, false); 1632 addFunctionCases<SignCase> (this, "sign", true, true, false); 1633 addFunctionCases<FloorCase> (this, "floor", true, false, false); 1634 addFunctionCases<TruncCase> (this, "trunc", true, false, false); 1635 addFunctionCases<RoundCase> (this, "round", true, false, false); 1636 addFunctionCases<RoundEvenCase> (this, "roundeven", true, false, false); 1637 addFunctionCases<CeilCase> (this, "ceil", true, false, false); 1638 addFunctionCases<FractCase> (this, "fract", true, false, false); 1639 // mod 1640 addFunctionCases<ModfCase> (this, "modf", true, false, false); 1641 // min 1642 // max 1643 // clamp 1644 // mix 1645 // step 1646 // smoothstep 1647 addFunctionCases<IsnanCase> (this, "isnan", true, false, false); 1648 addFunctionCases<IsinfCase> (this, "isinf", true, false, false); 1649 addFunctionCases<FloatBitsToIntCase> (this, "floatbitstoint", true, false, false); 1650 addFunctionCases<FloatBitsToUintCase> (this, "floatbitstouint", true, false, false); 1651 1652 // (u)intBitsToFloat() 1653 { 1654 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "intbitstofloat", "intBitsToFloat() Tests"); 1655 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uintbitstofloat", "uintBitsToFloat() Tests"); 1656 1657 addChild(intGroup); 1658 addChild(uintGroup); 1659 1660 for (int vecSize = 1; vecSize < 4; vecSize++) 1661 { 1662 const glu::DataType intType = vecSize > 1 ? glu::getDataTypeIntVec(vecSize) : glu::TYPE_INT; 1663 const glu::DataType uintType = vecSize > 1 ? glu::getDataTypeUintVec(vecSize) : glu::TYPE_UINT; 1664 1665 for (int shaderType = glu::SHADERTYPE_VERTEX; shaderType <= glu::SHADERTYPE_FRAGMENT; shaderType++) 1666 { 1667 intGroup->addChild(new BitsToFloatCase(m_context, intType, glu::ShaderType(shaderType))); 1668 uintGroup->addChild(new BitsToFloatCase(m_context, uintType, glu::ShaderType(shaderType))); 1669 } 1670 } 1671 } 1672} 1673 1674} // Functional 1675} // gles3 1676} // deqp 1677