perf/pixel-rate: new pixel throughput microbenchmark
[piglit.git] / tests / spec / glsl-1.30 / execution / isinf-and-isnan.c
blob99f267f41ea347907916bf6b085f17a97fe15637
1 /*
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
13 * Software.
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.
24 /**
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
38 * read out.
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
49 * point framebuffer)
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
52 * delta.
54 * And then it performs the following checks:
55 * - If the value was expected to be finite, verifies that isinf() and isnan()
56 * returned false.
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
66 * tolerance).
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
104 * out of the shader.
105 * - vs_xfb: test the VS, using transform feedback to read values out of the
106 * shader.
107 * - fs_fbo: test the FS, using a floating-point framebuffer to read values
108 * out of the shader.
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
134 * shader.
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
145 * vertex shader.
147 static bool use_fs;
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
152 * checks.
154 static bool precise;
156 enum modes
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[] =
171 "#version 130\n"
172 "void main()\n"
173 "{\n"
174 " gl_Position = gl_Vertex;\n"
175 "}\n";
177 static const char stock_fs_text[] =
178 "#version 130\n"
179 "flat in vec4 data;\n"
180 "void main()\n"
181 "{\n"
182 " gl_FragColor = data;\n"
183 "}\n";
185 static const char main_vs_text[] =
186 "#version 130\n"
187 "flat out vec4 data;\n"
188 "vec4 do_test();\n"
189 "void main()\n"
190 "{\n"
191 " gl_Position = gl_Vertex;\n"
192 " data = do_test();\n"
193 "}\n";
195 static const char main_fs_text[] =
196 "#version 130\n"
197 "flat in vec4 data;\n"
198 "vec4 do_test();\n"
199 "void main()\n"
200 "{\n"
201 " gl_FragColor = do_test();\n"
202 "}\n";
204 static const char do_test_text[] =
205 "#version 130\n"
206 "uniform float ref;\n" /* Value fed back from C */
207 "uniform int mode;\n" /* See enum modes */
208 "float compute_value();\n"
209 "vec4 do_test()\n"
210 "{\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"
219 " value - ref,\n"
220 " 0.0,\n"
221 " 0.0);\n"
222 " } else { /* Unrecognized mode */\n"
223 " return vec4(0.0);\n"
224 " }\n"
225 "}\n";
227 static void
228 setup_fbo()
230 GLuint fb = 0;
231 GLuint color_rb = 0;
232 GLenum fb_status;
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);
257 static void
258 setup_xfb()
260 glGenBuffers(1, &xfb_buffer);
263 static void
264 print_usage_and_exit(char *prog_name)
266 printf("Usage: %s <mode>\n"
267 " where <mode> is one of:\n"
268 " vs_basic\n"
269 " fs_basic\n"
270 " vs_fbo\n"
271 " vs_xfb\n"
272 " fs_fbo\n", prog_name);
273 exit(1);
276 void
277 piglit_init(int argc, char **argv)
279 if (argc != 2)
280 print_usage_and_exit(argv[0]);
281 if (strcmp(argv[1], "vs_basic") == 0) {
282 use_fs = false;
283 } else if (strcmp(argv[1], "fs_basic") == 0) {
284 use_fs = true;
285 } else if (strcmp(argv[1], "vs_fbo") == 0) {
286 use_fs = false;
287 use_fbo = true;
288 } else if (strcmp(argv[1], "vs_xfb") == 0) {
289 use_fs = false;
290 use_xfb = true;
291 } else if (strcmp(argv[1], "fs_fbo") == 0) {
292 use_fs = true;
293 use_fbo = true;
294 } else {
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);
306 } else {
307 piglit_require_gl_version(30);
310 if (use_fbo) {
311 setup_fbo();
313 if (use_xfb) {
314 setup_xfb();
317 stock_vs = piglit_compile_shader_text(GL_VERTEX_SHADER,
318 stock_vs_text);
319 stock_fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER,
320 stock_fs_text);
321 main_vs = piglit_compile_shader_text(GL_VERTEX_SHADER,
322 main_vs_text);
323 main_fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER,
324 main_fs_text);
325 do_test_vs = piglit_compile_shader_text(GL_VERTEX_SHADER,
326 do_test_text);
327 do_test_fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER,
328 do_test_text);
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.
337 enum behavior
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
348 char *expression;
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 },
363 { "u_nan", B_NAN },
364 { "0.0/0.0", B_NAN },
365 { "z/z", 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
384 * case (c).
386 static void
387 draw_and_readback(float *readback)
389 if (use_xfb) {
390 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 4096, NULL,
391 GL_DYNAMIC_COPY);
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);
399 if (use_xfb) {
400 glEndTransformFeedback();
401 memcpy(readback,
402 glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY),
403 4*sizeof(float));
404 glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
405 } else {
406 glReadPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, readback);
410 static void
411 set_uniform_float_if_present(GLint program, char *name, float value)
413 GLint loc = glGetUniformLocation(program, name);
414 if (loc != -1)
415 glUniform1f(loc, value);
418 static void
419 set_uniform_int_if_present(GLint program, char *name, int value)
421 GLint loc = glGetUniformLocation(program, name);
422 if (loc != -1)
423 glUniform1i(loc, value);
427 * Test the given expression, to make sure its behavior is self-consistent and
428 * consistent with the expected behavior.
430 static bool
431 test_expr(char *expression, int expected_behavior)
433 char compute_value_text[4096];
434 GLint shader;
435 GLint prog;
436 float readback[4];
437 float value;
438 bool isinf_in_shader;
439 bool isnan_in_shader;
440 int sign_in_shader;
441 float delta;
442 bool greater_than_zero;
443 bool pass = true;
444 char *expected_behavior_string;
446 /* Create and link a program specifically to test this expression */
447 prog = glCreateProgram();
448 sprintf(compute_value_text,
449 "#version 130\n"
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"
456 " return %s;\n"
457 "}\n",
458 expression);
459 if (use_fs) {
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,
464 compute_value_text);
465 glAttachShader(prog, shader);
466 } else {
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,
471 compute_value_text);
472 glAttachShader(prog, shader);
474 if (use_xfb) {
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);
480 glLinkProgram(prog);
481 glDeleteShader(shader);
482 glUseProgram(prog);
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),
490 * and sign(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);
495 value = readback[0];
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;
507 delta = readback[1];
509 /* Check that the behavior was as expected */
510 switch (expected_behavior) {
511 case B_FINITE:
512 expected_behavior_string = "finite";
513 if (isinf_in_shader || isnan_in_shader) {
514 /* Expected finite, got Inf or NaN */
515 pass = false;
517 break;
518 case B_POSINF:
519 expected_behavior_string = "+Inf";
520 if (!isnan_in_shader && sign_in_shader != 1.0) {
521 /* Expected positive or NaN, got <= 0 */
522 pass = false;
524 break;
525 case B_NEGINF:
526 expected_behavior_string = "-Inf";
527 if (!isnan_in_shader && sign_in_shader != -1.0) {
528 /* Expected negative or NaN, got >= 0 */
529 pass = false;
531 break;
532 case B_FINITE_NAN_OK:
533 expected_behavior_string = "finite";
534 break;
535 default:
536 expected_behavior_string = "NaN";
537 break;
540 /* Do other sanity checks */
541 if (isnan_in_shader && isinf_in_shader) {
542 /* No value can be simultaneously Inf and NaN */
543 pass = false;
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) */
549 pass = false;
551 } else if (sign_in_shader == 1) {
552 if (!greater_than_zero) {
553 /* sign(value) inconsistent with (value>0) */
554 pass = false;
556 } else {
557 /* Illegal return value for sign() */
558 pass = false;
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.
566 if (precise) {
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.
574 pass = false;
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.
584 pass = false;
589 /* Output a line for the results table */
590 printf("%17s %6s %5s %5s %4d %5s ",
591 expression,
592 expected_behavior_string,
593 isinf_in_shader ? "true" : "false",
594 isnan_in_shader ? "true" : "false",
595 sign_in_shader,
596 greater_than_zero ? "true" : "false");
597 if (precise) {
598 printf("%12g %12g ", value, delta);
600 printf("%s\n", pass ? "OK" : "FAIL");
602 glUseProgram(0);
603 glDeleteProgram(prog);
605 return pass;
608 enum piglit_result
609 piglit_display()
611 int i;
612 bool pass = true;
614 printf(" expression expect isinf isnan sign >0?");
615 if (precise)
616 printf(" value delta");
617 printf("\n");
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;