2 * Copyright © 2011 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
25 * \file isinf-and-isnan.c
27 * Test that isinf() and isnan() built-in functions behave properly.
29 * The GLSL 1.30 spec does not define when an implementation is required to
30 * generate infinite or NaN values; in fact, it explicitly allows for
31 * implementations that do not even have a representation of infinity or Nan.
32 * Therefore, we cannot check that infinities and NaNs are created when we
33 * expect them. However, we can test: (a) that isnan() and isinf() return
34 * false for finite values, (b) that isinf() and isnan() behave consistently
35 * with each other, and (c) that when a floating-point value is read out the
36 * shader (using transform feedback or a floating point framebuffer) the
37 * behavior of isnan() and isinf() behave consistently with the value that is
40 * This test operates by generating several expressions, some of which are
41 * likely to produce infinities, some of which are likely to produce NaN, and
42 * some of which are expected to produce finite values. For each expression,
43 * it does the following:
44 * - evaluates isinf(value) in the shader
45 * - evaluates isnan(value) in the shader
46 * - evaluates sign(value) in the shader
47 * - evaluates (value > 0) in the shader
48 * - reads the value out of the shader (using transform feedback or a floating
50 * - feeds that value back into the shader (using a uniform); the shader
51 * subtracts this uniform from the originally computed value to produce a
54 * And then it performs the following checks:
55 * - If the value was expected to be finite, verifies that isinf() and isnan()
57 * - If the value was expected to be +Inf or -Inf, verifies that the sign is
58 * correct, using both sign(value) and (value > 0). This check is skipped
59 * if isnan(value) is true, since it's possible that a conformant
60 * implementation might generate NaN instead of infinity, and NaN does not
61 * have a well-defined sign.
62 * - Checks that isinf() and isnan() didn't both return true.
63 * - Checks that the C isinf() and isnan() functions give the same result as
64 * the shader's isinf() and isnan() functions.
65 * - If the value is finite, checks that the delta is zero (to within
68 * The last two checks are only performed when using a floating point
69 * framebuffer or transform feedback, because those are the only ways to get
70 * infinities and NaNs out of the shader and into C code.
72 * Note: the reason for the final check is to verify that a value claimed by
73 * the shader to be finite is truly behaving like a finite number. Without
74 * it, an implementation could pass all these tests by simply having isinf()
75 * and isnan() return false, and converting infinities and NaNs to finite
76 * values when they exit the shader.
78 * The output of the test is a table whose columns are:
79 * - The expression being tested (this expression may refer to the uniforms
80 * z=0.0, u_inf=+Inf, u_minus_inf=-Inf, and u_nan=NaN).
81 * - The expected behavior of the expression ("finite", "+Inf", "-Inf", or
82 * "NaN", indicating how the expression would be expected to evaluate on a
83 * fully IEEE 754 compliant architecture)
84 * - isinf(value), as computed by the shader
85 * - isnan(value), as computed by the shader
86 * - sign(value), as computed by the shader
87 * - (value > 0), as computed by the shader
88 * - value, as read out of the shader using transform feedback or a
89 * floating-point framebuffer
90 * - delta, the difference between the computed value and the value that was
91 * fed back into the shader.
92 * - A pass/fail indication.
94 * Note: the uniform z=0.0 is present to defeat constant folding and ensure
95 * that the expression is evaluated during shader execution rather than during
96 * compilation. For example, "exp(1000.0)" might be evaluated at compile-time,
97 * preventing us from exercising this test case in the GPU. But
98 * "exp(1000.0+z)" will definitely be evaluated on the GPU.
100 * The test must be invoked with one of the following command-line arguments:
101 * - vs_basic: test the VS without reading values out of the shader.
102 * - fs_basic: test the FS without reading values out of the shader.
103 * - vs_fbo: test the VS, using a floating-point framebuffer to read values
105 * - vs_xfb: test the VS, using transform feedback to read values out of the
107 * - fs_fbo: test the FS, using a floating-point framebuffer to read values
111 #include "piglit-util-gl.h"
113 PIGLIT_GL_TEST_CONFIG_BEGIN
115 config
.supports_gl_compat_version
= 10;
117 config
.window_visual
= PIGLIT_GL_VISUAL_RGBA
| PIGLIT_GL_VISUAL_DOUBLE
;
118 config
.khr_no_error_support
= PIGLIT_NO_ERRORS
;
120 PIGLIT_GL_TEST_CONFIG_END
122 static float gl_version
;
124 static GLint stock_vs
;
125 static GLint stock_fs
;
126 static GLint main_vs
;
127 static GLint main_fs
;
128 static GLint do_test_vs
;
129 static GLint do_test_fs
;
130 static GLuint xfb_buffer
;
133 * True if we are using a floating-point framebuffer to read data out of the
136 static bool use_fbo
= false;
139 * True if we are using transform feedback to read data out of the shader.
141 static bool use_xfb
= false;
144 * True if we are testing the fragment shader, false if we are testing the
150 * True if we are reading data out of the shader using a mechanism that
151 * preserves the full 32-bit floating point value, so we can do additional
159 * Output = vec4(value, isinf(value), isnan(value),
160 * (sign(value) + 1.0) / 2.0)
162 MODE_VALUE_ISINF_ISNAN_SIGN
= 0,
165 * Output = vec4(value > 0, value - ref, 0.0, 0.0)
167 MODE_GTZERO_DELTA_ZERO_ZERO
= 1,
170 static const char stock_vs_text
[] =
174 " gl_Position = gl_Vertex;\n"
177 static const char stock_fs_text
[] =
179 "flat in vec4 data;\n"
182 " gl_FragColor = data;\n"
185 static const char main_vs_text
[] =
187 "flat out vec4 data;\n"
191 " gl_Position = gl_Vertex;\n"
192 " data = do_test();\n"
195 static const char main_fs_text
[] =
197 "flat in vec4 data;\n"
201 " gl_FragColor = do_test();\n"
204 static const char do_test_text
[] =
206 "uniform float ref;\n" /* Value fed back from C */
207 "uniform int mode;\n" /* See enum modes */
208 "float compute_value();\n"
211 " float value = compute_value();\n"
212 " if (mode == 0) { /* MODE_VALUE_ISINF_ISNAN_SIGN */\n"
213 " return vec4(value,\n"
214 " isinf(value) ? 1 : 0,\n"
215 " isnan(value) ? 1 : 0,\n"
216 " (sign(value) + 1.0) / 2.0);\n"
217 " } else if (mode == 1) { /* MODE_GTZERO_DELTA_ZERO_ZERO */\n"
218 " return vec4(value > 0 ? 1 : 0,\n"
222 " } else { /* Unrecognized mode */\n"
223 " return vec4(0.0);\n"
234 glGenFramebuffers(1, &fb
);
235 glBindFramebuffer(GL_FRAMEBUFFER
, fb
);
237 /* Bind color attachment. */
238 glGenRenderbuffers(1, &color_rb
);
239 glBindRenderbuffer(GL_RENDERBUFFER
, color_rb
);
240 glRenderbufferStorage(GL_RENDERBUFFER
, GL_RGBA32F
,
241 piglit_width
, piglit_height
);
242 glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
243 GL_RENDERBUFFER
, color_rb
);
244 if (!piglit_check_gl_error(0))
245 piglit_report_result(PIGLIT_FAIL
);
247 fb_status
= glCheckFramebufferStatus(GL_FRAMEBUFFER
);
248 if (fb_status
!= GL_FRAMEBUFFER_COMPLETE
) {
249 printf("error: FBO incomplete (status = 0x%04x)\n", fb_status
);
250 piglit_report_result(PIGLIT_SKIP
);
253 glBindFramebuffer(GL_DRAW_FRAMEBUFFER
, fb
);
254 glBindFramebuffer(GL_READ_FRAMEBUFFER
, fb
);
260 glGenBuffers(1, &xfb_buffer
);
264 print_usage_and_exit(char *prog_name
)
266 printf("Usage: %s <mode>\n"
267 " where <mode> is one of:\n"
272 " fs_fbo\n", prog_name
);
277 piglit_init(int argc
, char **argv
)
280 print_usage_and_exit(argv
[0]);
281 if (strcmp(argv
[1], "vs_basic") == 0) {
283 } else if (strcmp(argv
[1], "fs_basic") == 0) {
285 } else if (strcmp(argv
[1], "vs_fbo") == 0) {
288 } else if (strcmp(argv
[1], "vs_xfb") == 0) {
291 } else if (strcmp(argv
[1], "fs_fbo") == 0) {
295 print_usage_and_exit(argv
[0]);
297 precise
= use_fbo
|| use_xfb
;
299 gl_version
= strtod((char *) glGetString(GL_VERSION
), NULL
);
301 piglit_require_GLSL();
302 piglit_require_GLSL_version(130);
304 if (piglit_is_extension_supported("GL_EXT_gpu_shader4")) {
305 piglit_require_gl_version(21);
307 piglit_require_gl_version(30);
317 stock_vs
= piglit_compile_shader_text(GL_VERTEX_SHADER
,
319 stock_fs
= piglit_compile_shader_text(GL_FRAGMENT_SHADER
,
321 main_vs
= piglit_compile_shader_text(GL_VERTEX_SHADER
,
323 main_fs
= piglit_compile_shader_text(GL_FRAGMENT_SHADER
,
325 do_test_vs
= piglit_compile_shader_text(GL_VERTEX_SHADER
,
327 do_test_fs
= piglit_compile_shader_text(GL_FRAGMENT_SHADER
,
332 * enum indicating how the expression would be expected to behave on a fully
333 * IEEE 754 compliant architecture. Note: since OpenGL implementations are
334 * not required to respect all of IEEE 754's rules for infinities and NaN's,
335 * we don't necessarily check all of these behaviors.
339 B_NAN
= 0, /* Expected to evaluate to NaN */
340 B_FINITE
= 1, /* Expected to evaluate to a finite value */
341 B_POSINF
= 2, /* Expected to evaluate to +Infinity */
342 B_NEGINF
= 3, /* Expected to evaluate to -Infinity */
343 B_FINITE_NAN_OK
= 4, /* Expected finite value, but NaN ok */
346 struct expression_table_element
349 int expected_behavior
;
352 static struct expression_table_element expressions
[] = {
353 { "1000.0", B_FINITE
},
354 { "1000.0+z", B_FINITE
},
355 { "-1000.0", B_FINITE
},
356 { "-1000.0+z", B_FINITE
},
357 { "u_inf", B_POSINF
},
358 { "exp(1000.0)", B_POSINF
},
359 { "exp(1000.0+z)", B_POSINF
},
360 { "u_minus_inf", B_NEGINF
},
361 { "-exp(1000.0)", B_NEGINF
},
362 { "-exp(1000.0+z)", B_NEGINF
},
364 { "0.0/0.0", B_NAN
},
366 { "u_inf/u_minus_inf", B_NAN
},
367 { "z*u_inf", B_NAN
},
368 { "u_inf+u_minus_inf", B_NAN
},
369 { "log(-1.0)", B_NAN
},
370 { "log(-1.0+z)", B_NAN
},
371 { "sqrt(-1.0)", B_NAN
},
372 { "sqrt(-1.0+z)", B_NAN
},
373 { "clamp(u_nan, 0.0, 1.0)", B_FINITE_NAN_OK
},
374 { "min(u_two, u_nan)", B_FINITE_NAN_OK
},
375 { "max(u_two, u_nan)", B_FINITE_NAN_OK
},
380 * Draw using the shader, and then read back values using either (a) the
381 * floating-point framebuffer, (b) transform feedback, or (c) pixel reads from
382 * the window. Note that pixel reads from the window are only accurate to one
383 * part in 255, so the caller must be careful not to rely on high precision in
387 draw_and_readback(float *readback
)
390 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER
, 4096, NULL
,
392 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER
, 0, xfb_buffer
);
393 glEnable(GL_RASTERIZER_DISCARD
);
394 glBeginTransformFeedback(GL_TRIANGLES
);
397 piglit_draw_rect(-1, -1, 2, 2);
400 glEndTransformFeedback();
402 glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER
, GL_READ_ONLY
),
404 glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER
);
406 glReadPixels(0, 0, 1, 1, GL_RGBA
, GL_FLOAT
, readback
);
411 set_uniform_float_if_present(GLint program
, char *name
, float value
)
413 GLint loc
= glGetUniformLocation(program
, name
);
415 glUniform1f(loc
, value
);
419 set_uniform_int_if_present(GLint program
, char *name
, int value
)
421 GLint loc
= glGetUniformLocation(program
, name
);
423 glUniform1i(loc
, value
);
427 * Test the given expression, to make sure its behavior is self-consistent and
428 * consistent with the expected behavior.
431 test_expr(char *expression
, int expected_behavior
)
433 char compute_value_text
[4096];
438 bool isinf_in_shader
;
439 bool isnan_in_shader
;
442 bool greater_than_zero
;
444 char *expected_behavior_string
;
446 /* Create and link a program specifically to test this expression */
447 prog
= glCreateProgram();
448 sprintf(compute_value_text
,
450 "uniform float z = 0.0;\n" /* To defeat constant folding */
451 "uniform float u_inf;\n" /* Always == +infinity */
452 "uniform float u_minus_inf;\n" /* Always == -infinity */
453 "uniform float u_nan;\n" /* Always == NaN */
454 "uniform float u_two = 2.0;\n" /* To defeat constant folding */
455 "float compute_value() {\n"
460 glAttachShader(prog
, stock_vs
);
461 glAttachShader(prog
, main_fs
);
462 glAttachShader(prog
, do_test_fs
);
463 shader
= piglit_compile_shader_text(GL_FRAGMENT_SHADER
,
465 glAttachShader(prog
, shader
);
467 glAttachShader(prog
, stock_fs
);
468 glAttachShader(prog
, main_vs
);
469 glAttachShader(prog
, do_test_vs
);
470 shader
= piglit_compile_shader_text(GL_VERTEX_SHADER
,
472 glAttachShader(prog
, shader
);
475 static const char *var_name
= "data";
476 glTransformFeedbackVaryings(prog
, 1, &var_name
,
477 GL_SEPARATE_ATTRIBS
);
478 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER
, xfb_buffer
);
481 glDeleteShader(shader
);
484 /* Set up uniforms */
485 set_uniform_float_if_present(prog
, "u_inf", INFINITY
);
486 set_uniform_float_if_present(prog
, "u_minus_inf", -INFINITY
);
487 set_uniform_float_if_present(prog
, "u_nan", NAN
);
489 /* Use one draw call to read out value, isinf(value), isnan(value),
492 set_uniform_float_if_present(prog
, "ref", 0.0);
493 set_uniform_int_if_present(prog
, "mode", MODE_VALUE_ISINF_ISNAN_SIGN
);
494 draw_and_readback(readback
);
496 isinf_in_shader
= readback
[1] > 0.5;
497 isnan_in_shader
= readback
[2] > 0.5;
498 sign_in_shader
= (int) (2.0*readback
[3] + 0.5) - 1;
500 /* Use a second draw call to feed value back into the shader, and read
501 * out (value > 0) and delta.
503 set_uniform_float_if_present(prog
, "ref", value
);
504 set_uniform_int_if_present(prog
, "mode", MODE_GTZERO_DELTA_ZERO_ZERO
);
505 draw_and_readback(readback
);
506 greater_than_zero
= readback
[0] > 0.5;
509 /* Check that the behavior was as expected */
510 switch (expected_behavior
) {
512 expected_behavior_string
= "finite";
513 if (isinf_in_shader
|| isnan_in_shader
) {
514 /* Expected finite, got Inf or NaN */
519 expected_behavior_string
= "+Inf";
520 if (!isnan_in_shader
&& sign_in_shader
!= 1.0) {
521 /* Expected positive or NaN, got <= 0 */
526 expected_behavior_string
= "-Inf";
527 if (!isnan_in_shader
&& sign_in_shader
!= -1.0) {
528 /* Expected negative or NaN, got >= 0 */
532 case B_FINITE_NAN_OK
:
533 expected_behavior_string
= "finite";
536 expected_behavior_string
= "NaN";
540 /* Do other sanity checks */
541 if (isnan_in_shader
&& isinf_in_shader
) {
542 /* No value can be simultaneously Inf and NaN */
545 if (!isnan_in_shader
) {
546 if (sign_in_shader
== -1 || sign_in_shader
== 0) {
547 if (greater_than_zero
) {
548 /* sign(value) inconsistent with (value>0) */
551 } else if (sign_in_shader
== 1) {
552 if (!greater_than_zero
) {
553 /* sign(value) inconsistent with (value>0) */
557 /* Illegal return value for sign() */
562 /* If we are using a high-precision technique to read data out of the
563 * shader (fbo or xfb), check the behavior of isinf and isnan against
564 * their C counterparts, and verify that delta ~= 0 for finite values.
567 bool isinf_in_c
= !!isinf(value
);
568 bool isnan_in_c
= !!isnan(value
);
569 if (isinf_in_shader
!= isinf_in_c
||
570 isnan_in_shader
!= isnan_in_c
) {
571 /* Result of isinf() and isnan() in the shader did not
572 * match the result in C code.
576 if (!isinf_in_shader
&& !isnan_in_shader
) {
577 float threshold
= fabs(value
* 1e-6);
578 if (isinf(delta
) || isnan(delta
) ||
579 fabs(delta
) > threshold
) {
580 /* The shader and C code agree that the value
581 * was finite, but it isn't behaving as a nice
582 * finite value should.
589 /* Output a line for the results table */
590 printf("%17s %6s %5s %5s %4d %5s ",
592 expected_behavior_string
,
593 isinf_in_shader
? "true" : "false",
594 isnan_in_shader
? "true" : "false",
596 greater_than_zero
? "true" : "false");
598 printf("%12g %12g ", value
, delta
);
600 printf("%s\n", pass
? "OK" : "FAIL");
603 glDeleteProgram(prog
);
614 printf(" expression expect isinf isnan sign >0?");
616 printf(" value delta");
619 for (i
= 0; i
< sizeof(expressions
)/sizeof(*expressions
); ++i
) {
620 pass
= test_expr(expressions
[i
].expression
,
621 expressions
[i
].expected_behavior
) && pass
;
624 return pass
? PIGLIT_PASS
: PIGLIT_FAIL
;