On 7 October 2011 12:28, Ian Romanick <span dir="ltr"><<a href="mailto:idr@freedesktop.org">idr@freedesktop.org</a>></span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div><div></div><div class="h5">On 10/06/2011 08:45 AM, Paul Berry wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
isinf() and isnan() are difficult to test, since the GLSL 1.30 spec<br>
does not define when an implemenation is required to generate infinite<br>
or NaN values; in fact, it explicitly allows for implementations that<br>
do not even have a representation of infinity or NaN.<br>
<br>
This test verifies that if the implementation does generate infinite<br>
or NaN values, they behave in a consistent way, and that when it<br>
claims that a value is finite, it truly is finite.<br>
<br>
Tested on the nVidia proprietary Linux driver, which passes all<br>
variants of the test except the "vs_xfb" variant (which is skipped<br>
because the driver doesn't have the correct extension string to<br>
indicate that it is capable of performing transform feedback).<br>
---<br>
tests/all.tests | 3 +<br>
tests/spec/glsl-1.30/<u></u>execution/CMakeLists.gl.txt | 1 +<br>
tests/spec/glsl-1.30/<u></u>execution/isinf-and-isnan.c | 554 ++++++++++++++++++++++<br>
3 files changed, 558 insertions(+), 0 deletions(-)<br>
create mode 100644 tests/spec/glsl-1.30/<u></u>execution/isinf-and-isnan.c<br>
<br>
diff --git a/tests/all.tests b/tests/all.tests<br>
index 40bc92c..aca2c7f 100644<br>
--- a/tests/all.tests<br>
+++ b/tests/all.tests<br>
@@ -853,6 +853,9 @@ spec['glsl-1.30']['linker'] = Group()<br>
spec['glsl-1.30']['linker']['<u></u>clipping'] = Group()<br>
add_plain_test(spec['glsl-1.<u></u>30']['linker']['clipping'], 'mixing-clip-distance-and-<u></u>clip-vertex-disallowed')<br>
add_plain_test(spec['glsl-1.<u></u>30']['execution']['clipping'], 'max-clip-distances')<br>
+for arg in ['vs_basic', 'vs_xfb', 'vs_fbo', 'fs_basic', 'fs_fbo']:<br>
+ test_name = 'isinf-and-isnan ' + arg<br>
+ spec['glsl-1.30']['execution']<u></u>[test_name] = PlainExecTest(test_name + ' -auto')<br>
spec['glsl-1.30']['execution']<u></u>['clipping']['clip-plane-<u></u>transformation pos'] = \<br>
concurrent_test('clip-plane-<u></u>transformation pos')<br>
<br>
diff --git a/tests/spec/glsl-1.30/<u></u>execution/CMakeLists.gl.txt b/tests/spec/glsl-1.30/<u></u>execution/CMakeLists.gl.txt<br>
index e569968..1a920ca 100644<br>
--- a/tests/spec/glsl-1.30/<u></u>execution/CMakeLists.gl.txt<br>
+++ b/tests/spec/glsl-1.30/<u></u>execution/CMakeLists.gl.txt<br>
@@ -16,3 +16,4 @@ link_libraries (<br>
add_executable (fs-textureSize-2D fs-textureSize-2D.c)<br>
add_executable (fs-texelFetch-2D fs-texelFetch-2D.c)<br>
add_executable (fs-texelFetchOffset-2D fs-texelFetchOffset-2D.c)<br>
+add_executable (isinf-and-isnan isinf-and-isnan.c)<br>
diff --git a/tests/spec/glsl-1.30/<u></u>execution/isinf-and-isnan.c b/tests/spec/glsl-1.30/<u></u>execution/isinf-and-isnan.c<br>
new file mode 100644<br>
index 0000000..601d36d<br>
--- /dev/null<br>
+++ b/tests/spec/glsl-1.30/<u></u>execution/isinf-and-isnan.c<br>
@@ -0,0 +1,554 @@<br>
+/*<br>
+ * Copyright © 2011 Intel Corporation<br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER<br>
+ * DEALINGS IN THE SOFTWARE.<br>
+ */<br>
+<br>
+/**<br>
+ * \file isinf-and-isnan.c<br>
+ *<br>
+ * Test that isinf() and isnan() built-in functions behave properly.<br>
+ *<br>
+ * The GLSL 1.30 spec does not define when an implementation is required to<br>
+ * generate infinite or NaN values; in fact, it explicitly allows for<br>
+ * implementations that do not even have a representation of infinity or Nan.<br>
+ * Therefore, we cannot check that infinities and NaNs are created when we<br>
+ * expect them. However, we can test: (a) that isnan() and isinf() return<br>
+ * false for finite values, (b) that isinf() and isnan() behave consistently<br>
+ * with each other, and (c) that when a floating-point value is read out the<br>
+ * shader (using transform feedback or a floating point framebuffer) the<br>
+ * behavior of isnan() and isinf() behave consistently with the value that is<br>
+ * read out.<br>
+ *<br>
+ * This test operates by generating several expressions, some of which are<br>
+ * likely to produce infinities, some of which are likely to produce NaN, and<br>
+ * some of which are expected to produce finite values. For each expression,<br>
+ * it does the following:<br>
+ * - evaluates isinf(value) in the shader<br>
+ * - evaluates isnan(value) in the shader<br>
+ * - evaluates sign(value) in the shader<br>
+ * - evaluates (value> 0) in the shader<br>
+ * - reads the value out of the shader (using transform feedback or a floating<br>
+ * point framebuffer)<br>
+ * - feeds that value back into the shader (using a uniform); the shader<br>
+ * subtracts this uniform from the originally computed value to produce a<br>
+ * delta.<br>
+ *<br>
+ * And then it performs the following checks:<br>
+ * - If the value was expected to be finite, verifies that isinf() and isnan()<br>
+ * returned false.<br>
+ * - If the value was expected to be +Inf or -Inf, verifies that the sign is<br>
+ * correct, using both sign(value) and (value> 0). This check is skipped<br>
+ * if isnan(value) is true, since it's possible that a conformant<br>
+ * implementation might generate NaN instead of infinity, and NaN does not<br>
+ * have a well-defined sign.<br>
+ * - Checks that isinf() and isnan() didn't both return true.<br>
+ * - Checks that the C isinf() and isnan() functions give the same result as<br>
+ * the shader's isinf() and isnan() functions.<br>
+ * - If the value is finite, checks that the delta is zero (to within<br>
+ * tolerance).<br>
+ *<br>
+ * The last two checks are only performed when using a floating point<br>
+ * framebuffer or transform feedback, because those are the only ways to get<br>
+ * infinities and NaNs out of the shader and into C code.<br>
+ *<br>
+ * Note: the reason for the final check is to verify that a value claimed by<br>
+ * the shader to be finite is truly behaving like a finite number. Without<br>
+ * it, an implementation could pass all these tests by simply having isinf()<br>
+ * and isnan() return false, and converting infinities and NaNs to finite<br>
+ * values when they exit the shader.<br>
+ *<br>
+ * The output of the test is a table whose columns are:<br>
+ * - The expression being tested (this expression may refer to the uniforms<br>
+ * z=0, u_inf=+Inf, u_minus_inf=-Inf, and u_nan=NaN).<br>
+ * - The expected behavior of the expression ("finite", "+Inf", "-Inf", or<br>
+ * "NaN", indicating how the expression would be expected to evaluate on a<br>
+ * fully IEEE 754 compliant architecture)<br>
+ * - isinf(value), as computed by the shader<br>
+ * - isnan(value), as computed by the shader<br>
+ * - sign(value), as computed by the shader<br>
+ * - (value> 0), as computed by the shader<br>
+ * - value, as read out of the shader using transform feedback or a<br>
+ * floating-point framebuffer<br>
+ * - delta, the difference between the computed value and the value that was<br>
+ * fed back into the shader.<br>
+ * - A pass/fail indication.<br>
+ *<br>
+ * The test must be invoked with one of the following command-line arguments:<br>
+ * - vs_basic: test the VS without reading values out of the shader.<br>
+ * - fs_basic: test the FS without reading values out of the shader.<br>
+ * - vs_fbo: test the VS, using a floating-point framebuffer to read values<br>
+ * out of the shader.<br>
+ * - vs_xfb: test the VS, using transform feedback to read values out of the<br>
+ * shader.<br>
+ * - fs_fbo: test the FS, using a floating-point framebuffer to read values<br>
+ * out of the shader.<br>
+ */<br>
+<br>
+#include "piglit-util.h"<br>
+<br>
+int piglit_width = 100;<br>
+int piglit_height = 100;<br>
+int piglit_window_mode = GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE;<br>
+<br>
+GLint stock_vs;<br>
+GLint stock_fs;<br>
+GLuint xfb_buffer;<br>
+<br>
+/**<br>
+ * True if we are using a floating-point framebuffer to read data out of the<br>
+ * shader.<br>
+ */<br>
+bool use_fbo = false;<br>
+<br>
+/**<br>
+ * True if we are using transform feedback to read data out of the shader.<br>
+ */<br>
+bool use_xfb = false;<br>
+<br>
+/**<br>
+ * True if we are testing the fragment shader, false if we are testing the<br>
+ * vertex shader.<br>
+ */<br>
+bool use_fs;<br>
+<br>
+/**<br>
+ * True if we are reading data out of the shader using a mechanism that<br>
+ * preserves the full 32-bit floating point value, so we can do additional<br>
+ * checks.<br>
+ */<br>
+bool precise;<br>
+<br>
+static const char stock_vs_text[] =<br>
+ "#version 130\n"<br>
+ "void main()\n"<br>
+ "{\n"<br>
+ " gl_Position = gl_Vertex;\n"<br>
+ "}\n";<br>
+<br>
+static const char stock_fs_text[] =<br>
+ "#version 130\n"<br>
+ "flat in vec4 data;\n"<br>
+ "void main()\n"<br>
+ "{\n"<br>
+ " gl_FragColor = data;\n"<br>
+ "}\n";<br>
+<br>
+static const char shader_template[] =<br>
+ "#version 130\n"<br>
+ "uniform float z;\n"<br>
</blockquote>
<br></div></div>
Why is z a uniform? Is this just to defeat constant folding? If so, a comment to that effect is in order. <br></blockquote><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<br>
At the very least, you can initialize its value in the shader:<br>
<br>
uniform float z = 0.0;<div class="im"><br></div></blockquote><div><br>Ok, I will do this and make a comment to explain that z is present to defeat constant folding.<br> </div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<div class="im">
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ "uniform float u_inf;\n"<br>
+ "uniform float u_minus_inf;\n"<br>
+ "uniform float u_nan;\n"<br>
+ "uniform float ref;\n"<br>
+ "uniform int mode;\n"<br>
+ "%s" /* Either vs_boilerplate or fs_boilerplate */<br>
</blockquote>
<br></div>
Yuck. There's no reason to concatenate the shader strings in the application code. There are two better ways to do this in GLSL:<br>
<br>
1. Multiple compilation units.<br>
<br>
2. Multiple source strings passed to glShaderSource. This makes things a bit more difficult with our piglit wrapper functions, but that may mean that we need a different set of warpper functions. A varargs version of piglit_compile_shader_text might be in order. Then you'd just do:<br>
<br>
piglit_compile_shader_text_<u></u>varags(GL_FRAGMENT_SHADER,<br>
shader_template,<br>
fs_boilerplate,<br>
NULL);<br>
<br>
Or something like that.<br>
<br>
One way that I've seen tests like this structured is to have a boilerplate main function for each shader target. Each flavor of main is designed to call a do_test function that does the real work. Then you just need a bunch of do_test function snippets that you compile and like with the per-shader main. If GLSL had a "generic" shader target that you could compile to, this would be even easier.</blockquote>
<div><br>Ok, fair point. I'll restructure the test using multiple compilation units.<br> </div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<div class="im"><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ "void main()\n"<br>
+ "{\n"<br>
+ " do_common_shader_operations();<u></u>\n"<br>
+ " float value = %s;\n" /* Expression to be tested */<br>
</blockquote>
<br></div>
In the do_test style, this would just become<br>
<br>
float value = do_test();<div class="im"><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ " if (mode == 0) {\n"<br>
</blockquote>
<br></div>
I'd like to see an enum or some #defines in the C code to explain the values of mode.</blockquote><div><br>Ok, fair point.<br> </div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<div><div></div><div class="h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ " output_result(vec4(value,\n"<br>
+ " isinf(value) ? 1 : 0,\n"<br>
+ " isnan(value) ? 1 : 0,\n"<br>
+ " (sign(value) + 1.0) / 2.0));\n"<br>
+ " } else if (mode == 1) {\n"<br>
+ " output_result(vec4(value> 0 ? 1 : 0,\n"<br>
+ " value - ref,\n"<br>
+ " 0.0,\n"<br>
+ " 0.0));\n"<br>
+ " } else {\n"<br>
+ " /* Ensure all uniforms are used */\n"<br>
+ " output_result(vec4(z + u_inf + u_minus_inf + u_nan));\n"<br>
+ " }\n"<br>
+ "}\n";<br>
+<br>
+static const char vs_boilerplate[] =<br>
+ "flat out vec4 data;\n"<br>
+ "void do_common_shader_operations()\<u></u>n"<br>
+ "{\n"<br>
+ " gl_Position = gl_Vertex;\n"<br>
+ "}\n"<br>
+ "void output_result(vec4 result)\n"<br>
+ "{\n"<br>
+ " data = result;\n"<br>
+ "}\n";<br>
+<br>
+static const char fs_boilerplate[] =<br>
+ "void do_common_shader_operations()\<u></u>n"<br>
+ "{\n"<br>
+ "}\n"<br>
+ "void output_result(vec4 result)\n"<br>
+ "{\n"<br>
+ " gl_FragColor = result;\n"<br>
+ "}\n";<br>
</blockquote>
<br></div></div>
Also yuck. Since this test will require GLSL 1.30 and OpenGL 3.0, you can just make a fragment shader output called result.<br>
<br>
out vec4 result;<br>
<br>
Mesa doesn't have glBindFragDataLocation yet, but you can use explicit attribute locations. Something like:<br>
<br>
#version 130<br>
#extension GL_ARB_explicit_attrib_<u></u>location: enable<br>
<br>
#if defined GL_ARB_explicit_attrib_<u></u>location<br>
layout(location = 0)<br>
#endif<br>
out vec4 result;<br>
<br>
In the application, you'll have to call glBindFragDataLocation if GL_ARB_explicit_attrib_<u></u>location isn't supported.<br></blockquote><div><br>Ok, I think I see why you had a "yuck" reaction to the output_result() function above, but I'm having a "yuck" reaction of my own to the preprocessor stuff above, and to having a run-time decision of whether to call glBindFragDataLocation based on whether GL_ARB_explicit_attrib_location is supported. I think I can make the gl_FragColor approach look a lot cleaner when I rework the test to use multiple compilation units. Let's revisit this question after I've done the rework and see if it's still objectionable to you.<br>
</div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<br>
The other alternative, if we really care about GLSL 1.30 without OpenGL 3.0, is<br>
<br>
#define result gl_FragColor <br></blockquote><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><div><div></div><div class="h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+void<br>
+setup_fbo()<br>
+{<br>
+ piglit_require_extension("GL_<u></u>ARB_framebuffer_object");<br>
+ piglit_require_extension("GL_<u></u>ARB_texture_float");<br>
+<br>
+ GLuint fb = 0;<br>
+ GLuint color_rb = 0;<br>
+ GLenum fb_status;<br>
+<br>
+ glGenFramebuffers(1,&fb);<br>
+ glBindFramebuffer(GL_<u></u>FRAMEBUFFER, fb);<br>
+<br>
+ /* Bind color attachment. */<br>
+ glGenRenderbuffers(1,&color_<u></u>rb);<br>
+ glBindRenderbuffer(GL_<u></u>RENDERBUFFER, color_rb);<br>
+ glRenderbufferStorage(GL_<u></u>RENDERBUFFER, GL_RGBA32F,<br>
+ piglit_width, piglit_height);<br>
+ glFramebufferRenderbuffer(GL_<u></u>DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,<br>
+ GL_RENDERBUFFER, color_rb);<br>
+ piglit_check_gl_error(0, PIGLIT_FAIL);<br>
+<br>
+ fb_status = glCheckFramebufferStatus(GL_<u></u>FRAMEBUFFER);<br>
+ if (fb_status != GL_FRAMEBUFFER_COMPLETE) {<br>
+ printf("error: FBO incomplete (status = 0x%04x)\n", fb_status);<br>
+ piglit_report_result(PIGLIT_<u></u>SKIP);<br>
+ }<br>
+<br>
+ glBindFramebuffer(GL_DRAW_<u></u>FRAMEBUFFER, fb);<br>
+ glBindFramebuffer(GL_READ_<u></u>FRAMEBUFFER, fb);<br>
+}<br>
+<br>
+void<br>
+setup_xfb()<br>
+{<br>
+ piglit_require_extension("GL_<u></u>EXT_transform_feedback");<br>
</blockquote>
<br></div></div>
Even though you're requiring GL_EXT_transform_feedback, you're using the function names from OpenGL 3.0. This will fail on any system that supports the EXT but doesn't support OpenGL 3.0. I think it's best to just require OpenGL 3.0 here.<br>
<br>
The other option is to require "any" of the transform feedback flavors and have piglit_ wrapper functions.<br>
<br></blockquote><div><br>I think I'll go with the approach of just requiring OpenGL 3.0. My efforts to make the test work without OpenGL 3.0 seem misguided to me now.<br>
<br></div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ glGenBuffers(1,&xfb_buffer);<div><div></div><div class="h5"><br>
+}<br>
+<br>
+void<br>
+print_usage_and_exit(char *prog_name)<br>
+{<br>
+ printf("Usage: %s<mode>\n"<br>
+ " where<mode> is one of:\n"<br>
+ " vs_basic\n"<br>
+ " fs_basic\n"<br>
+ " vs_fbo\n"<br>
+ " vs_xfb\n"<br>
+ " fs_fbo\n", prog_name);<br>
+ exit(1);<br>
+}<br>
+<br>
+void<br>
+piglit_init(int argc, char **argv)<br>
+{<br>
+ if (argc != 2)<br>
+ print_usage_and_exit(argv[0]);<br>
+ if (strcmp(argv[1], "vs_basic") == 0) {<br>
+ use_fs = false;<br>
+ } else if (strcmp(argv[1], "fs_basic") == 0) {<br>
+ use_fs = true;<br>
+ } else if (strcmp(argv[1], "vs_fbo") == 0) {<br>
+ use_fs = false;<br>
+ use_fbo = true;<br>
+ } else if (strcmp(argv[1], "vs_xfb") == 0) {<br>
+ use_fs = false;<br>
+ use_xfb = true;<br>
+ } else if (strcmp(argv[1], "fs_fbo") == 0) {<br>
+ use_fs = true;<br>
+ use_fbo = true;<br>
+ } else {<br>
+ print_usage_and_exit(argv[0]);<br>
+ }<br>
+ precise = use_fbo || use_xfb;<br>
+<br>
+ piglit_require_GLSL();<br>
+ piglit_require_GLSL_version(<u></u>130);<br>
+<br>
+ if (use_fbo) {<br>
+ setup_fbo();<br>
+ }<br>
+ if (use_xfb) {<br>
+ setup_xfb();<br>
+ }<br>
+<br>
+ stock_vs = piglit_compile_shader_text(GL_<u></u>VERTEX_SHADER,<br>
+ stock_vs_text);<br>
+ stock_fs = piglit_compile_shader_text(GL_<u></u>FRAGMENT_SHADER,<br>
+ stock_fs_text);<br>
+}<br>
+<br>
+/**<br>
+ * enum indicating how the expression would be expected to behave on a fully<br>
+ * IEEE 754 compliant architecture. Note: since OpenGL implementations are<br>
+ * not required to respect all of IEEE 754's rules for infinities and NaN's,<br>
+ * we don't necessarily check all of these behaviors.<br>
+ */<br>
+enum behavior<br>
+{<br>
+ B_NAN = 0, /* Expected to evaluate to NaN */<br>
+ B_FINITE = 1, /* Expected to evaluate to a finite value */<br>
+ B_POSINF = 2, /* Expected to evaluate to +Infinity */<br>
+ B_NEGINF = 3, /* Expected to evaluate to -Infinity */<br>
+};<br>
+<br>
+struct expression_table_element<br>
+{<br>
+ char *expression;<br>
+ int expected_behavior;<br>
+};<br>
+<br>
+struct expression_table_element expressions[] = {<br>
+ { "1000.0", B_FINITE },<br>
+ { "1000.0+z", B_FINITE },<br>
+ { "-1000.0", B_FINITE },<br>
+ { "-1000.0+z", B_FINITE },<br>
+ { "u_inf", B_POSINF },<br>
+ { "exp(1000.0)", B_POSINF },<br>
+ { "exp(1000.0+z)", B_POSINF },<br>
+ { "u_minus_inf", B_NEGINF },<br>
+ { "-exp(1000.0)", B_NEGINF },<br>
+ { "-exp(1000.0+z)", B_NEGINF },<br>
+ { "u_nan", B_NAN },<br>
+ { "0/0", B_NAN },<br>
+ { "z/z", B_NAN },<br>
+ { "u_inf/u_minus_inf", B_NAN },<br>
+ { "z*u_inf", B_NAN },<br>
+ { "u_inf+u_minus_inf", B_NAN },<br>
+ { "log(-1.0)", B_NAN },<br>
+ { "log(-1.0+z)", B_NAN },<br>
+ { "sqrt(-1.0)", B_NAN },<br>
+ { "sqrt(-1.0+z)", B_NAN },<br>
+};<br>
+<br>
+/**<br>
+ * Draw using the shader, and then read back values using either (a) the<br>
+ * floating-point framebuffer, (b) transform feedback, or (c) pixel reads from<br>
+ * the window. Note that pixel reads from the window are only accurate to one<br>
+ * part in 255, so the caller must be careful not to rely on high precision in<br>
+ * case (c).<br>
+ */<br>
+void draw_and_readback(float *readback)<br>
+{<br>
+ if (use_xfb) {<br>
+ glBufferData(GL_TRANSFORM_<u></u>FEEDBACK_BUFFER, 4096, NULL,<br>
+ GL_DYNAMIC_COPY);<br>
+ glBindBufferBase(GL_TRANSFORM_<u></u>FEEDBACK_BUFFER, 0, xfb_buffer);<br>
+ glEnable(GL_RASTERIZER_<u></u>DISCARD);<br>
+ glBeginTransformFeedback(GL_<u></u>TRIANGLES);<br>
+ }<br>
+<br>
+ piglit_draw_rect(-1, -1, 2, 2);<br>
+<br>
+ if (use_xfb) {<br>
+ glEndTransformFeedback();<br>
+ memcpy(readback,<br>
+ glMapBuffer(GL_TRANSFORM_<u></u>FEEDBACK_BUFFER, GL_READ_ONLY),<br>
+ 4*sizeof(float));<br>
+ glUnmapBuffer(GL_TRANSFORM_<u></u>FEEDBACK_BUFFER);<br>
+ } else {<br>
+ glReadPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, readback);<br>
+ }<br>
+}<br>
+<br>
+/**<br>
+ * Test the given expression, to make sure its behavior is self-consistent and<br>
+ * consistent with the expected behavior.<br>
+ */<br>
+bool test_expr(char *expression, int expected_behavior)<br>
+{<br>
+ char shader_text[4096];<br>
+ GLint shader;<br>
+ GLint prog;<br>
+ float readback[4];<br>
+ float value;<br>
+ bool isinf_in_shader;<br>
+ bool isnan_in_shader;<br>
+ int sign_in_shader;<br>
+ float delta;<br>
+ bool greater_than_zero;<br>
+ bool pass = true;<br>
+ char *expected_behavior_string;<br>
+<br>
+ /* Create and link a program specifically to test this expression */<br>
+ prog = piglit_CreateProgram();<br>
+ if (use_fs) {<br>
+ sprintf(shader_text, shader_template, fs_boilerplate,<br>
+ expression);<br>
+ shader = piglit_compile_shader_text(GL_<u></u>FRAGMENT_SHADER,<br>
+ shader_text);<br>
+ piglit_AttachShader(prog, stock_vs);<br>
+ piglit_AttachShader(prog, shader);<br>
+ } else {<br>
+ sprintf(shader_text, shader_template, vs_boilerplate,<br>
+ expression);<br>
+ shader = piglit_compile_shader_text(GL_<u></u>VERTEX_SHADER,<br>
+ shader_text);<br>
+ piglit_AttachShader(prog, shader);<br>
+ piglit_AttachShader(prog, stock_fs);<br>
+ }<br>
+ if (use_xfb) {<br>
+ static const char *var_name = "data";<br>
+ glTransformFeedbackVaryings(<u></u>prog, 1,&var_name,<br>
+ GL_SEPARATE_ATTRIBS);<br>
+ glBindBuffer(GL_TRANSFORM_<u></u>FEEDBACK_BUFFER, xfb_buffer);<br>
+ }<br>
+ piglit_LinkProgram(prog);<br>
+ piglit_DeleteShader(shader);<br>
+ piglit_UseProgram(prog);<br>
+<br>
+ /* Set up uniforms */<br>
+ piglit_Uniform1f(piglit_<u></u>GetUniformLocation(prog, "z"), 0.0);<br>
+ piglit_Uniform1f(piglit_<u></u>GetUniformLocation(prog, "u_inf"), 1.0/0.0);<br>
+ piglit_Uniform1f(piglit_<u></u>GetUniformLocation(prog, "u_minus_inf"),<br>
+ -1.0/0.0);<br>
+ piglit_Uniform1f(piglit_<u></u>GetUniformLocation(prog, "u_nan"), 0.0/0.0);<br>
+<br>
+ /* Use one draw call to read out value, isinf(value), isnan(value),<br>
+ * and sign(value).<br>
+ */<br>
+ piglit_Uniform1f(piglit_<u></u>GetUniformLocation(prog, "ref"), 0.0);<br>
+ piglit_Uniform1i(piglit_<u></u>GetUniformLocation(prog, "mode"), 0);<br>
+ draw_and_readback(readback);<br>
+ value = readback[0];<br>
+ isinf_in_shader = readback[1]> 0.5;<br>
+ isnan_in_shader = readback[2]> 0.5;<br>
+ sign_in_shader = (int) (2.0*readback[3] + 0.5) - 1;<br>
+<br>
+ /* Use a second draw call to feed value back into the shader, and read<br>
+ * out (value> 0) and delta.<br>
+ */<br>
+ piglit_Uniform1f(piglit_<u></u>GetUniformLocation(prog, "ref"), value);<br>
+ piglit_Uniform1i(piglit_<u></u>GetUniformLocation(prog, "mode"), 1);<br>
+ draw_and_readback(readback);<br>
+ greater_than_zero = readback[0]> 0.5;<br>
+ delta = readback[1];<br>
+<br>
+ /* Check that the behavior was as expected */<br>
+ switch (expected_behavior) {<br>
+ case B_FINITE:<br>
+ expected_behavior_string = "finite";<br>
+ if (isinf_in_shader || isnan_in_shader) {<br>
+ /* Expected finite, got Inf or NaN */<br>
+ pass = false;<br>
+ }<br>
+ break;<br>
+ case B_POSINF:<br>
+ expected_behavior_string = "+Inf";<br></div></div>
+ if (!isnan_in_shader&& sign_in_shader != 1.0) {<div class="im"><br>
+ /* Expected positive or NaN, got<= 0 */<br>
+ pass = false;<br>
+ }<br>
+ break;<br>
+ case B_NEGINF:<br>
+ expected_behavior_string = "-Inf";<br></div>
+ if (!isnan_in_shader&& sign_in_shader != -1.0) {<div class="im"><br>
+ /* Expected negative or NaN, got>= 0 */<br>
+ pass = false;<br>
+ }<br>
+ break;<br>
+ default:<br>
+ expected_behavior_string = "NaN";<br>
+ break;<br>
+ }<br>
+<br>
+ /* Do other sanity checks */<br></div>
+ if (isnan_in_shader&& isinf_in_shader) {<div><div></div><div class="h5"><br>
+ /* No value can be simultaneously Inf and NaN */<br>
+ pass = false;<br>
+ }<br>
+ if (!isnan_in_shader) {<br>
+ if (sign_in_shader == -1 || sign_in_shader == 0) {<br>
+ if (greater_than_zero) {<br>
+ /* sign(value) inconsistent with (value>0) */<br>
+ pass = false;<br>
+ }<br>
+ } else if (sign_in_shader == 1) {<br>
+ if (!greater_than_zero) {<br>
+ /* sign(value) inconsistent with (value>0) */<br>
+ pass = false;<br>
+ }<br>
+ } else {<br>
+ /* Illegal return value for sign() */<br>
+ pass = false;<br>
+ }<br>
+ }<br>
+<br>
+ /* If we are using a high-precision technique to read data out of the<br>
+ * shader (fbo or xfb), check the behavior of isinf and isnan against<br>
+ * their C counterparts, and verify that delta ~= 0 for finite values.<br>
+ */<br>
+ if (precise) {<br>
+ bool isinf_in_c = !!isinf(value);<br>
+ bool isnan_in_c = !!isnan(value);<br>
+ if (isinf_in_shader != isinf_in_c ||<br>
+ isnan_in_shader != isnan_in_c) {<br>
+ /* Result of isinf() and isnan() in the shader did not<br>
+ * match the result in C code.<br>
+ */<br>
+ pass = false;<br>
+ }<br></div></div>
+ if (!isinf_in_shader&& !isnan_in_shader) {<div class="im"><br>
+ float threshold = fabs(value * 1e-6);<br>
+ if (isinf(delta) || isnan(delta) ||<br>
+ fabs(delta)> threshold) {<br>
+ /* The shader and C code agree that the value<br>
+ * was finite, but it isn't behaving as a nice<br>
+ * finite value should.<br>
+ */<br>
+ pass = false;<br>
+ }<br>
+ }<br>
+ }<br>
+<br>
+ /* Output a line for the results table */<br>
+ printf("%17s %6s %5s %5s %4d %5s ",<br>
+ expression,<br>
+ expected_behavior_string,<br>
+ isinf_in_shader ? "true" : "false",<br>
+ isnan_in_shader ? "true" : "false",<br>
+ sign_in_shader,<br>
+ greater_than_zero ? "true" : "false");<br>
+ if (precise) {<br>
+ printf("%12f %12f ", value, delta);<br>
+ }<br>
+ printf("%s\n", pass ? "OK" : "FAIL");<br>
</div></blockquote>
<br>
We should probably only log messages for failure or when piglit_automatic is not set.<br>
<br></blockquote><div><br>Normally I would agree, but considering the amount of slop the GLSL spec allows in what generates NaN/Inf and what doesn't, it's really useful in diagnosing a failure to click on the test in the Piglit result window and see the behavior of both the passing and failing test cases. <br>
</div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
+<br>
+ piglit_UseProgram(0);<br>
+ piglit_DeleteProgram(prog);<br>
+<br>
+ return pass;<br>
+}<br>
+<br>
+enum piglit_result<br>
+piglit_display()<br>
+{<br>
+ int i;<br>
+ bool pass = true;<br>
+<br>
+ printf(" expression expect isinf isnan sign>0?");<br>
+ if (precise)<br>
+ printf(" value delta");<br>
+ printf("\n");<br>
+<br>
+ for (i = 0; i< sizeof(expressions)/sizeof(*<u></u>expressions); ++i) {<br>
+ pass = test_expr(expressions[i].<u></u>expression,<br></div>
+ expressions[i].expected_<u></u>behavior)&& pass;<div class="im"><br>
+ }<br>
+<br>
+ return pass ? PIGLIT_PASS : PIGLIT_FAIL;<br>
+}<br>
</div></blockquote>
</blockquote></div><br>I'll send out a v2 patch once I've reworked the test.<br>