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::string;
52 using std::vector;
53 using std::map;
54
55 using std::function;
56 using std::bind;
57 using namespace std::placeholders;
58
59 static const string defaultVertexShader =
60 "#version 300 es\n"
61 "in vec4 vPosition;\n"
62 "void main()\n"
63 "{\n"
64 " gl_Position = vPosition;\n"
65 "}\n";
66
67 static const string fragmentShaderTemplate =
68 "#version 300 es\n"
69 "precision highp float;\n"
70 "precision highp int;\n"
71 "out vec4 my_FragColor;\n"
72 "${TEST_GLOBALS}"
73 "void main()\n"
74 "{\n"
75 "${TEST_CODE}"
76 " my_FragColor = vec4(0.0, correct, 0.0, 1.0);\n"
77 "}\n";
78
79 typedef function<void (const glu::ShaderProgram&, const glw::Functions&)> SetupUniformsFn;
80
81 enum struct TestType
82 {
83 NORMAL = 0,
84 EXPECT_SHADER_FAIL
85 };
86
87 struct TestParams
88 {
89 TestType testType;
90 string name;
91 string description;
92 string testGlobals;
93 string testCode;
94 SetupUniformsFn setupUniformsFn;
95 };
96
97 static void initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value);
98 static void initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl);
99
100 static const TestParams tests[] =
101 {
102 {
103 TestType::NORMAL, // TestType testType
104 "unsigned_integer_above_signed_range_decimal", // string name
105 "Test that uint value higher than INT_MAX is parsed correctly", // string description
106 "uniform uint expected;\n", // string testGlobals
107 " uint i = 3221225472u;\n"
108 " float correct = (i == expected) ? 1.0 : 0.0;\n",
109 bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn setupUniformsFn
110 },
111 {
112 TestType::NORMAL, // TestType testType
113 "unsigned_integer_above_signed_range_base8", // string name
114 "Test that uint value higher than INT_MAX is parsed correctly in base 8 (octal)", // string description
115 "uniform uint expected;\n", // string testGlobals
116 " uint i = 030000000000u;\n"
117 " float correct = (i == expected) ? 1.0 : 0.0;\n",
118 bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn setupUniformsFn
119 },
120 {
121 TestType::NORMAL, // TestType testType
122 "unsigned_integer_above_signed_range_base16", // string name
123 "Test that uint value higher than INT_MAX is parsed correctly in base 16 (hex)", // string description
124 "uniform uint expected;\n", // string testGlobals
125 " uint i = 0xc0000000u;\n"
126 " float correct = (i == expected) ? 1.0 : 0.0;\n",
127 bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn setupUniformsFn
128 },
129 {
130 TestType::NORMAL, // TestType testType
131 "unsigned_integer_smallest_value_above_signed_range_decimal", // string name
132 "Test that uint value equal to INT_MAX+1 is parsed correctly", // string description
133 "uniform uint expected;\n", // string testGlobals
134 " uint i = 2147483648u;\n"
135 " float correct = (i == expected) ? 1.0 : 0.0;\n",
136 bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn setupUniformsFn
137 },
138 {
139 TestType::NORMAL, // TestType testType
140 "unsigned_integer_smallest_value_above_signed_range_base8", // string name
141 "Test that uint value equal to INT_MAX+1 is parsed correctly in base 8 (octal)", // string description
142 "uniform uint expected;\n", // string testGlobals
143 " uint i = 020000000000u;\n"
144 " float correct = (i == expected) ? 1.0 : 0.0;\n",
145 bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn setupUniformsFn
146 },
147 {
148 TestType::NORMAL, // TestType testType
149 "unsigned_integer_smallest_value_above_signed_range_base16", // string name
150 "Test that uint value equal to INT_MAX+1 is parsed correctly in base 16 (hex)", // string description
151 "uniform uint expected;\n", // string testGlobals
152 " uint i = 0x80000000u;\n"
153 " float correct = (i == expected) ? 1.0 : 0.0;\n",
154 bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn setupUniformsFn
155 },
156 {
157 TestType::NORMAL, // TestType testType
158 "unsigned_integer_max_value_decimal", // string name
159 "Test that uint value equal to UINT_MAX is parsed correctly", // string description
160 "uniform uint expected;\n", // string testGlobals
161 " uint i = 4294967295u;\n"
162 " float correct = (i == expected) ? 1.0 : 0.0;\n",
163 bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn setupUniformsFn
164 },
165 {
166 TestType::NORMAL, // TestType testType
167 "unsigned_integer_max_value_base8", // string name
168 "Test that uint value equal to UINT_MAX is parsed correctly in base 8 (octal)", // string description
169 "uniform uint expected;\n", // string testGlobals
170 " uint i = 037777777777u;\n"
171 " float correct = (i == expected) ? 1.0 : 0.0;\n",
172 bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn setupUniformsFn
173 },
174 {
175 TestType::NORMAL, // TestType testType
176 "unsigned_integer_max_value_base16", // string name
177 "Test that uint value equal to UINT_MAX is parsed correctly in base 16 (hex)", // string description
178 "uniform uint expected;\n", // string testGlobals
179 " uint i = 0xffffffffu;\n"
180 " float correct = (i == expected) ? 1.0 : 0.0;\n",
181 bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn setupUniformsFn
182 },
183 {
184 TestType::EXPECT_SHADER_FAIL, // TestType testType
185 "unsigned_integer_too_large_value_invalid", // string name
186 "Test that uint value outside uint range fails to compile", // string description
187 "", // string testGlobals
188 " uint i = 0xfffffffffu;"
189 " float correct = 0.0;",
190 nullptr // SetupUniformsFn setupUniformsFn
191 },
192 {
193 TestType::NORMAL, // TestType testType
194 "unsigned_integer_negative_value_as_uint", // string name
195 "Test that -1u is parsed correctly", // string description
196 "uniform uint expected;\n", // string testGlobals
197 " uint i = -1u;"
198 " float correct = (i == expected) ? 1.0 : 0.0;\n",
199 bind(initializeExpectedValue, _1, _2, 0xffffffffu) // SetupUniformsFn setupUniformsFn
200 },
201 /* The following floating point parsing tests are taken from the Khronos WebGL conformance tests at:
202 * https://www.khronos.org/registry/webgl/sdk/tests/conformance2/glsl3/float-parsing.html */
203 {
204 TestType::NORMAL, // TestType testType
205 "float_out_of_range_as_infinity", // string name
206 "Floats of too large magnitude should be converted infinity", // string description
207 "", // string testGlobals
208 " // Out-of-range floats should overflow to infinity\n" // string testCode
209 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
210 " // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
211 " float correct = isinf(1.0e40) ? 1.0 : 0.0;\n",
212 nullptr // SetupUniformsFn setupUniformsFn
213 },
214 {
215 TestType::NORMAL, // TestType testType
216 "float_out_of_range_as_zero", // string name
217 "Floats of too small magnitude should be converted to zero", // string description
218 "", // string testGlobals
219 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n" // string testCode
220 " // \"A value with a magnitude too small to be represented as a mantissa and exponent is converted to zero.\"\n"
221 " // 1.0e-50 is small enough that it can't even be stored as subnormal.\n"
222 " float correct = (1.0e-50 == 0.0) ? 1.0 : 0.0;\n",
223 nullptr // SetupUniformsFn setupUniformsFn
224 },
225 {
226 TestType::NORMAL, // TestType testType
227 "float_no_limit_on_number_of_digits_positive_exponent", // string name
228 "Number of digits in any digit-sequence is not limited - test with a small mantissa and large exponent", // string description
229 "", // string testGlobals
230 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n" // string testCode
231 " // \"There is no limit on the number of digits in any digit-sequence.\"\n"
232 " // The below float string has 100 zeros after the decimal point, but represents 1.0.\n"
233 " float x = 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 = 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0e-100;\n"
246 " float correct = (x == 1.0) ? 1.0 : 0.0;\n",
247 nullptr // SetupUniformsFn setupUniformsFn
248 },
249 {
250 TestType::NORMAL, // TestType testType
251 "float_slightly_out_of_range_exponent_as_positive_infinity", // string name
252 "Test that an exponent that slightly overflows signed 32-bit int range works", // string description
253 "", // string testGlobals
254 " // Out-of-range floats should overflow to infinity\n" // string testCode
255 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
256 " // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
257 " float correct = isinf(1.0e2147483649) ? 1.0 : 0.0;\n",
258 nullptr // SetupUniformsFn setupUniformsFn
259 },
260 {
261 TestType::NORMAL, // TestType testType
262 "float_overflow_to_positive_infinity", // string name
263 "Out-of-range floats greater than zero should overflow to positive infinity", // string description
264 "uniform float zero;\n", // string testGlobals
265 " // Out-of-range floats should overflow to infinity\n" // string testCode
266 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
267 " // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
268 " float f = 1.0e2048 - zero;\n"
269 " float correct = (isinf(f) && f > 0.0) ? 1.0 : 0.0;\n",
270 initializeZeroValue // SetupUniformsFn setupUniformsFn
271 },
272 {
273 TestType::NORMAL, // TestType testType
274 "float_overflow_to_negative_infinity", // string name
275 "Out-of-range floats less than zero should overflow to negative infinity", // string description
276 "uniform float zero;\n", // string testGlobals
277 " // Out-of-range floats should overflow to infinity\n" // string testCode
278 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
279 " // \"If the value of the floating point number is too large (small) to be stored as a single precision value, it is converted to positive (negative) infinity\"\n"
280 " float f = -1.0e2048 + zero;\n"
281 " float correct = (isinf(f) && f < 0.0) ? 1.0 : 0.0;\n",
282 initializeZeroValue // SetupUniformsFn setupUniformsFn
283 }
284 };
285
initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value)286 static void initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value)
287 {
288 const auto location = gl.getUniformLocation(program.getProgram(), "expected");
289 GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
290
291 gl.uniform1ui(location, value);
292 GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
293 }
294
initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl)295 static void initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl)
296 {
297 const auto location = gl.getUniformLocation(program.getProgram(), "zero");
298 GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
299
300 gl.uniform1f(location, 0.0f);
301 GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
302 }
303
replacePlaceholders(const string& shaderTemplate, const TestParams& params)304 static string replacePlaceholders(const string& shaderTemplate, const TestParams& params)
305 {
306 map<string,string> fields;
307 fields["TEST_GLOBALS"] = params.testGlobals;
308 fields["TEST_CODE"] = params.testCode;
309
310 tcu::StringTemplate output(shaderTemplate);
311 return output.specialize(fields);
312 }
313
314 static const std::vector<float> positions =
315 {
316 -1.0f, -1.0f,
317 1.0f, -1.0f,
318 -1.0f, 1.0f,
319 1.0f, 1.0f
320 };
321
322 static const std::vector<deUint32> indices = { 0, 1, 2, 3 };
323
324 const deInt32 RENDERTARGET_WIDTH = 16;
325 const deInt32 RENDERTARGET_HEIGHT = 16;
326
327 class NumberParsingCase : public deqp::TestCase
328 {
329 public:
330 NumberParsingCase(deqp::Context& context, const string& name, const TestParams& params, const string& vertexShader, const string& fragmentShader);
331
332 IterateResult iterate();
333
334 private:
335 void setupRenderTarget();
336 void releaseRenderTarget();
337
338 glw::GLuint m_fboId;
339 glw::GLuint m_rboId;
340
341 const TestParams& m_params;
342 string m_vertexShader;
343 string m_fragmentShader;
344 };
345
NumberParsingCase(deqp::Context& context, const string& name, const TestParams& params, const string& vertexShader, const string& fragmentShader)346 NumberParsingCase::NumberParsingCase(deqp::Context& context, const string& name, const TestParams& params, 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" + m_fragmentShader);
374 }
375
376 const std::vector<glu::VertexArrayBinding> vertexArrays =
377 {
378 glu::va::Float("vPosition", 2, 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
390 glu::draw(renderContext, program.getProgram(),
391 static_cast<int>(vertexArrays.size()), vertexArrays.data(),
392 glu::pr::TriangleStrip(static_cast<int>(indices.size()), indices.data()));
393
394 const auto pixelSize = tcu::getPixelSize(textureFormat);
395 std::vector<deUint8> fbData (RENDERTARGET_WIDTH * RENDERTARGET_HEIGHT * pixelSize);
396
397 if (pixelSize < 4)
398 gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
399
400 gl.readPixels(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, transferFormat.format, transferFormat.dataType, fbData.data());
401 GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
402
403 tcu::ConstPixelBufferAccess fbAccess { textureFormat, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, 1, fbData.data() };
404 const auto expectedColor = tcu::RGBA::green().toVec();
405 bool pass = true;
406 for(int y = 0; pass && y < RENDERTARGET_HEIGHT; ++y)
407 for(int x = 0; x < RENDERTARGET_WIDTH; ++x)
408 if (fbAccess.getPixel(x,y) != expectedColor)
409 {
410 pass = false;
411 break;
412 }
413
414 releaseRenderTarget();
415
416 const qpTestResult result = (pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL);
417 const char* desc = (pass ? "Pass" : "Pixel mismatch; numeric value parsed incorrectly");
418
419 m_testCtx.setTestResult(result, desc);
420
421 return STOP;
422 }
423
setupRenderTarget()424 void NumberParsingCase::setupRenderTarget()
425 {
426 const auto& renderContext = m_context.getRenderContext();
427 const auto& gl = renderContext.getFunctions();
428
429 gl.genFramebuffers(1, &m_fboId);
430 GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
431
432 gl.genRenderbuffers(1, &m_rboId);
433 GLU_EXPECT_NO_ERROR(gl.getError(), "GenRenderBuffers");
434
435 gl.bindRenderbuffer(GL_RENDERBUFFER, m_rboId);
436 GLU_EXPECT_NO_ERROR(gl.getError(), "BindRenderBuffer");
437
438 gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
439 GLU_EXPECT_NO_ERROR(gl.getError(), "RenderBufferStorage");
440
441 gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboId);
442 GLU_EXPECT_NO_ERROR(gl.getError(), "BindFrameBuffer");
443
444 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rboId);
445 GLU_EXPECT_NO_ERROR(gl.getError(), "FrameBufferRenderBuffer");
446
447 glw::GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
448 gl.drawBuffers(1, &drawBuffer);
449 GLU_EXPECT_NO_ERROR(gl.getError(), "DrawBuffers");
450
451 glw::GLfloat clearColor[4] = { 0, 0, 0, 0 };
452 gl.clearBufferfv(GL_COLOR, 0, clearColor);
453 GLU_EXPECT_NO_ERROR(gl.getError(), "ClearBuffers");
454
455 gl.viewport(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT);
456 GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
457 }
458
releaseRenderTarget()459 void NumberParsingCase::releaseRenderTarget()
460 {
461 const auto& renderContext = m_context.getRenderContext();
462 const auto& gl = renderContext.getFunctions();
463 if (m_fboId != 0)
464 {
465 gl.deleteFramebuffers(1, &m_fboId);
466 m_fboId = 0;
467 }
468 if (m_rboId != 0)
469 {
470 gl.deleteRenderbuffers(1, &m_rboId);
471 m_rboId = 0;
472 }
473 }
474
475 }
476
NumberParsingTests(deqp::Context& context)477 NumberParsingTests::NumberParsingTests(deqp::Context& context)
478 : deqp::TestCaseGroup(context, "number_parsing", "GLSL number parsing tests")
479 {
480 }
481
~NumberParsingTests(void)482 NumberParsingTests::~NumberParsingTests(void)
483 {
484 }
485
init(void)486 void NumberParsingTests::init(void)
487 {
488 for(const auto& params : tests)
489 {
490 addChild(new NumberParsingCase(m_context, params.name, params, defaultVertexShader, replacePlaceholders(fragmentShaderTemplate, params)));
491 }
492 }
493
494 }
495