xref: /aosp_15_r20/external/deqp/modules/gles3/functional/es3fShaderPackingFunctionTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Floating-point packing and unpacking function tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3fShaderPackingFunctionTests.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 gles3
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::gles3::Functional::__anone8a9cc660111::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::GLSL_VERSION_300_ES;
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 
ShaderPackingFunctionTests(Context & context)751 ShaderPackingFunctionTests::ShaderPackingFunctionTests(Context &context)
752     : TestCaseGroup(context, "pack_unpack", "Floating-point pack and unpack function tests")
753 {
754 }
755 
~ShaderPackingFunctionTests(void)756 ShaderPackingFunctionTests::~ShaderPackingFunctionTests(void)
757 {
758 }
759 
init(void)760 void ShaderPackingFunctionTests::init(void)
761 {
762     addChild(new PackSnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX, glu::PRECISION_LOWP));
763     addChild(new PackSnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT, glu::PRECISION_LOWP));
764     addChild(new PackSnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX, glu::PRECISION_MEDIUMP));
765     addChild(new PackSnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT, glu::PRECISION_MEDIUMP));
766     addChild(new PackSnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX, glu::PRECISION_HIGHP));
767     addChild(new PackSnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT, glu::PRECISION_HIGHP));
768 
769     addChild(new UnpackSnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX));
770     addChild(new UnpackSnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
771 
772     addChild(new PackUnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX, glu::PRECISION_LOWP));
773     addChild(new PackUnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT, glu::PRECISION_LOWP));
774     addChild(new PackUnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX, glu::PRECISION_MEDIUMP));
775     addChild(new PackUnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT, glu::PRECISION_MEDIUMP));
776     addChild(new PackUnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX, glu::PRECISION_HIGHP));
777     addChild(new PackUnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT, glu::PRECISION_HIGHP));
778 
779     addChild(new UnpackUnorm2x16Case(m_context, glu::SHADERTYPE_VERTEX));
780     addChild(new UnpackUnorm2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
781 
782     addChild(new PackHalf2x16Case(m_context, glu::SHADERTYPE_VERTEX));
783     addChild(new PackHalf2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
784 
785     addChild(new UnpackHalf2x16Case(m_context, glu::SHADERTYPE_VERTEX));
786     addChild(new UnpackHalf2x16Case(m_context, glu::SHADERTYPE_FRAGMENT));
787 }
788 
789 } // namespace Functional
790 } // namespace gles3
791 } // namespace deqp
792