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 ¶ms)
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 ¶ms, 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 ¶ms,
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 ¶ms : tests)
488 {
489 addChild(new NumberParsingCase(m_context, params.name, params, defaultVertexShader,
490 replacePlaceholders(fragmentShaderTemplate, params)));
491 }
492 }
493
494 } // namespace es3cts
495