xref: /aosp_15_r20/external/deqp/external/openglcts/modules/gles3/es3cNumberParsingTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2020 Google Inc.
6  * Copyright (c) 2020 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */ /*!
21  * \file  es3cNumberParsingTests.cpp
22  * \brief Tests for numeric value parsing in GLSL ES 3.0
23  */ /*-------------------------------------------------------------------*/
24 
25 #include "es3cNumberParsingTests.hpp"
26 
27 #include "gluDefs.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "gluDrawUtil.hpp"
30 #include "gluShaderProgram.hpp"
31 
32 #include "glwDefs.hpp"
33 #include "glwFunctions.hpp"
34 #include "glwEnums.hpp"
35 
36 #include "tcuTestLog.hpp"
37 #include "tcuRenderTarget.hpp"
38 #include "tcuStringTemplate.hpp"
39 
40 #include <string>
41 #include <vector>
42 #include <map>
43 
44 #include <functional>
45 
46 namespace es3cts
47 {
48 
49 namespace
50 {
51 using std::map;
52 using std::string;
53 using std::vector;
54 
55 using std::bind;
56 using std::function;
57 using namespace std::placeholders;
58 
59 static const string defaultVertexShader = "#version 300 es\n"
60                                           "in vec4 vPosition;\n"
61                                           "void main()\n"
62                                           "{\n"
63                                           "    gl_Position = vPosition;\n"
64                                           "}\n";
65 
66 static const string fragmentShaderTemplate = "#version 300 es\n"
67                                              "precision highp float;\n"
68                                              "precision highp int;\n"
69                                              "out vec4 my_FragColor;\n"
70                                              "${TEST_GLOBALS}"
71                                              "void main()\n"
72                                              "{\n"
73                                              "${TEST_CODE}"
74                                              "    my_FragColor = vec4(0.0, correct, 0.0, 1.0);\n"
75                                              "}\n";
76 
77 typedef function<void(const glu::ShaderProgram &, const glw::Functions &)> SetupUniformsFn;
78 
79 enum struct TestType
80 {
81     NORMAL = 0,
82     EXPECT_SHADER_FAIL
83 };
84 
85 struct TestParams
86 {
87     TestType testType;
88     string name;
89     string description;
90     string testGlobals;
91     string testCode;
92     SetupUniformsFn setupUniformsFn;
93 };
94 
95 static void initializeExpectedValue(const glu::ShaderProgram &program, const glw::Functions &gl, const uint32_t value);
96 static void initializeZeroValue(const glu::ShaderProgram &program, const glw::Functions &gl);
97 
98 static const TestParams tests[] = {
99     {
100         TestType::NORMAL,                                               // TestType            testType
101         "unsigned_integer_above_signed_range_decimal",                  // string            name
102         "Test that uint value higher than INT_MAX is parsed correctly", // string            description
103         "uniform uint expected;\n",                                     // string            testGlobals
104         "    uint i        = 3221225472u;\n"
105         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
106         bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn    setupUniformsFn
107     },
108     {
109         TestType::NORMAL,                            // TestType            testType
110         "unsigned_integer_above_signed_range_base8", // string            name
111         "Test that uint value higher than INT_MAX is parsed correctly in base 8 (octal)", // string            description
112         "uniform uint expected;\n", // string            testGlobals
113         "    uint i        = 030000000000u;\n"
114         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
115         bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn    setupUniformsFn
116     },
117     {
118         TestType::NORMAL,                                                                // TestType            testType
119         "unsigned_integer_above_signed_range_base16",                                    // string            name
120         "Test that uint value higher than INT_MAX is parsed correctly in base 16 (hex)", // string            description
121         "uniform uint expected;\n", // string            testGlobals
122         "    uint i        = 0xc0000000u;\n"
123         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
124         bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn    setupUniformsFn
125     },
126     {
127         TestType::NORMAL,                                              // TestType            testType
128         "unsigned_integer_smallest_value_above_signed_range_decimal",  // string            name
129         "Test that uint value equal to INT_MAX+1 is parsed correctly", // string            description
130         "uniform uint expected;\n",                                    // string            testGlobals
131         "    uint i        = 2147483648u;\n"
132         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
133         bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn    setupUniformsFn
134     },
135     {
136         TestType::NORMAL,                                                                // TestType            testType
137         "unsigned_integer_smallest_value_above_signed_range_base8",                      // string            name
138         "Test that uint value equal to INT_MAX+1 is parsed correctly in base 8 (octal)", // string            description
139         "uniform uint expected;\n", // string            testGlobals
140         "    uint i        = 020000000000u;\n"
141         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
142         bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn    setupUniformsFn
143     },
144     {
145         TestType::NORMAL,                                                               // TestType            testType
146         "unsigned_integer_smallest_value_above_signed_range_base16",                    // string            name
147         "Test that uint value equal to INT_MAX+1 is parsed correctly in base 16 (hex)", // string            description
148         "uniform uint expected;\n",                                                     // string            testGlobals
149         "    uint i        = 0x80000000u;\n"
150         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
151         bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn    setupUniformsFn
152     },
153     {
154         TestType::NORMAL,                                             // TestType            testType
155         "unsigned_integer_max_value_decimal",                         // string            name
156         "Test that uint value equal to UINT_MAX is parsed correctly", // string            description
157         "uniform uint expected;\n",                                   // string            testGlobals
158         "    uint i        = 4294967295u;\n"
159         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
160         bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn    setupUniformsFn
161     },
162     {
163         TestType::NORMAL,                                                               // TestType            testType
164         "unsigned_integer_max_value_base8",                                             // string            name
165         "Test that uint value equal to UINT_MAX is parsed correctly in base 8 (octal)", // string            description
166         "uniform uint expected;\n",                                                     // string            testGlobals
167         "    uint i        = 037777777777u;\n"
168         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
169         bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn    setupUniformsFn
170     },
171     {
172         TestType::NORMAL,                                                              // TestType            testType
173         "unsigned_integer_max_value_base16",                                           // string            name
174         "Test that uint value equal to UINT_MAX is parsed correctly in base 16 (hex)", // string            description
175         "uniform uint expected;\n",                                                    // string            testGlobals
176         "    uint i        = 0xffffffffu;\n"
177         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
178         bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn    setupUniformsFn
179     },
180     {
181         TestType::EXPECT_SHADER_FAIL,                               // TestType            testType
182         "unsigned_integer_too_large_value_invalid",                 // string            name
183         "Test that uint value outside uint range fails to compile", // string            description
184         "",                                                         // string            testGlobals
185         "    uint i        = 0xfffffffffu;"
186         "    float correct = 0.0;",
187         nullptr // SetupUniformsFn    setupUniformsFn
188     },
189     {
190         TestType::NORMAL,                          // TestType            testType
191         "unsigned_integer_negative_value_as_uint", // string            name
192         "Test that -1u is parsed correctly",       // string            description
193         "uniform uint expected;\n",                // string            testGlobals
194         "    uint i        = -1u;"
195         "    float correct = (i == expected) ? 1.0 : 0.0;\n",
196         bind(initializeExpectedValue, _1, _2, 0xffffffffu) // SetupUniformsFn    setupUniformsFn
197     },
198     /* The following floating point parsing tests are taken from the Khronos WebGL conformance tests at:
199      *     https://www.khronos.org/registry/webgl/sdk/tests/conformance2/glsl3/float-parsing.html */
200     {
201         TestType::NORMAL,                                             // TestType            testType
202         "float_out_of_range_as_infinity",                             // string            name
203         "Floats of too large magnitude should be converted infinity", // string            description
204         "",                                                           // string            testGlobals
205         "    // Out-of-range floats should overflow to infinity\n"    // string            testCode
206         "    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
207         "    // \"If the value of the floating point number is too large (small) to be stored as a single precision "
208         "value, it is converted to positive (negative) infinity\"\n"
209         "    float correct = isinf(1.0e40) ? 1.0 : 0.0;\n",
210         nullptr // SetupUniformsFn    setupUniformsFn
211     },
212     {
213         TestType::NORMAL,                                            // TestType            testType
214         "float_out_of_range_as_zero",                                // string            name
215         "Floats of too small magnitude should be converted to zero", // string            description
216         "",                                                          // string            testGlobals
217         "    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"              // string            testCode
218         "    // \"A value with a magnitude too small to be represented as a mantissa and exponent is converted to "
219         "zero.\"\n"
220         "    // 1.0e-50 is small enough that it can't even be stored as subnormal.\n"
221         "    float correct = (1.0e-50 == 0.0) ? 1.0 : 0.0;\n",
222         nullptr // SetupUniformsFn    setupUniformsFn
223     },
224     {
225         TestType::NORMAL,                                       // TestType            testType
226         "float_no_limit_on_number_of_digits_positive_exponent", // string            name
227         "Number of digits in any digit-sequence is not limited - test with a small mantissa and large exponent", // string            description
228         "",                                             // string            testGlobals
229         "    // GLSL ES 3.00.6 section 4.1.4 Floats:\n" // string            testCode
230         "    // \"There is no limit on the number of digits in any digit-sequence.\"\n"
231         "    // The below float string has 100 zeros after the decimal point, but represents 1.0.\n"
232         "    float x = "
233         "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e101;\n"
234         "    float correct = (x == 1.0) ? 1.0 : 0.0;\n",
235         nullptr // SetupUniformsFn    setupUniformsFn
236     },
237     {
238         TestType::NORMAL,                                       // TestType            testType
239         "float_no_limit_on_number_of_digits_negative_exponent", // string            name
240         "Number of digits in any digit-sequence is not limited - test with a large mantissa and negative exponent", // string            description
241         "",                                             // string            testGlobals
242         "    // GLSL ES 3.00.6 section 4.1.4 Floats:\n" // string            testCode
243         "    // \"There is no limit on the number of digits in any digit-sequence.\"\n"
244         "    // The below float string has 100 zeros, but represents 1.0.\n"
245         "    float x = "
246         "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0e-100;"
247         "\n"
248         "    float correct = (x == 1.0) ? 1.0 : 0.0;\n",
249         nullptr // SetupUniformsFn    setupUniformsFn
250     },
251     {
252         TestType::NORMAL,                                                              // TestType            testType
253         "float_slightly_out_of_range_exponent_as_positive_infinity",                   // string            name
254         "Test that an exponent that slightly overflows signed 32-bit int range works", // string            description
255         "",                                                                            // string            testGlobals
256         "    // Out-of-range floats should overflow to infinity\n"                     // string            testCode
257         "    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
258         "    // \"If the value of the floating point number is too large (small) to be stored as a single precision "
259         "value, it is converted to positive (negative) infinity\"\n"
260         "    float correct = isinf(1.0e2147483649) ? 1.0 : 0.0;\n",
261         nullptr // SetupUniformsFn    setupUniformsFn
262     },
263     {
264         TestType::NORMAL,                                                             // TestType            testType
265         "float_overflow_to_positive_infinity",                                        // string            name
266         "Out-of-range floats greater than zero should overflow to positive infinity", // string            description
267         "uniform float zero;\n",                                                      // string            testGlobals
268         "    // Out-of-range floats should overflow to infinity\n"                    // string            testCode
269         "    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
270         "    // \"If the value of the floating point number is too large (small) to be stored as a single precision "
271         "value, it is converted to positive (negative) infinity\"\n"
272         "    float f = 1.0e2048 - zero;\n"
273         "    float correct = (isinf(f) && f > 0.0) ? 1.0 : 0.0;\n",
274         initializeZeroValue // SetupUniformsFn    setupUniformsFn
275     },
276     {
277         TestType::NORMAL,                                                          // TestType            testType
278         "float_overflow_to_negative_infinity",                                     // string            name
279         "Out-of-range floats less than zero should overflow to negative infinity", // string            description
280         "uniform float zero;\n",                                                   // string            testGlobals
281         "    // Out-of-range floats should overflow to infinity\n"                 // string            testCode
282         "    // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
283         "    // \"If the value of the floating point number is too large (small) to be stored as a single precision "
284         "value, it is converted to positive (negative) infinity\"\n"
285         "    float f = -1.0e2048 + zero;\n"
286         "    float correct = (isinf(f) && f < 0.0) ? 1.0 : 0.0;\n",
287         initializeZeroValue // SetupUniformsFn    setupUniformsFn
288     }};
289 
initializeExpectedValue(const glu::ShaderProgram & program,const glw::Functions & gl,const uint32_t value)290 static void initializeExpectedValue(const glu::ShaderProgram &program, const glw::Functions &gl, const uint32_t value)
291 {
292     const auto location = gl.getUniformLocation(program.getProgram(), "expected");
293     GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
294 
295     gl.uniform1ui(location, value);
296     GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
297 }
298 
initializeZeroValue(const glu::ShaderProgram & program,const glw::Functions & gl)299 static void initializeZeroValue(const glu::ShaderProgram &program, const glw::Functions &gl)
300 {
301     const auto location = gl.getUniformLocation(program.getProgram(), "zero");
302     GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
303 
304     gl.uniform1f(location, 0.0f);
305     GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
306 }
307 
replacePlaceholders(const string & shaderTemplate,const TestParams & params)308 static string replacePlaceholders(const string &shaderTemplate, const TestParams &params)
309 {
310     map<string, string> fields;
311     fields["TEST_GLOBALS"] = params.testGlobals;
312     fields["TEST_CODE"]    = params.testCode;
313 
314     tcu::StringTemplate output(shaderTemplate);
315     return output.specialize(fields);
316 }
317 
318 static const std::vector<float> positions = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
319 
320 static const std::vector<uint32_t> indices = {0, 1, 2, 3};
321 
322 const int32_t RENDERTARGET_WIDTH  = 16;
323 const int32_t RENDERTARGET_HEIGHT = 16;
324 
325 class NumberParsingCase : public deqp::TestCase
326 {
327 public:
328     NumberParsingCase(deqp::Context &context, const string &name, const TestParams &params, const string &vertexShader,
329                       const string &fragmentShader);
330 
331     IterateResult iterate();
332 
333 private:
334     void setupRenderTarget();
335     void releaseRenderTarget();
336 
337     glw::GLuint m_fboId;
338     glw::GLuint m_rboId;
339 
340     const TestParams &m_params;
341     string m_vertexShader;
342     string m_fragmentShader;
343 };
344 
NumberParsingCase(deqp::Context & context,const string & name,const TestParams & params,const string & vertexShader,const string & fragmentShader)345 NumberParsingCase::NumberParsingCase(deqp::Context &context, const string &name, const TestParams &params,
346                                      const string &vertexShader, const string &fragmentShader)
347     : TestCase(context, name.c_str(), params.description.c_str())
348     , m_fboId(0)
349     , m_rboId(0)
350     , m_params(params)
351     , m_vertexShader(vertexShader)
352     , m_fragmentShader(fragmentShader)
353 {
354 }
355 
iterate(void)356 NumberParsingCase::IterateResult NumberParsingCase::iterate(void)
357 {
358     const auto &renderContext = m_context.getRenderContext();
359     const auto &gl            = renderContext.getFunctions();
360     const auto textureFormat  = tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8);
361     const auto transferFormat = glu::getTransferFormat(textureFormat);
362 
363     setupRenderTarget();
364 
365     glu::ShaderProgram program(renderContext, glu::makeVtxFragSources(m_vertexShader, m_fragmentShader));
366     if (!program.isOk())
367         switch (m_params.testType)
368         {
369         case TestType::EXPECT_SHADER_FAIL:
370             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
371             return STOP;
372         default:
373             TCU_FAIL("Shader compilation failed:\nVertex shader:\n" + m_vertexShader + "\nFragment shader:\n" +
374                      m_fragmentShader);
375         }
376 
377     const std::vector<glu::VertexArrayBinding> vertexArrays = {
378         glu::va::Float("vPosition", 2, (int)positions.size(), 0, positions.data()),
379     };
380 
381     gl.useProgram(program.getProgram());
382     GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram failed");
383 
384     if (m_params.setupUniformsFn != DE_NULL)
385         m_params.setupUniformsFn(program, gl);
386 
387     gl.clear(GL_COLOR_BUFFER_BIT);
388 
389     glu::draw(renderContext, program.getProgram(), static_cast<int>(vertexArrays.size()), vertexArrays.data(),
390               glu::pr::TriangleStrip(static_cast<int>(indices.size()), indices.data()));
391 
392     const auto pixelSize = tcu::getPixelSize(textureFormat);
393     std::vector<uint8_t> fbData(RENDERTARGET_WIDTH * RENDERTARGET_HEIGHT * pixelSize);
394 
395     if (pixelSize < 4)
396         gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
397 
398     gl.readPixels(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, transferFormat.format, transferFormat.dataType,
399                   fbData.data());
400     GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
401 
402     tcu::ConstPixelBufferAccess fbAccess{textureFormat, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, 1, fbData.data()};
403     const auto expectedColor = tcu::RGBA::green().toVec();
404     bool pass                = true;
405     for (int y = 0; pass && y < RENDERTARGET_HEIGHT; ++y)
406         for (int x = 0; x < RENDERTARGET_WIDTH; ++x)
407             if (fbAccess.getPixel(x, y) != expectedColor)
408             {
409                 pass = false;
410                 break;
411             }
412 
413     releaseRenderTarget();
414 
415     const qpTestResult result = (pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL);
416     const char *desc          = (pass ? "Pass" : "Pixel mismatch; numeric value parsed incorrectly");
417 
418     m_testCtx.setTestResult(result, desc);
419 
420     return STOP;
421 }
422 
setupRenderTarget()423 void NumberParsingCase::setupRenderTarget()
424 {
425     const auto &renderContext = m_context.getRenderContext();
426     const auto &gl            = renderContext.getFunctions();
427 
428     gl.genFramebuffers(1, &m_fboId);
429     GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
430 
431     gl.genRenderbuffers(1, &m_rboId);
432     GLU_EXPECT_NO_ERROR(gl.getError(), "GenRenderBuffers");
433 
434     gl.bindRenderbuffer(GL_RENDERBUFFER, m_rboId);
435     GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderBuffer");
436 
437     gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
438     GLU_EXPECT_NO_ERROR(gl.getError(), "RenderBufferStorage");
439 
440     gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboId);
441     GLU_EXPECT_NO_ERROR(gl.getError(), "BindFrameBuffer");
442 
443     gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rboId);
444     GLU_EXPECT_NO_ERROR(gl.getError(), "FrameBufferRenderBuffer");
445 
446     glw::GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
447     gl.drawBuffers(1, &drawBuffer);
448     GLU_EXPECT_NO_ERROR(gl.getError(), "DrawBuffers");
449 
450     glw::GLfloat clearColor[4] = {0, 0, 0, 0};
451     gl.clearBufferfv(GL_COLOR, 0, clearColor);
452     GLU_EXPECT_NO_ERROR(gl.getError(), "ClearBuffers");
453 
454     gl.viewport(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
455     GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
456 }
457 
releaseRenderTarget()458 void NumberParsingCase::releaseRenderTarget()
459 {
460     const auto &renderContext = m_context.getRenderContext();
461     const auto &gl            = renderContext.getFunctions();
462     if (m_fboId != 0)
463     {
464         gl.deleteFramebuffers(1, &m_fboId);
465         m_fboId = 0;
466     }
467     if (m_rboId != 0)
468     {
469         gl.deleteRenderbuffers(1, &m_rboId);
470         m_rboId = 0;
471     }
472 }
473 
474 } // namespace
475 
NumberParsingTests(deqp::Context & context)476 NumberParsingTests::NumberParsingTests(deqp::Context &context)
477     : deqp::TestCaseGroup(context, "number_parsing", "GLSL number parsing tests")
478 {
479 }
480 
~NumberParsingTests(void)481 NumberParsingTests::~NumberParsingTests(void)
482 {
483 }
484 
init(void)485 void NumberParsingTests::init(void)
486 {
487     for (const auto &params : tests)
488     {
489         addChild(new NumberParsingCase(m_context, params.name, params, defaultVertexShader,
490                                        replacePlaceholders(fragmentShaderTemplate, params)));
491     }
492 }
493 
494 } // namespace es3cts
495