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 "out vec4 my_FragColor;\n"
71 "${TEST_GLOBALS}"
72 "void main()\n"
73 "{\n"
74 "${TEST_CODE}"
75 " my_FragColor = vec4(0.0, correct, 0.0, 1.0);\n"
76 "}\n";
77
78 typedef function<void (const glu::ShaderProgram&, const glw::Functions&)> SetupUniformsFn;
79
80 enum struct TestType
81 {
82 NORMAL = 0,
83 EXPECT_SHADER_FAIL
84 };
85
86 struct TestParams
87 {
88 TestType testType;
89 string name;
90 string description;
91 string testGlobals;
92 string testCode;
93 SetupUniformsFn setupUniformsFn;
94 };
95
96 static void initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value);
97 static void initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl);
98
99 static const TestParams tests[] =
100 {
101 {
102 TestType::NORMAL, // TestType testType
103 "unsigned_integer_above_signed_range_decimal", // string name
104 "Test that uint value higher than INT_MAX is parsed correctly", // string description
105 "uniform uint expected;\n", // string testGlobals
106 " uint i = 3221225472u;\n"
107 " float correct = (i == expected) ? 1.0 : 0.0;\n",
108 bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn setupUniformsFn
109 },
110 {
111 TestType::NORMAL, // TestType testType
112 "unsigned_integer_above_signed_range_base8", // string name
113 "Test that uint value higher than INT_MAX is parsed correctly in base 8 (octal)", // string description
114 "uniform uint expected;\n", // string testGlobals
115 " uint i = 030000000000u;\n"
116 " float correct = (i == expected) ? 1.0 : 0.0;\n",
117 bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn setupUniformsFn
118 },
119 {
120 TestType::NORMAL, // TestType testType
121 "unsigned_integer_above_signed_range_base16", // string name
122 "Test that uint value higher than INT_MAX is parsed correctly in base 16 (hex)", // string description
123 "uniform uint expected;\n", // string testGlobals
124 " uint i = 0xc0000000u;\n"
125 " float correct = (i == expected) ? 1.0 : 0.0;\n",
126 bind(initializeExpectedValue, _1, _2, 3221225472u) // SetupUniformsFn setupUniformsFn
127 },
128 {
129 TestType::NORMAL, // TestType testType
130 "unsigned_integer_smallest_value_above_signed_range_decimal", // string name
131 "Test that uint value equal to INT_MAX+1 is parsed correctly", // string description
132 "uniform uint expected;\n", // string testGlobals
133 " uint i = 2147483648u;\n"
134 " float correct = (i == expected) ? 1.0 : 0.0;\n",
135 bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn setupUniformsFn
136 },
137 {
138 TestType::NORMAL, // TestType testType
139 "unsigned_integer_smallest_value_above_signed_range_base8", // string name
140 "Test that uint value equal to INT_MAX+1 is parsed correctly in base 8 (octal)", // string description
141 "uniform uint expected;\n", // string testGlobals
142 " uint i = 020000000000u;\n"
143 " float correct = (i == expected) ? 1.0 : 0.0;\n",
144 bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn setupUniformsFn
145 },
146 {
147 TestType::NORMAL, // TestType testType
148 "unsigned_integer_smallest_value_above_signed_range_base16", // string name
149 "Test that uint value equal to INT_MAX+1 is parsed correctly in base 16 (hex)", // string description
150 "uniform uint expected;\n", // string testGlobals
151 " uint i = 0x80000000u;\n"
152 " float correct = (i == expected) ? 1.0 : 0.0;\n",
153 bind(initializeExpectedValue, _1, _2, 2147483648u) // SetupUniformsFn setupUniformsFn
154 },
155 {
156 TestType::NORMAL, // TestType testType
157 "unsigned_integer_max_value_decimal", // string name
158 "Test that uint value equal to UINT_MAX is parsed correctly", // string description
159 "uniform uint expected;\n", // string testGlobals
160 " uint i = 4294967295u;\n"
161 " float correct = (i == expected) ? 1.0 : 0.0;\n",
162 bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn setupUniformsFn
163 },
164 {
165 TestType::NORMAL, // TestType testType
166 "unsigned_integer_max_value_base8", // string name
167 "Test that uint value equal to UINT_MAX is parsed correctly in base 8 (octal)", // string description
168 "uniform uint expected;\n", // string testGlobals
169 " uint i = 037777777777u;\n"
170 " float correct = (i == expected) ? 1.0 : 0.0;\n",
171 bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn setupUniformsFn
172 },
173 {
174 TestType::NORMAL, // TestType testType
175 "unsigned_integer_max_value_base16", // string name
176 "Test that uint value equal to UINT_MAX is parsed correctly in base 16 (hex)", // string description
177 "uniform uint expected;\n", // string testGlobals
178 " uint i = 0xffffffffu;\n"
179 " float correct = (i == expected) ? 1.0 : 0.0;\n",
180 bind(initializeExpectedValue, _1, _2, 4294967295u) // SetupUniformsFn setupUniformsFn
181 },
182 {
183 TestType::EXPECT_SHADER_FAIL, // TestType testType
184 "unsigned_integer_too_large_value_invalid", // string name
185 "Test that uint value outside uint range fails to compile", // string description
186 "", // string testGlobals
187 " uint i = 0xfffffffffu;"
188 " float correct = 0.0;",
189 nullptr // SetupUniformsFn setupUniformsFn
190 },
191 {
192 TestType::NORMAL, // TestType testType
193 "unsigned_integer_negative_value_as_uint", // string name
194 "Test that -1u is parsed correctly", // string description
195 "uniform uint expected;\n", // string testGlobals
196 " uint i = -1u;"
197 " float correct = (i == expected) ? 1.0 : 0.0;\n",
198 bind(initializeExpectedValue, _1, _2, 0xffffffffu) // SetupUniformsFn setupUniformsFn
199 },
200 /* The following floating point parsing tests are taken from the Khronos WebGL conformance tests at:
201 * https://www.khronos.org/registry/webgl/sdk/tests/conformance2/glsl3/float-parsing.html */
202 {
203 TestType::NORMAL, // TestType testType
204 "float_out_of_range_as_infinity", // string name
205 "Floats of too large magnitude should be converted infinity", // string description
206 "", // string testGlobals
207 " // Out-of-range floats should overflow to infinity\n" // string testCode
208 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
209 " // \"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"
210 " float correct = isinf(1.0e40) ? 1.0 : 0.0;\n",
211 nullptr // SetupUniformsFn setupUniformsFn
212 },
213 {
214 TestType::NORMAL, // TestType testType
215 "float_out_of_range_as_zero", // string name
216 "Floats of too small magnitude should be converted to zero", // string description
217 "", // string testGlobals
218 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n" // string testCode
219 " // \"A value with a magnitude too small to be represented as a mantissa and exponent is converted to 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 = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e101;\n"
233 " float correct = (x == 1.0) ? 1.0 : 0.0;\n",
234 nullptr // SetupUniformsFn setupUniformsFn
235 },
236 {
237 TestType::NORMAL, // TestType testType
238 "float_no_limit_on_number_of_digits_negative_exponent", // string name
239 "Number of digits in any digit-sequence is not limited - test with a large mantissa and negative exponent", // string description
240 "", // string testGlobals
241 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n" // string testCode
242 " // \"There is no limit on the number of digits in any digit-sequence.\"\n"
243 " // The below float string has 100 zeros, but represents 1.0.\n"
244 " float x = 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0e-100;\n"
245 " float correct = (x == 1.0) ? 1.0 : 0.0;\n",
246 nullptr // SetupUniformsFn setupUniformsFn
247 },
248 {
249 TestType::NORMAL, // TestType testType
250 "float_slightly_out_of_range_exponent_as_positive_infinity", // string name
251 "Test that an exponent that slightly overflows signed 32-bit int range works", // string description
252 "", // string testGlobals
253 " // Out-of-range floats should overflow to infinity\n" // string testCode
254 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
255 " // \"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"
256 " float correct = isinf(1.0e2147483649) ? 1.0 : 0.0;\n",
257 nullptr // SetupUniformsFn setupUniformsFn
258 },
259 {
260 TestType::NORMAL, // TestType testType
261 "float_overflow_to_positive_infinity", // string name
262 "Out-of-range floats greater than zero should overflow to positive infinity", // string description
263 "uniform float zero;\n", // string testGlobals
264 " // Out-of-range floats should overflow to infinity\n" // string testCode
265 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
266 " // \"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"
267 " float f = 1.0e2048 - zero;\n"
268 " float correct = (isinf(f) && f > 0.0) ? 1.0 : 0.0;\n",
269 initializeZeroValue // SetupUniformsFn setupUniformsFn
270 },
271 {
272 TestType::NORMAL, // TestType testType
273 "float_overflow_to_negative_infinity", // string name
274 "Out-of-range floats less than zero should overflow to negative infinity", // string description
275 "uniform float zero;\n", // string testGlobals
276 " // Out-of-range floats should overflow to infinity\n" // string testCode
277 " // GLSL ES 3.00.6 section 4.1.4 Floats:\n"
278 " // \"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"
279 " float f = -1.0e2048 + zero;\n"
280 " float correct = (isinf(f) && f < 0.0) ? 1.0 : 0.0;\n",
281 initializeZeroValue // SetupUniformsFn setupUniformsFn
282 }
283 };
284
initializeExpectedValue(const glu::ShaderProgram & program,const glw::Functions & gl,const deUint32 value)285 static void initializeExpectedValue(const glu::ShaderProgram& program, const glw::Functions& gl, const deUint32 value)
286 {
287 const auto location = gl.getUniformLocation(program.getProgram(), "expected");
288 GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
289
290 gl.uniform1ui(location, value);
291 GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
292 }
293
initializeZeroValue(const glu::ShaderProgram & program,const glw::Functions & gl)294 static void initializeZeroValue(const glu::ShaderProgram& program, const glw::Functions& gl)
295 {
296 const auto location = gl.getUniformLocation(program.getProgram(), "zero");
297 GLU_EXPECT_NO_ERROR(gl.getError(), "GetAttribLocation call failed");
298
299 gl.uniform1f(location, 0.0f);
300 GLU_EXPECT_NO_ERROR(gl.getError(), "Set uniform value failed");
301 }
302
replacePlaceholders(const string & shaderTemplate,const TestParams & params)303 static string replacePlaceholders(const string& shaderTemplate, const TestParams& params)
304 {
305 map<string,string> fields;
306 fields["TEST_GLOBALS"] = params.testGlobals;
307 fields["TEST_CODE"] = params.testCode;
308
309 tcu::StringTemplate output(shaderTemplate);
310 return output.specialize(fields);
311 }
312
313 static const std::vector<float> positions =
314 {
315 -1.0f, -1.0f,
316 1.0f, -1.0f,
317 -1.0f, 1.0f,
318 1.0f, 1.0f
319 };
320
321 static const std::vector<deUint32> indices = { 0, 1, 2, 3 };
322
323 const deInt32 RENDERTARGET_WIDTH = 16;
324 const deInt32 RENDERTARGET_HEIGHT = 16;
325
326 class NumberParsingCase : public deqp::TestCase
327 {
328 public:
329 NumberParsingCase(deqp::Context& context, const string& name, const TestParams& params, const string& vertexShader, 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, const string& vertexShader, const string& fragmentShader)
346 : TestCase(context, name.c_str(), params.description.c_str())
347 , m_fboId(0)
348 , m_rboId(0)
349 , m_params(params)
350 , m_vertexShader(vertexShader)
351 , m_fragmentShader(fragmentShader)
352 {
353 }
354
iterate(void)355 NumberParsingCase::IterateResult NumberParsingCase::iterate(void)
356 {
357 const auto& renderContext = m_context.getRenderContext();
358 const auto& gl = renderContext.getFunctions();
359 const auto textureFormat = tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8);
360 const auto transferFormat = glu::getTransferFormat(textureFormat);
361
362 setupRenderTarget();
363
364 glu::ShaderProgram program(renderContext, glu::makeVtxFragSources(m_vertexShader, m_fragmentShader));
365 if (!program.isOk())
366 switch(m_params.testType)
367 {
368 case TestType::EXPECT_SHADER_FAIL:
369 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
370 return STOP;
371 default:
372 TCU_FAIL("Shader compilation failed:\nVertex shader:\n" + m_vertexShader + "\nFragment shader:\n" + m_fragmentShader);
373 }
374
375 const std::vector<glu::VertexArrayBinding> vertexArrays =
376 {
377 glu::va::Float("vPosition", 2, positions.size(), 0, positions.data()),
378 };
379
380 gl.useProgram(program.getProgram());
381 GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram failed");
382
383 if (m_params.setupUniformsFn != DE_NULL)
384 m_params.setupUniformsFn(program, gl);
385
386 gl.clear(GL_COLOR_BUFFER_BIT);
387
388
389 glu::draw(renderContext, program.getProgram(),
390 static_cast<int>(vertexArrays.size()), vertexArrays.data(),
391 glu::pr::TriangleStrip(static_cast<int>(indices.size()), indices.data()));
392
393 const auto pixelSize = tcu::getPixelSize(textureFormat);
394 std::vector<deUint8> fbData (RENDERTARGET_WIDTH * RENDERTARGET_HEIGHT * pixelSize);
395
396 if (pixelSize < 4)
397 gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
398
399 gl.readPixels(0, 0, RENDERTARGET_WIDTH, RENDERTARGET_HEIGHT, transferFormat.format, transferFormat.dataType, 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 }
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, replacePlaceholders(fragmentShaderTemplate, params)));
490 }
491 }
492
493 }
494