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