[Piglit] [RFC] ARB_shader_precision tests

Micah Fedke micah.fedke at collabora.co.uk
Mon Aug 18 08:27:00 PDT 2014


Hi all,

I'm working on adding ARB_shader_precision tests to piglit.  My primary goal
is ensure that the intel driver supports this GL 4.x feature correctly, but
the tests really ought to work for all mesa drivers, where possible.  The
GL_ARB_shader_precision spec has a list of rather innocent looking built-in
functions and the associated precision requirements but as I'm finding out,
things get a bit "combinatorial".  Floats have a number of corner cases 
(NaNs,
Infs, subnormals, zeros, etc.) and it seems important to me to make sure 
that
the compiler/hardware is performing correctly for each of these cases, for
each built-in required by GL_ARB_shader_precision.  There is also a much
larger implied list of complex built-in functions that are constructed 
from a
sequence of simple built-ins (eg. dot, cross, etc.), which will also need to
be tested to ensure that they meet the precision requirements in the spec.

To this end, I'm proposing to write a sub-suite of tests, located under
tests/spec/arb_shader_precision.  This has been done before, but this suite
has the potential to grow quite large, so I figure it's good to start the
review process early :)

I've pasted a patch that describes a framework and a sample test
(divide-by-zero) below.  The test parameters test some of the limits but not
all, and will need to be fleshed out further.

The framework has been lightly optimized to reduce the manual labor 
associated
with adding each new test for a built-in, but I'm sure it could use a 
few more
optimization passes.  I wanted to get most of the tests added first to find
out just how much they had in common, and then revisit the optimization 
topic.

The framework is basically just a couple of helper functions that 
encapsulate
commonly used code.  Tests are individual .c files that compile to 
individual
tests.  The framework is a statically linked library compiled into each 
test.
The framework includes a function for setting up an unclamped output
framebuffer so that any floating point value can be passed through as an
output, a function that loads a list of test parameters from a (very rough)
text-based data file format, and a function that executes precision tests
using an array of test parameters and a vert and a frag shader as inputs.

The basic goal of the precision test function is to calculate the operation
with the given parameters on both the cpu (using libc) and on the gpu (using
the supplied shaders), and then compare the results.  The operation is 
tested
in both a vert and a frag shader to cover those cases where the operation is
implemented differently depending on the stage.  Most requirements specify a
maximum tolerance in ULPs, so this value can be passed as a parameter to the
precision test function.  Sometimes merely "the correct result", or "correct
rounding" is required, either of which can be tested by setting the ULPs
difference to zero for that test, requiring an exact match.  An interesting
by-product of this design is that both success and failure cases can be
specified in the params file - as long as the shader produces a result that
matches the result produced by the cpu, the test will pass.  This comes in
handy when testing comparison operators.

Please let me know if I'm heading in the right direction with this. I 
will be
glad to consider revisions and recommendations to this project, especially
from those with floating point experience, as I am a bit new to the topic.


Thanks,

Micah Fedke
Collabora, Ltd.


diff --git a/tests/shader_precision.py b/tests/shader_precision.py
new file mode 100644
index 0000000..0997dde
--- /dev/null
+++ b/tests/shader_precision.py
@@ -0,0 +1,23 @@
+#
+# Minimal tests to check whether the installation is working
+#
+
+from framework.profile import TestProfile
+from framework.exectest import PiglitTest
+
+__all__ = ['profile']
+
+profile = TestProfile()
+profile.tests['spec/arb_shader_precision-divide-by-zero'] = 
PiglitTest('arb_shader_precision-divide-by-zero')
diff --git a/tests/spec/CMakeLists.txt b/tests/spec/CMakeLists.txt
index 489ff0c..902307b 100644
--- a/tests/spec/CMakeLists.txt
+++ b/tests/spec/CMakeLists.txt
@@ -36,6 +36,7 @@ add_subdirectory (arb_separate_shader_objects)
  add_subdirectory (arb_shader_texture_lod/execution)
  add_subdirectory (arb_shader_atomic_counters)
  add_subdirectory (arb_shader_objects)
+add_subdirectory (arb_shader_precision)
  add_subdirectory (arb_shading_language_420pack/execution)
  add_subdirectory (arb_stencil_texturing)
  add_subdirectory (arb_sync)
diff --git a/tests/spec/arb_shader_precision/CMakeLists.gl.txt 
b/tests/spec/arb_shader_precision/CMakeLists.gl.txt
new file mode 100644
index 0000000..2aa6216
--- /dev/null
+++ b/tests/spec/arb_shader_precision/CMakeLists.gl.txt
@@ -0,0 +1,27 @@
+include_directories(
+    ${GLEXT_INCLUDE_DIR}
+    ${OPENGL_INCLUDE_PATH}
+)
+
+link_libraries (
+    piglitutil_${piglit_target_api}
+    ${OPENGL_gl_LIBRARY}
+    ${OPENGL_glu_LIBRARY}
+)
+
+add_library(common OBJECT common.c)
+piglit_add_executable (arb_shader_precision-divide-by-zero 
$<TARGET_OBJECTS:common> divide-by-zero.c)
+
+# vim: ft=cmake:
diff --git a/tests/spec/arb_shader_precision/CMakeLists.txt 
b/tests/spec/arb_shader_precision/CMakeLists.txt
new file mode 100644
index 0000000..144a306
--- /dev/null
+++ b/tests/spec/arb_shader_precision/CMakeLists.txt
@@ -0,0 +1 @@
+piglit_include_target_api()
diff --git a/tests/spec/arb_shader_precision/common.c 
b/tests/spec/arb_shader_precision/common.c
new file mode 100644
index 0000000..5c6f93d
--- /dev/null
+++ b/tests/spec/arb_shader_precision/common.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2014 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the 
"Software"),
+ * to deal in the Software without restriction, including without 
limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons 
to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the 
next
+ * paragraph) shall be included in all copies or substantial portions 
of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL VMWARE AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#include "common.h"
+#include "piglit-util-gl-common.h"
+
+
+int
+setup_unclamped_output(void)
+{
+   int ret = 0;
+   GLuint fb;
+
+   GLuint rb;
+
+
+   //clear glGetError()
+   glGetError();
+
+   glGenFramebuffers(1, &fb);
+   ret = glGetError();
+   if (ret) {
+      printf("glGenFramebuffer error: %x\n", ret);
+      return ret;
+   }
+
+   glBindFramebuffer(GL_FRAMEBUFFER, fb);
+   ret = glGetError();
+   if (ret) {
+      printf("glBindFramebuffer error: %x\n", ret);
+      return ret;
+   }
+
+   glGenRenderbuffers(1, &rb);
+   ret = glGetError();
+   if (ret) {
+      printf("glGenRenderbuffers error: %x\n", ret);
+      return ret;
+   }
+
+   glBindRenderbuffer(GL_RENDERBUFFER, rb);
+   ret = glGetError();
+   if (ret) {
+      printf("glBindRenderbuffer error: %x\n", ret);
+      return ret;
+   }
+
+   glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, 2, 2);
+   ret = glGetError();
+   if (ret) {
+      printf("glRenderbufferStorage error: %x\n", ret);
+      return ret;
+   }
+
+   glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                             GL_RENDERBUFFER, rb);
+   ret = glGetError();
+   if (ret) {
+      printf("glFramebufferRenderbuffer error: %x\n", ret);
+      return ret;
+   }
+
+   return ret;
+}
+
+float *
+load_and_alloc_params(const char *name, unsigned int *num_params)
+{
+   float *retarray = NULL;
+   FILE *fp;
+   char *filename;
+   unsigned int filename_sz;
+   char prefix[] = "tests/spec/arb_shader_precision/";
+   char suffix[] = ".params";
+   int scanfret = 0;
+   unsigned int count = 0;
+   float val;
+   bool done = false;
+
+   filename_sz = strlen(prefix) + strlen(name) + strlen(suffix) + 1;
+   filename = (char *) malloc(filename_sz);
+   memset(filename, 0, filename_sz);
+   sprintf(filename, "%s%s%s", prefix, name, suffix);
+
+   fp = fopen(filename, "r");
+
+   if (fp) {
+      //parse text into array of floats
+      //sorry, not very robust
+      //line must lead with a float val in "d.ddde+-dd" format, 
comments may trail
+      do {
+         scanfret = fscanf(fp, "%e", &val);
+         if (scanfret != EOF) {
+            retarray = realloc(retarray, (count + 1) * sizeof(float));
+            retarray[count++] = val;
+            fscanf(fp, "%*[^\n]%*c");   //comments and endline
+         } else {
+            done = true;
+         }
+      } while (!done);
+      fclose(fp);
+   } else {
+      printf("could not open param file: \"%s\"! ", filename);
+   }
+
+   *num_params = count;
+
+   free(filename);
+
+   return retarray;
+}
+
+bool
+precision_test_fv(const unsigned int prog, const char *uniformname,
+                  const float_u * params, const unsigned int nmemb,
+                  const precision_func_t * pf)
+{
+   float_u expected;
+   float_u probed[4];
+   int param_location;
+   uint32_t vert_ulps_diff;
+   uint32_t frag_ulps_diff;
+   bool vert_sign_match;
+   bool frag_sign_match;
+   bool vert_result = GL_TRUE;
+   bool frag_result = GL_TRUE;
+   bool overall_result = GL_TRUE;
+   int i;
+   GLenum glerr;
+
+
+   param_location = glGetUniformLocation(prog, uniformname);
+   glerr = glGetError();
+   if (glerr) {
+      printf
+         ("failed to get uniform location for uniform: \"%s\", test 
failed! \n",
+          uniformname);
+      return false;
+   }
+   //TODO set fenv defaults?
+
+   for (i = 0; i < nmemb; i += pf->func_type) {
+      //calculate result on cpu
+      switch (pf->func_type) {
+      case ONE_PARAM:
+         expected = (float_u) pf->func_one_param(params[i].f);
+         glUniform1f(param_location, params[i].f);
+         glerr = glGetError();
+         if (glerr) {
+            printf
+               ("failed to set uniform value: %1.8e, location: %d %x \n",
+                params[i].f, param_location, glerr);
+            return false;
+         }
+         printf("%s(%1.8e)(%x): ", pf->name,
+                params[i].f, params[i].u);
+         break;
+      case TWO_PARAM:
+         expected =
+            (float_u) pf->func_two_param(params[i].f, params[i + 1].f);
+         glUniform2fv(param_location, 1, (GLfloat *) & params[i].f);
+         glerr = glGetError();
+         if (glerr) {
+            printf
+               ("failed to set uniform values: %1.8e %1.8e, %x \n",
+                params[i].f, params[i + 1].f, glerr);
+            return false;
+         }
+         printf("%s(%1.8e, %1.8e)(%x, %x): ", pf->name,
+                params[i].f, params[i + 1].f,
+                params[i].u, params[i + 1].u);
+         break;
+      case THREE_PARAM:
+         expected =
+            (float_u) pf->func_three_param(params[i].f, params[i + 1].f,
+                                           params[i + 2].f);
+         glUniform3fv(param_location, 1, (GLfloat *) & params[i].f);
+         glerr = glGetError();
+         if (glerr) {
+            printf
+               ("failed to set uniform values: %1.8e %1.8e %1.8e, %x \n",
+                params[i].f, params[i + 1].f, params[i + 2].f, glerr);
+            return false;
+         }
+         printf("%s(%1.8e, %1.8e, %1.8e)(%x, %x, %x): ", pf->name,
+                params[i].f, params[i + 1].f, params[i + 2].f,
+                params[i].u, params[i + 1].u, params[i + 2].u);
+         break;
+      default:
+         printf("unrecognized func_type, test failed! ");
+         return false;
+      }
+
+      glClear(GL_COLOR_BUFFER_BIT);
+      piglit_draw_rect(0, 0, WIDTH, HEIGHT);
+
+      glReadPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, (float *) probed);
+
+      if (probed[0].fields.sign != expected.fields.sign) {
+         vert_sign_match = false;
+         vert_result = GL_FALSE;
+      } else {
+         vert_sign_match = true;
+         vert_ulps_diff = abs(probed[0].u - expected.u);
+         vert_result = vert_ulps_diff <= pf->ulps;
+      }
+
+      if (probed[1].fields.sign != expected.fields.sign) {
+         frag_sign_match = false;
+         frag_result = GL_FALSE;
+      } else {
+         frag_sign_match = true;
+         frag_ulps_diff = abs(probed[1].u - expected.u);
+         frag_result = frag_ulps_diff <= pf->ulps;
+      }
+
+      printf("cpu: %1.8e (%x) "
+             "vert: %1.8e (%x) sign match: %u ulp diff: %u(%u) passed: %d "
+             "frag: %1.8e (%x) sign match: %u ulp diff: %u(%u) passed: 
%d \n",
+             expected.f, expected.u,
+             probed[0].f, probed[0].u, vert_sign_match,
+             vert_ulps_diff, pf->ulps, vert_result,
+             probed[1].f, probed[1].u, frag_sign_match,
+             frag_ulps_diff, pf->ulps, frag_result);
+
+      overall_result = overall_result && vert_result && frag_result;
+   }
+
+   return overall_result;
+}
diff --git a/tests/spec/arb_shader_precision/common.h 
b/tests/spec/arb_shader_precision/common.h
new file mode 100644
index 0000000..a145050
--- /dev/null
+++ b/tests/spec/arb_shader_precision/common.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the 
"Software"),
+ * to deal in the Software without restriction, including without 
limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons 
to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the 
next
+ * paragraph) shall be included in all copies or substantial portions 
of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL VMWARE AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define WIDTH 1
+#define HEIGHT 1
+
+typedef union
+{
+   float f;
+   int32_t i;
+   uint32_t u;
+   //portability: works for small endian only :|
+   struct
+   {
+      uint32_t significand:23;
+      uint32_t exponent:8;
+      uint32_t sign:1;
+   } fields;
+
+} float_u;
+
+typedef enum
+{
+   ONE_PARAM = 1,
+   TWO_PARAM,
+   THREE_PARAM
+} func_type_e;
+
+typedef struct
+{
+   char *name;
+   func_type_e func_type;
+   union
+   {
+      float (*func_one_param) (float);
+      float (*func_two_param) (float, float);
+      float (*func_three_param) (float, float, float);
+   };
+   unsigned int ulps;
+} precision_func_t;
+
+
+int setup_unclamped_output(void);
+float *load_and_alloc_params(const char *name, unsigned int *num_params);
+bool precision_test_fv(const unsigned int prog, const char *uniformname,
+                       const float_u * params, const unsigned int nmemb,
+                       const precision_func_t * pf);
diff --git a/tests/spec/arb_shader_precision/divide-by-zero.c 
b/tests/spec/arb_shader_precision/divide-by-zero.c
new file mode 100644
index 0000000..68ee756
--- /dev/null
+++ b/tests/spec/arb_shader_precision/divide-by-zero.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2014 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the 
"Software"),
+ * to deal in the Software without restriction, including without 
limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons 
to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the 
next
+ * paragraph) shall be included in all copies or substantial portions 
of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT.  IN NO EVENT SHALL VMWARE AND/OR THEIR SUPPLIERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/**
+ * Test: divide-by-zero
+ * Tests "Dividing by 0 results in the appropriately signed IEEE Inf." from
+ * ARB_shader_precision.
+ */
+
+#include <string.h>
+
+#include "piglit-util-gl-common.h"
+#include "common.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+   config.supports_gl_core_version = 33;
+
+   config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+static char *TestName = "divide-by-zero";
+static GLuint Prog;
+
+
+static float
+divide_by_zero(float f)
+{
+   return f / 0.0f;
+}
+
+enum piglit_result
+piglit_display(void)
+{
+   float *params;
+   unsigned int num_params;
+   GLboolean overall_result;
+   precision_func_t pf = { 0 };
+
+   pf.name = TestName;
+   pf.func_type = ONE_PARAM;
+   pf.func_one_param = divide_by_zero;
+   pf.ulps = 3;
+
+   params = load_and_alloc_params(TestName, &num_params);
+
+   overall_result =
+      precision_test_fv(Prog, "param", (float_u *) params, num_params, 
&pf);
+
+   printf("%s: overall test result: %d\n", TestName, overall_result);
+   piglit_present_results();
+   piglit_report_result(overall_result ? PIGLIT_PASS : PIGLIT_FAIL);
+   return overall_result ? PIGLIT_PASS : PIGLIT_FAIL;
+}
+
+
+void
+piglit_init(int argc, char **argv)
+{
+   const char *verttext =
+      "#version 330\n"
+      "#extension GL_ARB_SHADER_PRECISION : enable\n"
+      "uniform float param;\n"
+      "in vec4 piglit_vertex;\n"
+      "out float vertresult;\n"
+      "void main(void) \n"
+      "{ \n"
+      "   vertresult = param/0.0f;\n"
+      "   gl_Position = piglit_vertex;\n"
+      "} \n";
+   const char *fragtext =
+      "#version 330\n"
+      "#extension GL_ARB_SHADER_PRECISION : enable\n"
+      "uniform float param;\n"
+      "in float vertresult;\n"
+      "out vec4 outputColor;\n"
+      "void main(void) \n"
+      "{ \n"
+      "   float fragresult = param/0.0f;\n"
+      "   outputColor = vec4(vertresult, fragresult, param, 1.0);\n"
+      "} \n";
+
+   //piglit_require_extension("GL_ARB_fragment_shader");
+
+   if (piglit_width < WIDTH || piglit_height < HEIGHT) {
+      printf("%s: window is too small.\n", TestName);
+      exit(1);
+   }
+
+   if (setup_unclamped_output()) {
+      printf("%s: failed to setup unclamped output, exiting\n", TestName);
+      exit(1);
+   }
+
+   Prog = piglit_build_simple_program(verttext, fragtext);
+   glUseProgram(Prog);
+
+   glViewport(0, 0, WIDTH, HEIGHT);
+
+}
diff --git a/tests/spec/arb_shader_precision/divide-by-zero.params 
b/tests/spec/arb_shader_precision/divide-by-zero.params
new file mode 100644
index 0000000..92817a0
--- /dev/null
+++ b/tests/spec/arb_shader_precision/divide-by-zero.params
@@ -0,0 +1,4 @@
+3.40282347e+38 //most positive float (FLT_MAX)
+-3.40282347e+38 //most negative float (-FLT_MAX)
+1.17549435e-38 //least positive normalized float (FLT_MIN)
+-1.17549435e-38 //least negative normalized float (-FLT_MIN)


More information about the Piglit mailing list