xref: /aosp_15_r20/external/deqp/modules/gles31/functional/es31fShaderPackingFunctionTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 Floating-point packing and unpacking function tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fShaderPackingFunctionTests.hpp"
25 #include "glsShaderExecUtil.hpp"
26 #include "tcuTestLog.hpp"
27 #include "tcuFormatUtil.hpp"
28 #include "tcuFloat.hpp"
29 #include "deRandom.hpp"
30 #include "deMath.h"
31 #include "deString.h"
32 
33 namespace deqp
34 {
35 namespace gles31
36 {
37 namespace Functional
38 {
39 
40 using std::string;
41 using tcu::TestLog;
42 using namespace gls::ShaderExecUtil;
43 
44 namespace
45 {
46 
getUlpDiff(float a,float b)47 inline uint32_t getUlpDiff(float a, float b)
48 {
49     const uint32_t aBits = tcu::Float32(a).bits();
50     const uint32_t bBits = tcu::Float32(b).bits();
51     return aBits > bBits ? aBits - bBits : bBits - aBits;
52 }
53 
54 struct HexFloat
55 {
56     const float value;
HexFloatdeqp::gles31::Functional::__anonf0671f880111::HexFloat57     HexFloat(const float value_) : value(value_)
58     {
59     }
60 };
61 
operator <<(std::ostream & str,const HexFloat & v)62 std::ostream &operator<<(std::ostream &str, const HexFloat &v)
63 {
64     return str << v.value << " / " << tcu::toHex(tcu::Float32(v.value).bits());
65 }
66 
67 } // namespace
68 
69 // ShaderPackingFunctionCase
70 
71 class ShaderPackingFunctionCase : public TestCase
72 {
73 public:
74     ShaderPackingFunctionCase(Context &context, const char *name, const char *description, glu::ShaderType shaderType);
75     ~ShaderPackingFunctionCase(void);
76 
77     void init(void);
78     void deinit(void);
79 
80 protected:
81     glu::ShaderType m_shaderType;
82     ShaderSpec m_spec;
83     ShaderExecutor *m_executor;
84 
85 private:
86     ShaderPackingFunctionCase(const ShaderPackingFunctionCase &other);
87     ShaderPackingFunctionCase &operator=(const ShaderPackingFunctionCase &other);
88 };
89 
ShaderPackingFunctionCase(Context & context,const char * name,const char * description,glu::ShaderType shaderType)90 ShaderPackingFunctionCase::ShaderPackingFunctionCase(Context &context, const char *name, const char *description,
91                                                      glu::ShaderType shaderType)
92     : TestCase(context, name, description)
93     , m_shaderType(shaderType)
94     , m_executor(DE_NULL)
95 {
96     m_spec.version = glu::getContextTypeGLSLVersion(context.getRenderContext().getType());
97 }
98 
~ShaderPackingFunctionCase(void)99 ShaderPackingFunctionCase::~ShaderPackingFunctionCase(void)
100 {
101     ShaderPackingFunctionCase::deinit();
102 }
103 
init(void)104 void ShaderPackingFunctionCase::init(void)
105 {
106     DE_ASSERT(!m_executor);
107 
108     m_executor = createExecutor(m_context.getRenderContext(), m_shaderType, m_spec);
109     m_testCtx.getLog() << m_executor;
110 
111     if (!m_executor->isOk())
112         throw tcu::TestError("Compile failed");
113 }
114 
deinit(void)115 void ShaderPackingFunctionCase::deinit(void)
116 {
117     delete m_executor;
118     m_executor = DE_NULL;
119 }
120 
121 // Test cases
122 
123 class PackSnorm2x16Case : public ShaderPackingFunctionCase
124 {
125 public:
PackSnorm2x16Case(Context & context,glu::ShaderType shaderType,glu::Precision precision)126     PackSnorm2x16Case(Context &context, glu::ShaderType shaderType, glu::Precision precision)
127         : ShaderPackingFunctionCase(
128               context,
129               (string("packsnorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(),
130               "packSnorm2x16", shaderType)
131         , m_precision(precision)
132     {
133         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
134         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
135 
136         m_spec.source = "out0 = packSnorm2x16(in0);";
137     }
138 
iterate(void)139     IterateResult iterate(void)
140     {
141         de::Random rnd(deStringHash(getName()) ^ 0x776002);
142         std::vector<tcu::Vec2> inputs;
143         std::vector<uint32_t> outputs;
144         const int                    maxDiff = m_precision == glu::PRECISION_HIGHP    ? 1        : // Rounding only.
145                                                   m_precision == glu::PRECISION_MEDIUMP    ? 33    : // (2^-10) * (2^15) + 1
146                                                   m_precision == glu::PRECISION_LOWP    ? 129    : 0;    // (2^-8) * (2^15) + 1
147 
148         // Special values to check.
149         inputs.push_back(tcu::Vec2(0.0f, 0.0f));
150         inputs.push_back(tcu::Vec2(-1.0f, 1.0f));
151         inputs.push_back(tcu::Vec2(0.5f, -0.5f));
152         inputs.push_back(tcu::Vec2(-1.5f, 1.5f));
153         inputs.push_back(tcu::Vec2(0.25f, -0.75f));
154 
155         // Random values, mostly in range.
156         for (int ndx = 0; ndx < 15; ndx++)
157         {
158             const float x = rnd.getFloat() * 2.5f - 1.25f;
159             const float y = rnd.getFloat() * 2.5f - 1.25f;
160             inputs.push_back(tcu::Vec2(x, y));
161         }
162 
163         // Large random values.
164         for (int ndx = 0; ndx < 80; ndx++)
165         {
166             const float x = rnd.getFloat() * 1e6f - 0.5e6f;
167             const float y = rnd.getFloat() * 1e6f - 0.5e6f;
168             inputs.push_back(tcu::Vec2(x, y));
169         }
170 
171         outputs.resize(inputs.size());
172 
173         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
174                            << tcu::TestLog::EndMessage;
175 
176         {
177             const void *in = &inputs[0];
178             void *out      = &outputs[0];
179 
180             m_executor->useProgram();
181             m_executor->execute((int)inputs.size(), &in, &out);
182         }
183 
184         // Verify
185         {
186             const int numValues = (int)inputs.size();
187             const int maxPrints = 10;
188             int numFailed       = 0;
189 
190             for (int valNdx = 0; valNdx < numValues; valNdx++)
191             {
192                 const uint16_t ref0 =
193                     (uint16_t)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 32767.0f),
194                                         -(1 << 15), (1 << 15) - 1);
195                 const uint16_t ref1 =
196                     (uint16_t)de::clamp(deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 32767.0f),
197                                         -(1 << 15), (1 << 15) - 1);
198                 const uint32_t ref  = (ref1 << 16) | ref0;
199                 const uint32_t res  = outputs[valNdx];
200                 const uint16_t res0 = (uint16_t)(res & 0xffff);
201                 const uint16_t res1 = (uint16_t)(res >> 16);
202                 const int diff0     = de::abs((int)ref0 - (int)res0);
203                 const int diff1     = de::abs((int)ref1 - (int)res1);
204 
205                 if (diff0 > maxDiff || diff1 > maxDiff)
206                 {
207                     if (numFailed < maxPrints)
208                     {
209                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
210                                            << ", expected packSnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
211                                            << ", got " << tcu::toHex(res) << "\n  diffs = (" << diff0 << ", " << diff1
212                                            << "), max diff = " << maxDiff << TestLog::EndMessage;
213                     }
214                     else if (numFailed == maxPrints)
215                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
216 
217                     numFailed += 1;
218                 }
219             }
220 
221             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
222                                << TestLog::EndMessage;
223 
224             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
225                                     numFailed == 0 ? "Pass" : "Result comparison failed");
226         }
227 
228         return STOP;
229     }
230 
231 private:
232     glu::Precision m_precision;
233 };
234 
235 class UnpackSnorm2x16Case : public ShaderPackingFunctionCase
236 {
237 public:
UnpackSnorm2x16Case(Context & context,glu::ShaderType shaderType)238     UnpackSnorm2x16Case(Context &context, glu::ShaderType shaderType)
239         : ShaderPackingFunctionCase(context, (string("unpacksnorm2x16") + getShaderTypePostfix(shaderType)).c_str(),
240                                     "unpackSnorm2x16", shaderType)
241     {
242         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
243         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
244 
245         m_spec.source = "out0 = unpackSnorm2x16(in0);";
246     }
247 
iterate(void)248     IterateResult iterate(void)
249     {
250         const uint32_t maxDiff = 1; // Rounding error.
251         de::Random rnd(deStringHash(getName()) ^ 0x776002);
252         std::vector<uint32_t> inputs;
253         std::vector<tcu::Vec2> outputs;
254 
255         inputs.push_back(0x00000000u);
256         inputs.push_back(0x7fff8000u);
257         inputs.push_back(0x80007fffu);
258         inputs.push_back(0xffffffffu);
259         inputs.push_back(0x0001fffeu);
260 
261         // Random values.
262         for (int ndx = 0; ndx < 95; ndx++)
263             inputs.push_back(rnd.getUint32());
264 
265         outputs.resize(inputs.size());
266 
267         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
268                            << tcu::TestLog::EndMessage;
269 
270         {
271             const void *in = &inputs[0];
272             void *out      = &outputs[0];
273 
274             m_executor->useProgram();
275             m_executor->execute((int)inputs.size(), &in, &out);
276         }
277 
278         // Verify
279         {
280             const int numValues = (int)inputs.size();
281             const int maxPrints = 10;
282             int numFailed       = 0;
283 
284             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
285             {
286                 const int16_t in0 = (int16_t)(uint16_t)(inputs[valNdx] & 0xffff);
287                 const int16_t in1 = (int16_t)(uint16_t)(inputs[valNdx] >> 16);
288                 const float ref0  = de::clamp(float(in0) / 32767.f, -1.0f, 1.0f);
289                 const float ref1  = de::clamp(float(in1) / 32767.f, -1.0f, 1.0f);
290                 const float res0  = outputs[valNdx].x();
291                 const float res1  = outputs[valNdx].y();
292 
293                 const uint32_t diff0 = getUlpDiff(ref0, res0);
294                 const uint32_t diff1 = getUlpDiff(ref1, res1);
295 
296                 if (diff0 > maxDiff || diff1 > maxDiff)
297                 {
298                     if (numFailed < maxPrints)
299                     {
300                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
301                                            << "  expected unpackSnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
302                                            << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
303                                            << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
304                                            << "\n  ULP diffs = (" << diff0 << ", " << diff1
305                                            << "), max diff = " << maxDiff << TestLog::EndMessage;
306                     }
307                     else if (numFailed == maxPrints)
308                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
309 
310                     numFailed += 1;
311                 }
312             }
313 
314             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
315                                << TestLog::EndMessage;
316 
317             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
318                                     numFailed == 0 ? "Pass" : "Result comparison failed");
319         }
320 
321         return STOP;
322     }
323 };
324 
325 class PackUnorm2x16Case : public ShaderPackingFunctionCase
326 {
327 public:
PackUnorm2x16Case(Context & context,glu::ShaderType shaderType,glu::Precision precision)328     PackUnorm2x16Case(Context &context, glu::ShaderType shaderType, glu::Precision precision)
329         : ShaderPackingFunctionCase(
330               context,
331               (string("packunorm2x16") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(),
332               "packUnorm2x16", shaderType)
333         , m_precision(precision)
334     {
335         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, precision)));
336         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
337 
338         m_spec.source = "out0 = packUnorm2x16(in0);";
339     }
340 
iterate(void)341     IterateResult iterate(void)
342     {
343         de::Random rnd(deStringHash(getName()) ^ 0x776002);
344         std::vector<tcu::Vec2> inputs;
345         std::vector<uint32_t> outputs;
346         const int                    maxDiff = m_precision == glu::PRECISION_HIGHP    ? 1        : // Rounding only.
347                                                   m_precision == glu::PRECISION_MEDIUMP    ? 65    : // (2^-10) * (2^16) + 1
348                                                   m_precision == glu::PRECISION_LOWP    ? 257    : 0;    // (2^-8) * (2^16) + 1
349 
350         // Special values to check.
351         inputs.push_back(tcu::Vec2(0.0f, 0.0f));
352         inputs.push_back(tcu::Vec2(0.5f, 1.0f));
353         inputs.push_back(tcu::Vec2(1.0f, 0.5f));
354         inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
355         inputs.push_back(tcu::Vec2(0.25f, 0.75f));
356 
357         // Random values, mostly in range.
358         for (int ndx = 0; ndx < 15; ndx++)
359         {
360             const float x = rnd.getFloat() * 1.25f;
361             const float y = rnd.getFloat() * 1.25f;
362             inputs.push_back(tcu::Vec2(x, y));
363         }
364 
365         // Large random values.
366         for (int ndx = 0; ndx < 80; ndx++)
367         {
368             const float x = rnd.getFloat() * 1e6f - 1e5f;
369             const float y = rnd.getFloat() * 1e6f - 1e5f;
370             inputs.push_back(tcu::Vec2(x, y));
371         }
372 
373         outputs.resize(inputs.size());
374 
375         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
376                            << tcu::TestLog::EndMessage;
377 
378         {
379             const void *in = &inputs[0];
380             void *out      = &outputs[0];
381 
382             m_executor->useProgram();
383             m_executor->execute((int)inputs.size(), &in, &out);
384         }
385 
386         // Verify
387         {
388             const int numValues = (int)inputs.size();
389             const int maxPrints = 10;
390             int numFailed       = 0;
391 
392             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
393             {
394                 const uint16_t ref0 = (uint16_t)de::clamp(
395                     deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 65535.0f), 0, (1 << 16) - 1);
396                 const uint16_t ref1 = (uint16_t)de::clamp(
397                     deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 65535.0f), 0, (1 << 16) - 1);
398                 const uint32_t ref  = (ref1 << 16) | ref0;
399                 const uint32_t res  = outputs[valNdx];
400                 const uint16_t res0 = (uint16_t)(res & 0xffff);
401                 const uint16_t res1 = (uint16_t)(res >> 16);
402                 const int diff0     = de::abs((int)ref0 - (int)res0);
403                 const int diff1     = de::abs((int)ref1 - (int)res1);
404 
405                 if (diff0 > maxDiff || diff1 > maxDiff)
406                 {
407                     if (numFailed < maxPrints)
408                     {
409                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
410                                            << ", expected packUnorm2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
411                                            << ", got " << tcu::toHex(res) << "\n  diffs = (" << diff0 << ", " << diff1
412                                            << "), max diff = " << maxDiff << TestLog::EndMessage;
413                     }
414                     else if (numFailed == maxPrints)
415                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
416 
417                     numFailed += 1;
418                 }
419             }
420 
421             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
422                                << TestLog::EndMessage;
423 
424             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
425                                     numFailed == 0 ? "Pass" : "Result comparison failed");
426         }
427 
428         return STOP;
429     }
430 
431 private:
432     glu::Precision m_precision;
433 };
434 
435 class UnpackUnorm2x16Case : public ShaderPackingFunctionCase
436 {
437 public:
UnpackUnorm2x16Case(Context & context,glu::ShaderType shaderType)438     UnpackUnorm2x16Case(Context &context, glu::ShaderType shaderType)
439         : ShaderPackingFunctionCase(context, (string("unpackunorm2x16") + getShaderTypePostfix(shaderType)).c_str(),
440                                     "unpackUnorm2x16", shaderType)
441     {
442         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
443         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
444 
445         m_spec.source = "out0 = unpackUnorm2x16(in0);";
446     }
447 
iterate(void)448     IterateResult iterate(void)
449     {
450         const uint32_t maxDiff = 1; // Rounding error.
451         de::Random rnd(deStringHash(getName()) ^ 0x776002);
452         std::vector<uint32_t> inputs;
453         std::vector<tcu::Vec2> outputs;
454 
455         inputs.push_back(0x00000000u);
456         inputs.push_back(0x7fff8000u);
457         inputs.push_back(0x80007fffu);
458         inputs.push_back(0xffffffffu);
459         inputs.push_back(0x0001fffeu);
460 
461         // Random values.
462         for (int ndx = 0; ndx < 95; ndx++)
463             inputs.push_back(rnd.getUint32());
464 
465         outputs.resize(inputs.size());
466 
467         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
468                            << tcu::TestLog::EndMessage;
469 
470         {
471             const void *in = &inputs[0];
472             void *out      = &outputs[0];
473 
474             m_executor->useProgram();
475             m_executor->execute((int)inputs.size(), &in, &out);
476         }
477 
478         // Verify
479         {
480             const int numValues = (int)inputs.size();
481             const int maxPrints = 10;
482             int numFailed       = 0;
483 
484             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
485             {
486                 const uint16_t in0 = (uint16_t)(inputs[valNdx] & 0xffff);
487                 const uint16_t in1 = (uint16_t)(inputs[valNdx] >> 16);
488                 const float ref0   = float(in0) / 65535.0f;
489                 const float ref1   = float(in1) / 65535.0f;
490                 const float res0   = outputs[valNdx].x();
491                 const float res1   = outputs[valNdx].y();
492 
493                 const uint32_t diff0 = getUlpDiff(ref0, res0);
494                 const uint32_t diff1 = getUlpDiff(ref1, res1);
495 
496                 if (diff0 > maxDiff || diff1 > maxDiff)
497                 {
498                     if (numFailed < maxPrints)
499                     {
500                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
501                                            << "  expected unpackUnorm2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
502                                            << "vec2(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ")"
503                                            << ", got vec2(" << HexFloat(res0) << ", " << HexFloat(res1) << ")"
504                                            << "\n  ULP diffs = (" << diff0 << ", " << diff1
505                                            << "), max diff = " << maxDiff << TestLog::EndMessage;
506                     }
507                     else if (numFailed == maxPrints)
508                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
509 
510                     numFailed += 1;
511                 }
512             }
513 
514             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
515                                << TestLog::EndMessage;
516 
517             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
518                                     numFailed == 0 ? "Pass" : "Result comparison failed");
519         }
520 
521         return STOP;
522     }
523 };
524 
525 class PackHalf2x16Case : public ShaderPackingFunctionCase
526 {
527 public:
PackHalf2x16Case(Context & context,glu::ShaderType shaderType)528     PackHalf2x16Case(Context &context, glu::ShaderType shaderType)
529         : ShaderPackingFunctionCase(context, (string("packhalf2x16") + getShaderTypePostfix(shaderType)).c_str(),
530                                     "packHalf2x16", shaderType)
531     {
532         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_HIGHP)));
533         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
534 
535         m_spec.source = "out0 = packHalf2x16(in0);";
536     }
537 
iterate(void)538     IterateResult iterate(void)
539     {
540         const int maxDiff = 0; // Values can be represented exactly in mediump.
541         de::Random rnd(deStringHash(getName()) ^ 0x776002);
542         std::vector<tcu::Vec2> inputs;
543         std::vector<uint32_t> outputs;
544 
545         // Special values to check.
546         inputs.push_back(tcu::Vec2(0.0f, 0.0f));
547         inputs.push_back(tcu::Vec2(0.5f, 1.0f));
548         inputs.push_back(tcu::Vec2(1.0f, 0.5f));
549         inputs.push_back(tcu::Vec2(-0.5f, 1.5f));
550         inputs.push_back(tcu::Vec2(0.25f, 0.75f));
551 
552         // Random values.
553         {
554             const int minExp = -14;
555             const int maxExp = 15;
556 
557             for (int ndx = 0; ndx < 95; ndx++)
558             {
559                 tcu::Vec2 v;
560                 for (int c = 0; c < 2; c++)
561                 {
562                     const int s             = rnd.getBool() ? 1 : -1;
563                     const int exp           = rnd.getInt(minExp, maxExp);
564                     const uint32_t mantissa = rnd.getUint32() & ((1 << 23) - 1);
565 
566                     v[c] = tcu::Float32::construct(s, exp ? exp : 1 /* avoid denormals */, (1u << 23) | mantissa)
567                                .asFloat();
568                 }
569                 inputs.push_back(v);
570             }
571         }
572 
573         // Convert input values to fp16 and back to make sure they can be represented exactly in mediump.
574         for (std::vector<tcu::Vec2>::iterator inVal = inputs.begin(); inVal != inputs.end(); ++inVal)
575             *inVal = tcu::Vec2(tcu::Float16(inVal->x()).asFloat(), tcu::Float16(inVal->y()).asFloat());
576 
577         outputs.resize(inputs.size());
578 
579         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
580                            << tcu::TestLog::EndMessage;
581 
582         {
583             const void *in = &inputs[0];
584             void *out      = &outputs[0];
585 
586             m_executor->useProgram();
587             m_executor->execute((int)inputs.size(), &in, &out);
588         }
589 
590         // Verify
591         {
592             const int numValues = (int)inputs.size();
593             const int maxPrints = 10;
594             int numFailed       = 0;
595 
596             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
597             {
598                 const uint16_t ref0 = (uint16_t)tcu::Float16(inputs[valNdx].x()).bits();
599                 const uint16_t ref1 = (uint16_t)tcu::Float16(inputs[valNdx].y()).bits();
600                 const uint32_t ref  = (ref1 << 16) | ref0;
601                 const uint32_t res  = outputs[valNdx];
602                 const uint16_t res0 = (uint16_t)(res & 0xffff);
603                 const uint16_t res1 = (uint16_t)(res >> 16);
604                 const int diff0     = de::abs((int)ref0 - (int)res0);
605                 const int diff1     = de::abs((int)ref1 - (int)res1);
606 
607                 if (diff0 > maxDiff || diff1 > maxDiff)
608                 {
609                     if (numFailed < maxPrints)
610                     {
611                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx
612                                            << ", expected packHalf2x16(" << inputs[valNdx] << ") = " << tcu::toHex(ref)
613                                            << ", got " << tcu::toHex(res) << "\n  diffs = (" << diff0 << ", " << diff1
614                                            << "), max diff = " << maxDiff << TestLog::EndMessage;
615                     }
616                     else if (numFailed == maxPrints)
617                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
618 
619                     numFailed += 1;
620                 }
621             }
622 
623             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
624                                << TestLog::EndMessage;
625 
626             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
627                                     numFailed == 0 ? "Pass" : "Result comparison failed");
628         }
629 
630         return STOP;
631     }
632 };
633 
634 class UnpackHalf2x16Case : public ShaderPackingFunctionCase
635 {
636 public:
UnpackHalf2x16Case(Context & context,glu::ShaderType shaderType)637     UnpackHalf2x16Case(Context &context, glu::ShaderType shaderType)
638         : ShaderPackingFunctionCase(context, (string("unpackhalf2x16") + getShaderTypePostfix(shaderType)).c_str(),
639                                     "unpackHalf2x16", shaderType)
640     {
641         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
642         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_MEDIUMP)));
643 
644         m_spec.source = "out0 = unpackHalf2x16(in0);";
645     }
646 
iterate(void)647     IterateResult iterate(void)
648     {
649         const int maxDiff = 0; // All bits must be accurate.
650         de::Random rnd(deStringHash(getName()) ^ 0x776002);
651         std::vector<uint32_t> inputs;
652         std::vector<tcu::Vec2> outputs;
653 
654         // Special values.
655         inputs.push_back((tcu::Float16(0.0f).bits() << 16) | tcu::Float16(1.0f).bits());
656         inputs.push_back((tcu::Float16(1.0f).bits() << 16) | tcu::Float16(0.0f).bits());
657         inputs.push_back((tcu::Float16(-1.0f).bits() << 16) | tcu::Float16(0.5f).bits());
658         inputs.push_back((tcu::Float16(0.5f).bits() << 16) | tcu::Float16(-0.5f).bits());
659 
660         // Construct random values.
661         {
662             const int minExp   = -14;
663             const int maxExp   = 15;
664             const int mantBits = 10;
665 
666             for (int ndx = 0; ndx < 96; ndx++)
667             {
668                 uint32_t inVal = 0;
669                 for (int c = 0; c < 2; c++)
670                 {
671                     const int s             = rnd.getBool() ? 1 : -1;
672                     const int exp           = rnd.getInt(minExp, maxExp);
673                     const uint32_t mantissa = rnd.getUint32() & ((1 << mantBits) - 1);
674                     const uint16_t value =
675                         tcu::Float16::construct(s, exp ? exp : 1 /* avoid denorm */, (uint16_t)((1u << 10) | mantissa))
676                             .bits();
677 
678                     inVal |= value << (16 * c);
679                 }
680                 inputs.push_back(inVal);
681             }
682         }
683 
684         outputs.resize(inputs.size());
685 
686         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
687                            << tcu::TestLog::EndMessage;
688 
689         {
690             const void *in = &inputs[0];
691             void *out      = &outputs[0];
692 
693             m_executor->useProgram();
694             m_executor->execute((int)inputs.size(), &in, &out);
695         }
696 
697         // Verify
698         {
699             const int numValues = (int)inputs.size();
700             const int maxPrints = 10;
701             int numFailed       = 0;
702 
703             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
704             {
705                 const uint16_t in0 = (uint16_t)(inputs[valNdx] & 0xffff);
706                 const uint16_t in1 = (uint16_t)(inputs[valNdx] >> 16);
707                 const float ref0   = tcu::Float16(in0).asFloat();
708                 const float ref1   = tcu::Float16(in1).asFloat();
709                 const float res0   = outputs[valNdx].x();
710                 const float res1   = outputs[valNdx].y();
711 
712                 const uint32_t refBits0 = tcu::Float32(ref0).bits();
713                 const uint32_t refBits1 = tcu::Float32(ref1).bits();
714                 const uint32_t resBits0 = tcu::Float32(res0).bits();
715                 const uint32_t resBits1 = tcu::Float32(res1).bits();
716 
717                 const int diff0 = de::abs((int)refBits0 - (int)resBits0);
718                 const int diff1 = de::abs((int)refBits1 - (int)resBits1);
719 
720                 if (diff0 > maxDiff || diff1 > maxDiff)
721                 {
722                     if (numFailed < maxPrints)
723                     {
724                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
725                                            << "  expected unpackHalf2x16(" << tcu::toHex(inputs[valNdx]) << ") = "
726                                            << "vec2(" << ref0 << " / " << tcu::toHex(refBits0) << ", " << ref1 << " / "
727                                            << tcu::toHex(refBits1) << ")"
728                                            << ", got vec2(" << res0 << " / " << tcu::toHex(resBits0) << ", " << res1
729                                            << " / " << tcu::toHex(resBits1) << ")"
730                                            << "\n  ULP diffs = (" << diff0 << ", " << diff1
731                                            << "), max diff = " << maxDiff << TestLog::EndMessage;
732                     }
733                     else if (numFailed == maxPrints)
734                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
735 
736                     numFailed += 1;
737                 }
738             }
739 
740             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
741                                << TestLog::EndMessage;
742 
743             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
744                                     numFailed == 0 ? "Pass" : "Result comparison failed");
745         }
746 
747         return STOP;
748     }
749 };
750 
751 class PackSnorm4x8Case : public ShaderPackingFunctionCase
752 {
753 public:
PackSnorm4x8Case(Context & context,glu::ShaderType shaderType,glu::Precision precision)754     PackSnorm4x8Case(Context &context, glu::ShaderType shaderType, glu::Precision precision)
755         : ShaderPackingFunctionCase(
756               context,
757               (string("packsnorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(),
758               "packSnorm4x8", shaderType)
759         , m_precision(precision)
760     {
761         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
762         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
763 
764         m_spec.source = "out0 = packSnorm4x8(in0);";
765     }
766 
iterate(void)767     IterateResult iterate(void)
768     {
769         de::Random rnd(deStringHash(getName()) ^ 0x42f2c0);
770         std::vector<tcu::Vec4> inputs;
771         std::vector<uint32_t> outputs;
772         const int                    maxDiff = m_precision == glu::PRECISION_HIGHP    ? 1    : // Rounding only.
773                                                   m_precision == glu::PRECISION_MEDIUMP    ? 1    : // (2^-10) * (2^7) + 1
774                                                   m_precision == glu::PRECISION_LOWP    ? 2    : 0;    // (2^-8) * (2^7) + 1
775 
776         // Special values to check.
777         inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
778         inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
779         inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
780         inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
781         inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
782 
783         // Random values, mostly in range.
784         for (int ndx = 0; ndx < 15; ndx++)
785         {
786             const float x = rnd.getFloat() * 2.5f - 1.25f;
787             const float y = rnd.getFloat() * 2.5f - 1.25f;
788             const float z = rnd.getFloat() * 2.5f - 1.25f;
789             const float w = rnd.getFloat() * 2.5f - 1.25f;
790             inputs.push_back(tcu::Vec4(x, y, z, w));
791         }
792 
793         // Large random values.
794         for (int ndx = 0; ndx < 80; ndx++)
795         {
796             const float x = rnd.getFloat() * 1e6f - 0.5e6f;
797             const float y = rnd.getFloat() * 1e6f - 0.5e6f;
798             const float z = rnd.getFloat() * 1e6f - 0.5e6f;
799             const float w = rnd.getFloat() * 1e6f - 0.5e6f;
800             inputs.push_back(tcu::Vec4(x, y, z, w));
801         }
802 
803         outputs.resize(inputs.size());
804 
805         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
806                            << tcu::TestLog::EndMessage;
807 
808         {
809             const void *in = &inputs[0];
810             void *out      = &outputs[0];
811 
812             m_executor->useProgram();
813             m_executor->execute((int)inputs.size(), &in, &out);
814         }
815 
816         // Verify
817         {
818             const int numValues = (int)inputs.size();
819             const int maxPrints = 10;
820             int numFailed       = 0;
821 
822             for (int valNdx = 0; valNdx < numValues; valNdx++)
823             {
824                 const uint16_t ref0 = (uint8_t)de::clamp(
825                     deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), -1.0f, 1.0f) * 127.0f), -(1 << 7), (1 << 7) - 1);
826                 const uint16_t ref1 = (uint8_t)de::clamp(
827                     deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), -1.0f, 1.0f) * 127.0f), -(1 << 7), (1 << 7) - 1);
828                 const uint16_t ref2 = (uint8_t)de::clamp(
829                     deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), -1.0f, 1.0f) * 127.0f), -(1 << 7), (1 << 7) - 1);
830                 const uint16_t ref3 = (uint8_t)de::clamp(
831                     deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), -1.0f, 1.0f) * 127.0f), -(1 << 7), (1 << 7) - 1);
832                 const uint32_t ref =
833                     (uint32_t(ref3) << 24) | (uint32_t(ref2) << 16) | (uint32_t(ref1) << 8) | uint32_t(ref0);
834                 const uint32_t res  = outputs[valNdx];
835                 const uint16_t res0 = (uint8_t)(res & 0xff);
836                 const uint16_t res1 = (uint8_t)((res >> 8) & 0xff);
837                 const uint16_t res2 = (uint8_t)((res >> 16) & 0xff);
838                 const uint16_t res3 = (uint8_t)((res >> 24) & 0xff);
839                 const int diff0     = de::abs((int)ref0 - (int)res0);
840                 const int diff1     = de::abs((int)ref1 - (int)res1);
841                 const int diff2     = de::abs((int)ref2 - (int)res2);
842                 const int diff3     = de::abs((int)ref3 - (int)res3);
843 
844                 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
845                 {
846                     if (numFailed < maxPrints)
847                     {
848                         m_testCtx.getLog()
849                             << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ", expected packSnorm4x8("
850                             << inputs[valNdx] << ") = " << tcu::toHex(ref) << ", got " << tcu::toHex(res)
851                             << "\n  diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
852                             << TestLog::EndMessage;
853                     }
854                     else if (numFailed == maxPrints)
855                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
856 
857                     numFailed += 1;
858                 }
859             }
860 
861             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
862                                << TestLog::EndMessage;
863 
864             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
865                                     numFailed == 0 ? "Pass" : "Result comparison failed");
866         }
867 
868         return STOP;
869     }
870 
871 private:
872     glu::Precision m_precision;
873 };
874 
875 class UnpackSnorm4x8Case : public ShaderPackingFunctionCase
876 {
877 public:
UnpackSnorm4x8Case(Context & context,glu::ShaderType shaderType)878     UnpackSnorm4x8Case(Context &context, glu::ShaderType shaderType)
879         : ShaderPackingFunctionCase(context, (string("unpacksnorm4x8") + getShaderTypePostfix(shaderType)).c_str(),
880                                     "unpackSnorm4x8", shaderType)
881     {
882         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
883         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
884 
885         m_spec.source = "out0 = unpackSnorm4x8(in0);";
886     }
887 
iterate(void)888     IterateResult iterate(void)
889     {
890         const uint32_t maxDiff = 1; // Rounding error.
891         de::Random rnd(deStringHash(getName()) ^ 0x776002);
892         std::vector<uint32_t> inputs;
893         std::vector<tcu::Vec4> outputs;
894 
895         inputs.push_back(0x00000000u);
896         inputs.push_back(0x7fff8000u);
897         inputs.push_back(0x80007fffu);
898         inputs.push_back(0xffffffffu);
899         inputs.push_back(0x0001fffeu);
900 
901         // Random values.
902         for (int ndx = 0; ndx < 95; ndx++)
903             inputs.push_back(rnd.getUint32());
904 
905         outputs.resize(inputs.size());
906 
907         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
908                            << tcu::TestLog::EndMessage;
909 
910         {
911             const void *in = &inputs[0];
912             void *out      = &outputs[0];
913 
914             m_executor->useProgram();
915             m_executor->execute((int)inputs.size(), &in, &out);
916         }
917 
918         // Verify
919         {
920             const int numValues = (int)inputs.size();
921             const int maxPrints = 10;
922             int numFailed       = 0;
923 
924             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
925             {
926                 const int8_t in0 = (int8_t)(uint8_t)(inputs[valNdx] & 0xff);
927                 const int8_t in1 = (int8_t)(uint8_t)((inputs[valNdx] >> 8) & 0xff);
928                 const int8_t in2 = (int8_t)(uint8_t)((inputs[valNdx] >> 16) & 0xff);
929                 const int8_t in3 = (int8_t)(uint8_t)(inputs[valNdx] >> 24);
930                 const float ref0 = de::clamp(float(in0) / 127.f, -1.0f, 1.0f);
931                 const float ref1 = de::clamp(float(in1) / 127.f, -1.0f, 1.0f);
932                 const float ref2 = de::clamp(float(in2) / 127.f, -1.0f, 1.0f);
933                 const float ref3 = de::clamp(float(in3) / 127.f, -1.0f, 1.0f);
934                 const float res0 = outputs[valNdx].x();
935                 const float res1 = outputs[valNdx].y();
936                 const float res2 = outputs[valNdx].z();
937                 const float res3 = outputs[valNdx].w();
938 
939                 const uint32_t diff0 = getUlpDiff(ref0, res0);
940                 const uint32_t diff1 = getUlpDiff(ref1, res1);
941                 const uint32_t diff2 = getUlpDiff(ref2, res2);
942                 const uint32_t diff3 = getUlpDiff(ref3, res3);
943 
944                 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
945                 {
946                     if (numFailed < maxPrints)
947                     {
948                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
949                                            << "  expected unpackSnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
950                                            << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", "
951                                            << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
952                                            << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", "
953                                            << HexFloat(res2) << ", " << HexFloat(res3) << ")"
954                                            << "\n  ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", "
955                                            << diff3 << "), max diff = " << maxDiff << TestLog::EndMessage;
956                     }
957                     else if (numFailed == maxPrints)
958                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
959 
960                     numFailed += 1;
961                 }
962             }
963 
964             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
965                                << TestLog::EndMessage;
966 
967             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
968                                     numFailed == 0 ? "Pass" : "Result comparison failed");
969         }
970 
971         return STOP;
972     }
973 };
974 
975 class PackUnorm4x8Case : public ShaderPackingFunctionCase
976 {
977 public:
PackUnorm4x8Case(Context & context,glu::ShaderType shaderType,glu::Precision precision)978     PackUnorm4x8Case(Context &context, glu::ShaderType shaderType, glu::Precision precision)
979         : ShaderPackingFunctionCase(
980               context,
981               (string("packunorm4x8") + getPrecisionPostfix(precision) + getShaderTypePostfix(shaderType)).c_str(),
982               "packUnorm4x8", shaderType)
983         , m_precision(precision)
984     {
985         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_FLOAT_VEC4, precision)));
986         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
987 
988         m_spec.source = "out0 = packUnorm4x8(in0);";
989     }
990 
iterate(void)991     IterateResult iterate(void)
992     {
993         de::Random rnd(deStringHash(getName()) ^ 0x776002);
994         std::vector<tcu::Vec4> inputs;
995         std::vector<uint32_t> outputs;
996         const int                    maxDiff = m_precision == glu::PRECISION_HIGHP    ? 1    : // Rounding only.
997                                                   m_precision == glu::PRECISION_MEDIUMP    ? 1    : // (2^-10) * (2^8) + 1
998                                                   m_precision == glu::PRECISION_LOWP    ? 2    : 0;    // (2^-8) * (2^8) + 1
999 
1000         // Special values to check.
1001         inputs.push_back(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
1002         inputs.push_back(tcu::Vec4(-1.0f, 1.0f, -1.0f, 1.0f));
1003         inputs.push_back(tcu::Vec4(0.5f, -0.5f, -0.5f, 0.5f));
1004         inputs.push_back(tcu::Vec4(-1.5f, 1.5f, -1.5f, 1.5f));
1005         inputs.push_back(tcu::Vec4(0.25f, -0.75f, -0.25f, 0.75f));
1006 
1007         // Random values, mostly in range.
1008         for (int ndx = 0; ndx < 15; ndx++)
1009         {
1010             const float x = rnd.getFloat() * 1.25f - 0.125f;
1011             const float y = rnd.getFloat() * 1.25f - 0.125f;
1012             const float z = rnd.getFloat() * 1.25f - 0.125f;
1013             const float w = rnd.getFloat() * 1.25f - 0.125f;
1014             inputs.push_back(tcu::Vec4(x, y, z, w));
1015         }
1016 
1017         // Large random values.
1018         for (int ndx = 0; ndx < 80; ndx++)
1019         {
1020             const float x = rnd.getFloat() * 1e6f - 1e5f;
1021             const float y = rnd.getFloat() * 1e6f - 1e5f;
1022             const float z = rnd.getFloat() * 1e6f - 1e5f;
1023             const float w = rnd.getFloat() * 1e6f - 1e5f;
1024             inputs.push_back(tcu::Vec4(x, y, z, w));
1025         }
1026 
1027         outputs.resize(inputs.size());
1028 
1029         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
1030                            << tcu::TestLog::EndMessage;
1031 
1032         {
1033             const void *in = &inputs[0];
1034             void *out      = &outputs[0];
1035 
1036             m_executor->useProgram();
1037             m_executor->execute((int)inputs.size(), &in, &out);
1038         }
1039 
1040         // Verify
1041         {
1042             const int numValues = (int)inputs.size();
1043             const int maxPrints = 10;
1044             int numFailed       = 0;
1045 
1046             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1047             {
1048                 const uint16_t ref0 = (uint8_t)de::clamp(
1049                     deRoundFloatToInt32(de::clamp(inputs[valNdx].x(), 0.0f, 1.0f) * 255.0f), 0, (1 << 8) - 1);
1050                 const uint16_t ref1 = (uint8_t)de::clamp(
1051                     deRoundFloatToInt32(de::clamp(inputs[valNdx].y(), 0.0f, 1.0f) * 255.0f), 0, (1 << 8) - 1);
1052                 const uint16_t ref2 = (uint8_t)de::clamp(
1053                     deRoundFloatToInt32(de::clamp(inputs[valNdx].z(), 0.0f, 1.0f) * 255.0f), 0, (1 << 8) - 1);
1054                 const uint16_t ref3 = (uint8_t)de::clamp(
1055                     deRoundFloatToInt32(de::clamp(inputs[valNdx].w(), 0.0f, 1.0f) * 255.0f), 0, (1 << 8) - 1);
1056                 const uint32_t ref =
1057                     (uint32_t(ref3) << 24) | (uint32_t(ref2) << 16) | (uint32_t(ref1) << 8) | uint32_t(ref0);
1058                 const uint32_t res  = outputs[valNdx];
1059                 const uint16_t res0 = (uint8_t)(res & 0xff);
1060                 const uint16_t res1 = (uint8_t)((res >> 8) & 0xff);
1061                 const uint16_t res2 = (uint8_t)((res >> 16) & 0xff);
1062                 const uint16_t res3 = (uint8_t)((res >> 24) & 0xff);
1063                 const int diff0     = de::abs((int)ref0 - (int)res0);
1064                 const int diff1     = de::abs((int)ref1 - (int)res1);
1065                 const int diff2     = de::abs((int)ref2 - (int)res2);
1066                 const int diff3     = de::abs((int)ref3 - (int)res3);
1067 
1068                 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1069                 {
1070                     if (numFailed < maxPrints)
1071                     {
1072                         m_testCtx.getLog()
1073                             << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ", expected packUnorm4x8("
1074                             << inputs[valNdx] << ") = " << tcu::toHex(ref) << ", got " << tcu::toHex(res)
1075                             << "\n  diffs = " << tcu::IVec4(diff0, diff1, diff2, diff3) << ", max diff = " << maxDiff
1076                             << TestLog::EndMessage;
1077                     }
1078                     else if (numFailed == maxPrints)
1079                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1080 
1081                     numFailed += 1;
1082                 }
1083             }
1084 
1085             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
1086                                << TestLog::EndMessage;
1087 
1088             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
1089                                     numFailed == 0 ? "Pass" : "Result comparison failed");
1090         }
1091 
1092         return STOP;
1093     }
1094 
1095 private:
1096     glu::Precision m_precision;
1097 };
1098 
1099 class UnpackUnorm4x8Case : public ShaderPackingFunctionCase
1100 {
1101 public:
UnpackUnorm4x8Case(Context & context,glu::ShaderType shaderType)1102     UnpackUnorm4x8Case(Context &context, glu::ShaderType shaderType)
1103         : ShaderPackingFunctionCase(context, (string("unpackunorm4x8") + getShaderTypePostfix(shaderType)).c_str(),
1104                                     "unpackUnorm4x8", shaderType)
1105     {
1106         m_spec.inputs.push_back(Symbol("in0", glu::VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP)));
1107         m_spec.outputs.push_back(Symbol("out0", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)));
1108 
1109         m_spec.source = "out0 = unpackUnorm4x8(in0);";
1110     }
1111 
iterate(void)1112     IterateResult iterate(void)
1113     {
1114         const uint32_t maxDiff = 1; // Rounding error.
1115         de::Random rnd(deStringHash(getName()) ^ 0x776002);
1116         std::vector<uint32_t> inputs;
1117         std::vector<tcu::Vec4> outputs;
1118 
1119         inputs.push_back(0x00000000u);
1120         inputs.push_back(0x7fff8000u);
1121         inputs.push_back(0x80007fffu);
1122         inputs.push_back(0xffffffffu);
1123         inputs.push_back(0x0001fffeu);
1124 
1125         // Random values.
1126         for (int ndx = 0; ndx < 95; ndx++)
1127             inputs.push_back(rnd.getUint32());
1128 
1129         outputs.resize(inputs.size());
1130 
1131         m_testCtx.getLog() << TestLog::Message << "Executing shader for " << inputs.size() << " input values"
1132                            << tcu::TestLog::EndMessage;
1133 
1134         {
1135             const void *in = &inputs[0];
1136             void *out      = &outputs[0];
1137 
1138             m_executor->useProgram();
1139             m_executor->execute((int)inputs.size(), &in, &out);
1140         }
1141 
1142         // Verify
1143         {
1144             const int numValues = (int)inputs.size();
1145             const int maxPrints = 10;
1146             int numFailed       = 0;
1147 
1148             for (int valNdx = 0; valNdx < (int)inputs.size(); valNdx++)
1149             {
1150                 const uint8_t in0 = (uint8_t)(inputs[valNdx] & 0xff);
1151                 const uint8_t in1 = (uint8_t)((inputs[valNdx] >> 8) & 0xff);
1152                 const uint8_t in2 = (uint8_t)((inputs[valNdx] >> 16) & 0xff);
1153                 const uint8_t in3 = (uint8_t)(inputs[valNdx] >> 24);
1154                 const float ref0  = de::clamp(float(in0) / 255.f, 0.0f, 1.0f);
1155                 const float ref1  = de::clamp(float(in1) / 255.f, 0.0f, 1.0f);
1156                 const float ref2  = de::clamp(float(in2) / 255.f, 0.0f, 1.0f);
1157                 const float ref3  = de::clamp(float(in3) / 255.f, 0.0f, 1.0f);
1158                 const float res0  = outputs[valNdx].x();
1159                 const float res1  = outputs[valNdx].y();
1160                 const float res2  = outputs[valNdx].z();
1161                 const float res3  = outputs[valNdx].w();
1162 
1163                 const uint32_t diff0 = getUlpDiff(ref0, res0);
1164                 const uint32_t diff1 = getUlpDiff(ref1, res1);
1165                 const uint32_t diff2 = getUlpDiff(ref2, res2);
1166                 const uint32_t diff3 = getUlpDiff(ref3, res3);
1167 
1168                 if (diff0 > maxDiff || diff1 > maxDiff || diff2 > maxDiff || diff3 > maxDiff)
1169                 {
1170                     if (numFailed < maxPrints)
1171                     {
1172                         m_testCtx.getLog() << TestLog::Message << "ERROR: Mismatch in value " << valNdx << ",\n"
1173                                            << "  expected unpackUnorm4x8(" << tcu::toHex(inputs[valNdx]) << ") = "
1174                                            << "vec4(" << HexFloat(ref0) << ", " << HexFloat(ref1) << ", "
1175                                            << HexFloat(ref2) << ", " << HexFloat(ref3) << ")"
1176                                            << ", got vec4(" << HexFloat(res0) << ", " << HexFloat(res1) << ", "
1177                                            << HexFloat(res2) << ", " << HexFloat(res3) << ")"
1178                                            << "\n  ULP diffs = (" << diff0 << ", " << diff1 << ", " << diff2 << ", "
1179                                            << diff3 << "), max diff = " << maxDiff << TestLog::EndMessage;
1180                     }
1181                     else if (numFailed == maxPrints)
1182                         m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
1183 
1184                     numFailed += 1;
1185                 }
1186             }
1187 
1188             m_testCtx.getLog() << TestLog::Message << (numValues - numFailed) << " / " << numValues << " values passed"
1189                                << TestLog::EndMessage;
1190 
1191             m_testCtx.setTestResult(numFailed == 0 ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
1192                                     numFailed == 0 ? "Pass" : "Result comparison failed");
1193         }
1194 
1195         return STOP;
1196     }
1197 };
1198 
ShaderPackingFunctionTests(Context & context)1199 ShaderPackingFunctionTests::ShaderPackingFunctionTests(Context &context)
1200     : TestCaseGroup(context, "pack_unpack", "Floating-point pack and unpack function tests")
1201 {
1202 }
1203 
~ShaderPackingFunctionTests(void)1204 ShaderPackingFunctionTests::~ShaderPackingFunctionTests(void)
1205 {
1206 }
1207 
init(void)1208 void ShaderPackingFunctionTests::init(void)
1209 {
1210     // New built-in functions in GLES 3.1
1211     {
1212         const glu::ShaderType allShaderTypes[] = {glu::SHADERTYPE_VERTEX,
1213                                                   glu::SHADERTYPE_TESSELLATION_CONTROL,
1214                                                   glu::SHADERTYPE_TESSELLATION_EVALUATION,
1215                                                   glu::SHADERTYPE_GEOMETRY,
1216                                                   glu::SHADERTYPE_FRAGMENT,
1217                                                   glu::SHADERTYPE_COMPUTE};
1218 
1219         // packSnorm4x8
1220         for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
1221         {
1222             for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1223                 addChild(new PackSnorm4x8Case(m_context, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1224         }
1225 
1226         // unpackSnorm4x8
1227         for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1228             addChild(new UnpackSnorm4x8Case(m_context, allShaderTypes[shaderTypeNdx]));
1229 
1230         // packUnorm4x8
1231         for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
1232         {
1233             for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1234                 addChild(new PackUnorm4x8Case(m_context, allShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1235         }
1236 
1237         // unpackUnorm4x8
1238         for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(allShaderTypes); shaderTypeNdx++)
1239             addChild(new UnpackUnorm4x8Case(m_context, allShaderTypes[shaderTypeNdx]));
1240     }
1241 
1242     // GLES 3 functions in new shader types.
1243     {
1244         const glu::ShaderType newShaderTypes[] = {glu::SHADERTYPE_GEOMETRY, glu::SHADERTYPE_COMPUTE};
1245 
1246         // packSnorm2x16
1247         for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
1248         {
1249             for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1250                 addChild(new PackSnorm2x16Case(m_context, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1251         }
1252 
1253         // unpackSnorm2x16
1254         for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1255             addChild(new UnpackSnorm2x16Case(m_context, newShaderTypes[shaderTypeNdx]));
1256 
1257         // packUnorm2x16
1258         for (int prec = 0; prec < glu::PRECISION_LAST; prec++)
1259         {
1260             for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1261                 addChild(new PackUnorm2x16Case(m_context, newShaderTypes[shaderTypeNdx], glu::Precision(prec)));
1262         }
1263 
1264         // unpackUnorm2x16
1265         for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1266             addChild(new UnpackUnorm2x16Case(m_context, newShaderTypes[shaderTypeNdx]));
1267 
1268         // packHalf2x16
1269         for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1270             addChild(new PackHalf2x16Case(m_context, newShaderTypes[shaderTypeNdx]));
1271 
1272         // unpackHalf2x16
1273         for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(newShaderTypes); shaderTypeNdx++)
1274             addChild(new UnpackHalf2x16Case(m_context, newShaderTypes[shaderTypeNdx]));
1275     }
1276 }
1277 
1278 } // namespace Functional
1279 } // namespace gles31
1280 } // namespace deqp
1281