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