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