On 27 October 2011 10:32, Ian Romanick <span dir="ltr">&lt;<a href="mailto:idr@freedesktop.org">idr@freedesktop.org</a>&gt;</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/20/2011 02:12 PM, Paul Berry wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
This patch adds the ability for shader_runner tests to include a<br>
&quot;[vertex data]&quot; section containing data in columnar format, for example:<br>
<br>
[vertex data]<br>
vertex/float/3 foo/uint/1 bar/int/2<br>
0.0 0.0 0.0    0xe0000000 0 0<br>
0.0 1.0 0.0    0x70000000 1 1<br>
1.0 1.0 0.0    0x00000000 0 1<br>
<br>
Each column header is of the form ATTRNAME/TYPE/COUNT, where ATTRNAME<br>
is the name of the vertex attribute to be bound to this column, TYPE<br>
is the type of data that follows (&quot;float&quot;, &quot;int&quot;, or &quot;uint&quot;), and<br>
COUNT is the vector length of the data (e.g. &quot;3&quot; for vec3 data).<br>
<br>
To send vertex data to the shader, use the new shader_runner command<br>
&quot;draw arrays&quot;.  The parameters are the same as for the glDrawArrays()<br>
command, so for example to draw triangle primitives using 3 elements<br>
from the vertex data array starting at element 0, use the command:<br>
<br>
draw arrays GL_TRIANGLES 0 3<br>
<br>
More detailed examples can be found in the tests/shaders/vbo<br>
directory.<br>
<br>
The implementation is largely in a new file, piglit-vbo.cpp, so that<br>
it can be re-used by other piglit tests if necessary.<br>
---<br>
  tests/shaders/shader_runner.c                   |   80 ++++<br>
  tests/shaders/vbo/vbo-generic-<u></u>float.shader_test |   37 ++<br>
  tests/shaders/vbo/vbo-generic-<u></u>int.shader_test   |   39 ++<br>
  tests/shaders/vbo/vbo-generic-<u></u>uint.shader_test  |   39 ++<br>
  tests/util/CMakeLists.gl.txt                    |    1 +<br>
  tests/util/piglit-vbo.cpp                       |  514 +++++++++++++++++++++++<br>
  tests/util/piglit-vbo.h                         |   35 ++<br>
  7 files changed, 745 insertions(+), 0 deletions(-)<br>
  create mode 100644 tests/shaders/vbo/vbo-generic-<u></u>float.shader_test<br>
  create mode 100644 tests/shaders/vbo/vbo-generic-<u></u>int.shader_test<br>
  create mode 100644 tests/shaders/vbo/vbo-generic-<u></u>uint.shader_test<br>
  create mode 100644 tests/util/piglit-vbo.cpp<br>
  create mode 100644 tests/util/piglit-vbo.h<br>
<br>
diff --git a/tests/shaders/shader_runner.<u></u>c b/tests/shaders/shader_runner.<u></u>c<br>
index b07ff55..77a753c 100644<br>
--- a/tests/shaders/shader_runner.<u></u>c<br>
+++ b/tests/shaders/shader_runner.<u></u>c<br>
@@ -37,6 +37,7 @@<br>
  #include&lt;libgen.h&gt;<br>
  #endif<br>
  #include &quot;piglit-util.h&quot;<br>
+#include &quot;piglit-vbo.h&quot;<br>
<br>
  int piglit_width = 250, piglit_height = 250;<br>
  int piglit_window_mode = GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE;<br>
@@ -65,7 +66,10 @@ unsigned num_fragment_shaders = 0;<br>
  char *shader_strings[256];<br>
  GLsizei shader_string_sizes[256];<br>
  unsigned num_shader_strings = 0;<br>
+const char *vertex_data_start = NULL;<br>
+const char *vertex_data_end = NULL;<br>
  GLuint prog;<br>
+size_t num_vbo_rows = 0;<br>
<br>
  enum states {<br>
        none = 0,<br>
@@ -79,6 +83,7 @@ enum states {<br>
        fragment_shader,<br>
        fragment_shader_file,<br>
        fragment_program,<br>
+       vertex_data,<br>
        test,<br>
  };<br>
<br>
@@ -473,6 +478,10 @@ leave_state(enum states state, const char *line)<br>
        case fragment_program:<br>
                break;<br>
<br>
+       case vertex_data:<br>
+               vertex_data_end = line;<br>
+               break;<br>
+<br>
        case test:<br>
                break;<br>
<br>
@@ -592,6 +601,9 @@ process_test_script(const char *script_name)<br>
                                state = fragment_shader_file;<br>
                                shader_strings[0] = NULL;<br>
                                num_shader_strings = 0;<br>
+                       } else if (strncmp(line, &quot;[vertex data]&quot;, 13) == 0) {<br>
+                               state = vertex_data;<br>
+                               vertex_data_start = NULL;<br>
                        } else if (strncmp(line, &quot;[test]&quot;, 6) == 0) {<br>
                                test_start = strchrnul(line, &#39;\n&#39;);<br>
                                if (test_start[0] != &#39;\0&#39;)<br>
@@ -625,6 +637,11 @@ process_test_script(const char *script_name)<br>
                                    load_shader_file(line);<br>
                                break;<br>
<br>
+                       case vertex_data:<br>
+                               if (vertex_data_start == NULL)<br>
+                                       vertex_data_start = line;<br>
+                               break;<br>
+<br>
                        case test:<br>
                                break;<br>
                        }<br>
@@ -884,6 +901,43 @@ draw_instanced_rect(int primcount, float x, float y, float w, float h)<br>
        glDisableClientState(GL_<u></u>VERTEX_ARRAY);<br>
  }<br>
<br>
+<br>
+struct mode_table {<br>
+       const char *name;<br>
+       GLenum value;<br>
+} mode_table[] = {<br>
+       { &quot;GL_POINTS&quot;,         GL_POINTS         },<br>
+       { &quot;GL_LINE_STRIP&quot;,     GL_LINE_STRIP     },<br>
+       { &quot;GL_LINE_LOOP&quot;,      GL_LINE_LOOP      },<br>
+       { &quot;GL_LINES&quot;,          GL_LINES          },<br>
+       { &quot;GL_POLYGON&quot;,        GL_POLYGON        },<br>
+       { &quot;GL_TRIANGLE_STRIP&quot;, GL_TRIANGLE_STRIP },<br>
+       { &quot;GL_TRIANGLE_FAN&quot;,   GL_TRIANGLE_FAN   },<br>
+       { &quot;GL_TRIANGLES&quot;,      GL_TRIANGLES      },<br>
+       { &quot;GL_QUAD_STRIP&quot;,     GL_QUAD_STRIP     },<br>
+       { &quot;GL_QUADS&quot;,          GL_QUADS          },<br>
+       { NULL, 0 }<br>
+};<br>
+<br>
+<br>
+GLenum<br>
+decode_mode(const char *mode_str)<br>
+{<br>
+       int i;<br>
+<br>
+       for (i = 0; mode_table[i].name; ++i) {<br>
+               if (0 == strcmp(mode_str, mode_table[i].name))<br>
+                       return mode_table[i].value;<br>
+       }<br>
+<br>
+       printf(&quot;unknown drawing mode \&quot;%s\&quot;&quot;, mode_str);<br>
+       piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+<br>
+       /* Should not be reached, but return 0 to avoid compiler warning */<br>
+       return 0;<br>
+}<br>
+<br>
+<br>
  enum piglit_result<br>
  piglit_display(void)<br>
  {<br>
@@ -900,6 +954,7 @@ piglit_display(void)<br>
                float c[32];<br>
                double d[4];<br>
                int x, y, w, h, l, tex, level;<br>
+               char s[32];<br>
<br>
                line = eat_whitespace(line);<br>
<br>
@@ -927,6 +982,28 @@ piglit_display(void)<br>
                        &amp;primcount,<br>
                               c + 0, c + 1, c + 2, c + 3);<br>
                        draw_instanced_rect(primcount, c[0], c[1], c[2], c[3]);<br>
+               } else if (sscanf(line, &quot;draw arrays %31s %d %d&quot;, s,&amp;x,&amp;y)) {<br>
+                       GLenum mode = decode_mode(s);<br>
+                       int first = x;<br>
+                       size_t count = (size_t) y;<br>
+                       if (first&lt;  0) {<br>
+                               printf(&quot;draw arrays &#39;first&#39; must be&gt;= 0\n&quot;);<br>
+                               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+                       } else if ((size_t) first&gt;= num_vbo_rows) {<br>
+                               printf(&quot;draw arrays &#39;first&#39; must be&lt;  %lu\n&quot;,<br>
+                                      num_vbo_rows);<br>
+                               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+                       }<br>
+                       if (count&lt;= 0) {<br>
+                               printf(&quot;draw arrays &#39;count&#39; must be&gt;  0\n&quot;);<br>
+                               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+                       } else if (count&gt;  num_vbo_rows - (size_t) first) {<br>
+                               printf(&quot;draw arrays cannot draw beyond %lu\n&quot;,<br>
+                                      num_vbo_rows);<br>
+                               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+                       }<br>
+                       /* TODO: wrapper? */<br>
+                       glDrawArrays(mode, first, count);<br>
                } else if (string_match(&quot;disable&quot;, line)) {<br>
                        do_enable_disable(line + 7, false);<br>
                } else if (string_match(&quot;enable&quot;, line)) {<br>
@@ -1523,4 +1600,7 @@ piglit_init(int argc, char **argv)<br>
<br>
        process_test_script(argv[1]);<br>
        link_and_use_shaders();<br>
+       if (vertex_data_start != NULL)<br>
+               num_vbo_rows = setup_vbo_from_text(prog, vertex_data_start,<br>
+                                                  vertex_data_end);<br>
  }<br>
diff --git a/tests/shaders/vbo/vbo-<u></u>generic-float.shader_test b/tests/shaders/vbo/vbo-<u></u>generic-float.shader_test<br>
new file mode 100644<br>
index 0000000..8982211<br>
--- /dev/null<br>
+++ b/tests/shaders/vbo/vbo-<u></u>generic-float.shader_test<br>
@@ -0,0 +1,37 @@<br>
+[require]<br>
+GLSL&gt;= 1.10<br>
+GL&gt;= 2.1<br>
+<br>
+[vertex shader]<br>
+attribute vec4 vertex;<br>
+attribute float foo;<br>
+attribute vec2 bar;<br>
+<br>
+void main()<br>
+{<br>
+       gl_Position = gl_ModelViewProjectionMatrix * vertex;<br>
+       gl_FrontColor = vec4(foo, bar, 1.0);<br>
+}<br>
+<br>
+[fragment shader]<br>
+void main()<br>
+{<br>
+       gl_FragColor = gl_Color;<br>
+}<br>
+<br>
+[vertex data]<br>
+vertex/float/3 foo/float/1 bar/float/2<br>
+0.0 0.0 0.0    1.0         0.0 0.0<br>
+0.0 1.0 0.0    0.5         1.0 1.0<br>
+1.0 1.0 0.0    0.0         0.0 1.0<br>
+<br>
+[test]<br>
+ortho 0.0 1.0 0.0 1.0<br>
+clear color 0.0 0.0 0.0 0.0<br>
+clear<br>
+draw arrays GL_TRIANGLES 0 3<br>
+relative probe rgba (0.3, 0.7) (0.5, 0.4, 0.7, 1.0)<br>
+relative probe rgba (0.1, 0.5) (0.7, 0.4, 0.5, 1.0)<br>
+relative probe rgba (0.1, 0.9) (0.5, 0.8, 0.9, 1.0)<br>
+relative probe rgba (0.5, 0.9) (0.3, 0.4, 0.9, 1.0)<br>
+relative probe rgba (0.7, 0.3) (0.0, 0.0, 0.0, 0.0)<br>
diff --git a/tests/shaders/vbo/vbo-<u></u>generic-int.shader_test b/tests/shaders/vbo/vbo-<u></u>generic-int.shader_test<br>
new file mode 100644<br>
index 0000000..669b85d<br>
--- /dev/null<br>
+++ b/tests/shaders/vbo/vbo-<u></u>generic-int.shader_test<br>
@@ -0,0 +1,39 @@<br>
+[require]<br>
+GLSL&gt;= 1.30<br>
+GL&gt;= 3.0<br>
+<br>
+[vertex shader]<br>
+#version 130<br>
+attribute vec4 vertex;<br>
+attribute int foo;<br>
+attribute ivec2 bar;<br>
+<br>
+void main()<br>
+{<br>
+       gl_Position = gl_ModelViewProjectionMatrix * vertex;<br>
+       gl_FrontColor = vec4(foo/10.0, bar, 1.0);<br>
+}<br>
+<br>
+[fragment shader]<br>
+#version 130<br>
+void main()<br>
+{<br>
+       gl_FragColor = gl_Color;<br>
+}<br>
+<br>
+[vertex data]<br>
+vertex/float/3 foo/int/1 bar/int/2<br>
+0.0 0.0 0.0    10        0 0<br>
+0.0 1.0 0.0     5        1 1<br>
+1.0 1.0 0.0     0        0 1<br>
+<br>
+[test]<br>
+ortho 0.0 1.0 0.0 1.0<br>
+clear color 0.0 0.0 0.0 0.0<br>
+clear<br>
+draw arrays GL_TRIANGLES 0 3<br>
+relative probe rgba (0.3, 0.7) (0.5, 0.4, 0.7, 1.0)<br>
+relative probe rgba (0.1, 0.5) (0.7, 0.4, 0.5, 1.0)<br>
+relative probe rgba (0.1, 0.9) (0.5, 0.8, 0.9, 1.0)<br>
+relative probe rgba (0.5, 0.9) (0.3, 0.4, 0.9, 1.0)<br>
+relative probe rgba (0.7, 0.3) (0.0, 0.0, 0.0, 0.0)<br>
diff --git a/tests/shaders/vbo/vbo-<u></u>generic-uint.shader_test b/tests/shaders/vbo/vbo-<u></u>generic-uint.shader_test<br>
new file mode 100644<br>
index 0000000..6f56dd1<br>
--- /dev/null<br>
+++ b/tests/shaders/vbo/vbo-<u></u>generic-uint.shader_test<br>
@@ -0,0 +1,39 @@<br>
+[require]<br>
+GLSL&gt;= 1.30<br>
+GL&gt;= 3.0<br>
+<br>
+[vertex shader]<br>
+#version 130<br>
+attribute vec4 vertex;<br>
+attribute uint foo;<br>
+attribute ivec2 bar;<br>
+<br>
+void main()<br>
+{<br>
+       gl_Position = gl_ModelViewProjectionMatrix * vertex;<br>
+       gl_FrontColor = vec4(float(foo)/float(<u></u>0xe0000000u), bar, 1.0);<br>
+}<br>
+<br>
+[fragment shader]<br>
+#version 130<br>
+void main()<br>
+{<br>
+       gl_FragColor = gl_Color;<br>
+}<br>
+<br>
+[vertex data]<br>
+vertex/float/3 foo/uint/1 bar/int/2<br>
+0.0 0.0 0.0    0xe0000000 0 0<br>
+0.0 1.0 0.0    0x70000000 1 1<br>
+1.0 1.0 0.0    0x00000000 0 1<br>
+<br>
+[test]<br>
+ortho 0.0 1.0 0.0 1.0<br>
+clear color 0.0 0.0 0.0 0.0<br>
+clear<br>
+draw arrays GL_TRIANGLES 0 3<br>
+relative probe rgba (0.3, 0.7) (0.5, 0.4, 0.7, 1.0)<br>
+relative probe rgba (0.1, 0.5) (0.7, 0.4, 0.5, 1.0)<br>
+relative probe rgba (0.1, 0.9) (0.5, 0.8, 0.9, 1.0)<br>
+relative probe rgba (0.5, 0.9) (0.3, 0.4, 0.9, 1.0)<br>
+relative probe rgba (0.7, 0.3) (0.0, 0.0, 0.0, 0.0)<br>
diff --git a/tests/util/CMakeLists.gl.txt b/tests/util/CMakeLists.gl.txt<br>
index a03b50b..df1bfb9 100644<br>
--- a/tests/util/CMakeLists.gl.txt<br>
+++ b/tests/util/CMakeLists.gl.txt<br>
@@ -10,6 +10,7 @@ set(UTIL_SOURCES<br>
        piglit-shader.c<br>
        piglit-shader-gl.c<br>
        piglit-util-gl.c<br>
+       piglit-vbo.cpp<br>
        )<br>
<br>
  IF(${CMAKE_SYSTEM_NAME} MATCHES &quot;Linux&quot;)<br>
diff --git a/tests/util/piglit-vbo.cpp b/tests/util/piglit-vbo.cpp<br>
new file mode 100644<br>
index 0000000..fdba6b1<br>
--- /dev/null<br>
+++ b/tests/util/piglit-vbo.cpp<br>
@@ -0,0 +1,514 @@<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 &quot;Software&quot;),<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 &quot;AS IS&quot;, 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 piglit-vbo.cpp<br>
+ *<br>
+ * This file adds the facility for specifying vertex data to piglit<br>
+ * tests using a columnar text format, for example:<br>
+ *<br>
+ *   \verbatim<br>
+ *   vertex/float/3 foo/uint/1 bar/int/2<br>
+ *   0.0 0.0 0.0    10         0 0 # comment<br>
+ *   0.0 1.0 0.0     5         1 1<br>
+ *   1.0 1.0 0.0     0         0 1<br>
+ *   \endverbatim<br>
+ *<br>
+ * The format consists of a row of column headers followed by any<br>
+ * number of rows of data.  Each column header has the form<br>
+ * &quot;ATTRNAME/TYPE/COUNT&quot;, where ATTRNAME is the name of the vertex<br>
+ * attribute to be bound to this column, TYPE is the type of data that<br>
+ * follows (&quot;float&quot;, &quot;int&quot;, or &quot;uint&quot;), and COUNT is the vector length<br>
+ * of the data (e.g. &quot;3&quot; for vec3 data).<br>
+ *<br>
+ * The data follows the column headers in space-separated form.  &quot;#&quot;<br>
+ * can be used for comments, as in shell scripts.<br>
+ *<br>
+ * To process textual vertex data, call the function<br>
+ * setup_vbo_from_text(), passing the int identifying the linked<br>
+ * program, and the string containing the vertex data.  The return<br>
+ * value is the number of rows of vertex data found.<br>
+ *<br>
+ * If an error occurs, setup_vbo_from_text() will print out a<br>
+ * description of the error and exit with PIGLIT_FAIL.<br>
+ *<br>
+ * For the example above, the call to setup_vbo_from_text() is roughly<br>
+ * equivalent to the following GL operations:<br>
+ *<br>
+ * \code<br>
+ * struct vertex_attributes {<br>
+ *         GLfloat vertex[3];<br>
+ *         GLuint foo;<br>
+ *         GLint bar[2];<br>
+ * } vertex_data[] = {<br>
+ *         { { 0.0, 0.0, 0.0 }, 10, { 0, 0 } },<br>
+ *         { { 0.0, 1.0, 0.0 },  5, { 1, 1 } },<br>
+ *         { { 1.0, 1.0, 0.0 },  0, { 0, 1 } }<br>
+ * };<br>
+ * size_t stride = sizeof(vertex_data[0]);<br>
+ * GLint vertex_index = glGetAttribLocation(prog, &quot;vertex&quot;);<br>
+ * GLint foo_index = glGetAttribLocation(prog, &quot;foo&quot;);<br>
+ * GLint bar_index = glGetAttribLocation(prog, &quot;bar&quot;);<br>
+ * GLuint buffer_handle;<br>
+ * glGenBuffers(1,&amp;buffer_handle)<u></u>;<br>
+ * glBindBuffer(GL_ARRAY_BUFFER, buffer_handle);<br>
+ * glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data),&amp;vertex_<u></u>data,<br>
+ *              GL_STATIC_DRAW);<br>
+ * glVertexAttribPointer(vertex_<u></u>index, 3, GL_FLOAT, GL_FALSE, stride,<br>
+ *                       (void *) offsetof(vertex_attributes, vertex));<br>
+ * glVertexAttribIPointer(foo_<u></u>index, 3, GL_UNSIGNED_INT, stride,<br>
+ *                        (void *) offsetof(vertex_attributes, foo));<br>
+ * glVertexAttribIPointer(bar_<u></u>index, 3, GL_INT, stride,<br>
+ *                        (void *) offsetof(vertex_attributes, bar));<br>
+ * glEnableVertexAttribArray(<u></u>vertex_index);<br>
+ * glEnableVertexAttribArray(foo_<u></u>index);<br>
+ * glEnableVertexAttribArray(bar_<u></u>index);<br>
+ * \endcode<br>
+ */<br>
+<br>
+#ifndef _GNU_SOURCE<br>
+#define _GNU_SOURCE<br>
+#endif<br>
+<br>
+#if defined(_MSC_VER)<br>
+#define bool BOOL<br>
+#define true 1<br>
+#define false 0<br>
+#else<br>
+#include&lt;stdbool.h&gt;<br>
+#endif<br>
</blockquote>
<br></div></div>
Is any of the above necessary (or even correct) in C++?<br></blockquote><div><br>Whoops, good catch.  I blindly copied this stuff out of shader_runner.c without engaging my brain.  You&#39;re right, of course.  It&#39;s not necessary in C++ and it probably would make things mighty confusing for people trying to build Piglit on Windows.  I&#39;ll pull it out.<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>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div></div><div class="h5">
+#include&lt;string.h&gt;<br>
+#include&lt;ctype.h&gt;<br>
+#if defined(_WIN32)<br>
+#include&lt;stdlib.h&gt;<br>
+#else<br>
+#include&lt;libgen.h&gt;<br>
+#endif<br>
+#include &quot;piglit-util.h&quot;<br>
+#include &quot;piglit-vbo.h&quot;<br>
+#include&lt;vector&gt;<br>
+#include&lt;string&gt;<br>
+#include&lt;errno.h&gt;<br>
+<br>
+<br>
+/**<br>
+ * Currently all the attribute types we support (int, uint, and float)<br>
+ * are 4 bytes in width.<br>
+ */<br>
+const int ATTRIBUTE_SIZE = 4;<br>
+<br>
+<br>
+/**<br>
+ * Convert a type name string to a GLenum.<br>
+ */<br>
+GLenum<br>
+decode_type(const char *type)<br>
+{<br>
+       static struct type_table_entry {<br>
+               const char *type; /* NULL means end of table */<br>
+               GLenum enum_value;<br>
+       } const type_table[] = {<br>
+               { &quot;int&quot;,     GL_INT            },<br>
+               { &quot;uint&quot;,    GL_UNSIGNED_INT   },<br>
+               { &quot;float&quot;,   GL_FLOAT          },<br>
+               { NULL,      0                 }<br>
+       };<br>
+<br>
+<br>
+       for (int i = 0; type_table[i].type; ++i) {<br>
+               if (0 == strcmp(type, type_table[i].type))<br>
+                       return type_table[i].enum_value;<br>
+       }<br>
+<br>
+       printf(&quot;Unrecognized type: %s\n&quot;, type);<br>
+       piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+       return 0;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Description of a vertex attribute, built from its column header<br>
+ */<br>
+class vertex_attrib_description<br>
+{<br>
+public:<br>
+       vertex_attrib_description(<u></u>GLuint prog, const char *text);<br>
+       bool parse_datum(const char **text, void *data) const;<br>
+       void setup(size_t *offset, size_t stride) const;<br>
+<br>
+       /**<br>
+        * Data type of this attribute.<br>
+        */<br>
+       GLenum data_type;<br>
+<br>
+       /**<br>
+        * Vector length of this attribute.<br>
+        */<br>
+       size_t count;<br>
+<br>
+       /**<br>
+        * Index of this vertex attribute in the linked program.<br>
+        */<br>
+       GLuint index;<br>
+};<br>
+<br>
+<br>
+/**<br>
+ * Build a vertex_attrib_description from a column header, by looking<br>
+ * up the vertex attribute in the linked program and interpreting the<br>
+ * type and count parts of the header.<br>
+ *<br>
+ * If there is a parse failure, print a description of the problem and<br>
+ * then exit with PIGLIT_FAIL.<br>
+ */<br>
+vertex_attrib_description::<u></u>vertex_attrib_description(<u></u>GLuint prog,<br>
+                                                    const char *text)<br>
+{<br>
+       /* Split the column header into name/type/count fields */<br>
+       const char *first_slash = strchr(text, &#39;/&#39;);<br>
+       if (first_slash == NULL) {<br>
+               printf(&quot;Column headers must be in the form name/type/count.  &quot;<br>
+                      &quot;Got: %s\n&quot;,<br>
+                      text);<br>
+               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+       }<br>
+       std::string name(text, first_slash);<br>
+       const char *second_slash = strchr(first_slash + 1, &#39;/&#39;);<br>
+       if (second_slash == NULL) {<br>
+               printf(&quot;Column headers must be in the form name/type/count.  &quot;<br>
+                      &quot;Got: %s\n&quot;,<br>
+                      text);<br>
+               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+       }<br>
+       std::string type_str(first_slash + 1, second_slash);<br>
+       this-&gt;data_type = decode_type(type_str.c_str());<br>
+       char *endptr;<br></div></div>
+       this-&gt;count = strtoul(second_slash + 1,&amp;endptr, 10);<div><div></div><div class="h5"><br>
+       if (*endptr != &#39;\0&#39;) {<br>
+               printf(&quot;Column headers must be in the form name/type/count.  &quot;<br>
+                      &quot;Got: %s\n&quot;,<br>
+                      text);<br>
+               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+       }<br>
+<br>
+       GLint attrib_location = glGetAttribLocation(prog, name.c_str());<br>
+       if (attrib_location == -1) {<br>
+               printf(&quot;Unexpected vbo column name.  Got: %s\n&quot;, name.c_str());<br>
+               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+       }<br>
+       this-&gt;index = attrib_location;<br>
+       /* If the type is integral, verify that integer vertex<br>
+        * attribute support is present.  Note: we treat it as a FAIL<br>
+        * if support is not present, because it&#39;s up to the test to<br>
+        * either (a) not require integer vertex attribute support, or<br>
+        * (b) skip itself if integer vertex attribute support is not<br>
+        * present.<br>
+        */<br>
+       if (this-&gt;data_type != GL_FLOAT&amp;&amp;<br>
+           (piglit_is_gles() || piglit_get_gl_version()&lt;  30)) {<br>
+               printf(&quot;Test uses glVertexAttribIPointer(),&quot;<br>
+                      &quot; which is unsupported.\n&quot;);<br>
+               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+       }<br>
+<br>
+       if (this-&gt;count&lt;  1 || this-&gt;count&gt;  4) {<br>
+               printf(&quot;Count must be between 1 and 4.  Got: %lu\n&quot;, count);<br>
+               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+       }<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Parse a single number (floating point or integral) from one of the<br>
+ * data rows, and store it in the location pointed to by \c data.<br>
+ * Update \c text to point to the next character of input.<br>
+ *<br>
+ * If there is a parse failure, print a description of the problem and<br>
+ * then return false.  Otherwise return true.<br>
+ */<br>
+bool<br>
+vertex_attrib_description::<u></u>parse_datum(const char **text, void *data) const<br>
+{<br>
+       char *endptr;<br>
+       errno = 0;<br>
+       switch (this-&gt;data_type) {<br>
+       case GL_FLOAT: {<br>
+               double value = strtod(*text,&amp;endptr);<br>
+               if (errno == ERANGE) {<br>
+                       printf(&quot;Could not parse as double\n&quot;);<br>
+                       return false;<br>
+               }<br>
+               *((GLfloat *) data) = (float) value;<br>
+               break;<br>
+       }<br>
+       case GL_INT: {<br></div></div>
+               long value = strtol(*text,&amp;endptr, 0);<div class="im"><br>
+               if (errno == ERANGE) {<br>
+                       printf(&quot;Could not parse as signed integer\n&quot;);<br>
+                       return false;<br>
+               }<br>
+               *((GLint *) data) = (GLint) value;<br>
+               break;<br>
+       }<br>
+       case GL_UNSIGNED_INT: {<br></div>
+               unsigned long value = strtoul(*text,&amp;endptr, 0);<div><div></div><div class="h5"><br>
+               if (errno == ERANGE) {<br>
+                       printf(&quot;Could not parse as unsigned integer\n&quot;);<br>
+                       return false;<br>
+               }<br>
+               *((GLuint *) data) = (GLuint) value;<br>
+               break;<br>
+       }<br>
+       }<br>
+       *text = endptr;<br>
+       return true;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Execute the necessary GL calls to bind this attribute to its data.<br>
+ */<br>
+void<br>
+vertex_attrib_description::<u></u>setup(size_t *offset, size_t stride) const<br>
+{<br>
+       if (this-&gt;data_type == GL_FLOAT) {<br>
+               glVertexAttribPointer(this-&gt;<u></u>index, this-&gt;count,<br>
+                                     this-&gt;data_type, GL_FALSE, stride,<br>
+                                     (void *) *offset);<br>
+       } else {<br>
+               glVertexAttribIPointer(this-&gt;<u></u>index, this-&gt;count,<br>
+                                      this-&gt;data_type, stride,<br>
+                                      (void *) *offset);<br>
+       }<br>
+       glEnableVertexAttribArray(<u></u>index);<br>
+       *offset += ATTRIBUTE_SIZE * this-&gt;count;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Data structure containing all of the data parsed from the text<br>
+ * input, as well as the methods that parse it and convert it to GL<br>
+ * calls.<br>
+ */<br>
+class vbo_data<br>
+{<br>
+public:<br></div></div>
+       vbo_data(std::string const&amp;text, GLuint prog);<div class="im"><br>
+       size_t setup() const;<br>
+<br>
+private:<br></div>
+       void parse_header_line(const std::string&amp;line, GLuint prog);<br>
+       void parse_data_line(const std::string&amp;line, unsigned int line_num);<div><div></div><div class="h5"><br>
+       void parse_line(std::string line, unsigned int line_num, GLuint prog);<br>
+<br>
+       /**<br>
+        * True if the header line has already been parsed.<br>
+        */<br>
+       bool header_seen;<br>
+<br>
+       /**<br>
+        * Description of each attribute.<br>
+        */<br>
+       std::vector&lt;vertex_attrib_<u></u>description&gt;  attribs;<br>
+<br>
+       /**<br>
+        * Raw data buffer containing parsed numbers.<br>
+        */<br>
+       std::vector&lt;char&gt;  raw_data;<br>
+<br>
+       /**<br>
+        * Number of bytes in each row of raw_data.<br>
+        */<br>
+       size_t stride;<br>
+<br>
+       /**<br>
+        * Number of rows in raw_data.<br>
+        */<br>
+       size_t num_rows;<br>
+};<br>
+<br>
+<br>
+<br>
+static bool<br>
+is_blank_line(const std::string&amp;line)<br>
+{<br>
+       for (size_t i = 0; i&lt;  line.size(); ++i) {<br>
+               if (!isspace(line[i]))<br>
+                       return false;<br>
+       }<br>
+       return true;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Populate this-&gt;attribs and compute this-&gt;stride based on column<br>
+ * headers.<br>
+ *<br>
+ * If there is a parse failure, print a description of the problem and<br>
+ * then exit with PIGLIT_FAIL.<br>
+ */<br>
+void<br></div></div>
+vbo_data::parse_header_line(<u></u>const std::string&amp;line, GLuint prog)<div><div></div><div class="h5"><br>
+{<br>
+       size_t pos = 0;<br>
+       this-&gt;stride = 0;<br>
+       while (pos&lt;  line.size()) {<br>
+               if (isspace(line[pos])) {<br>
+                       ++pos;<br>
+               } else {<br>
+                       size_t column_header_end = pos;<br>
+                       while (column_header_end&lt;  line.size()&amp;&amp;<br>
+                              !isspace(line[column_header_<u></u>end]))<br>
+                               ++column_header_end;<br>
+                       std::string column_header = line.substr(<br>
+                               pos, column_header_end - pos);<br>
+                       vertex_attrib_description desc(<br>
+                               prog, column_header.c_str());<br>
+                       attribs.push_back(desc);<br>
+                       this-&gt;stride += ATTRIBUTE_SIZE * desc.count;<br>
+                       pos = column_header_end + 1;<br>
+               }<br>
+       }<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Convert a data row into binary form and append it to this-&gt;raw_data.<br>
+ *<br>
+ * If there is a parse failure, print a description of the problem and<br>
+ * then exit with PIGLIT_FAIL.<br>
+ */<br>
+void<br></div></div>
+vbo_data::parse_data_line(<u></u>const std::string&amp;line, unsigned int line_num)<div class="im"><br>
+{<br>
+       /* Allocate space in raw_data for this line */<br>
+       size_t old_size = this-&gt;raw_data.size();<br>
+       this-&gt;raw_data.resize(old_size + this-&gt;stride);<br></div>
+       char *data_ptr =&amp;this-&gt;raw_data[old_size];<div><div></div><div class="h5"><br>
+<br>
+       const char *line_ptr = line.c_str();<br>
+       for (size_t i = 0; i&lt;  this-&gt;attribs.size(); ++i) {<br>
+               for (size_t j = 0; j&lt;  this-&gt;attribs[i].count; ++j) {<br>
+                       if (!this-&gt;attribs[i].parse_<u></u>datum(&amp;line_ptr,<br>
+                                                         data_ptr)) {<br>
+                               printf(&quot;At line %u of [vertex data] section\n&quot;,<br>
+                                      line_num);<br>
+                               printf(&quot;Offending text: %s\n&quot;, line_ptr);<br>
+                               piglit_report_result(PIGLIT_<u></u>FAIL);<br>
+                       }<br>
+                       data_ptr += ATTRIBUTE_SIZE;<br>
+               }<br>
+       }<br>
+<br>
+       ++this-&gt;num_rows;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Parse a line of input text.<br>
+ *<br>
+ * If there is a parse failure, print a description of the problem and<br>
+ * then exit with PIGLIT_FAIL.<br>
+ */<br>
+void<br>
+vbo_data::parse_line(std::<u></u>string line, unsigned int line_num, GLuint prog)<br>
+{<br>
+       /* Ignore end-of-line comments */<br>
+       line = line.substr(0, line.find(&#39;#&#39;));<br>
+<br>
+       /* Ignore blank or comment-only lines */<br>
+       if (is_blank_line(line))<br>
+               return;<br>
+<br>
+       if (!this-&gt;header_seen) {<br>
+               this-&gt;header_seen = true;<br>
+               parse_header_line(line, prog);<br>
+       } else {<br>
+               parse_data_line(line, line_num);<br>
+       }<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Parse the input but don&#39;t execute any GL commands.<br>
+ *<br>
+ * If there is a parse failure, print a description of the problem and<br>
+ * then exit with PIGLIT_FAIL.<br>
+ */<br></div></div>
+vbo_data::vbo_data(const std::string&amp;text, GLuint prog)<div><div></div><div class="h5"><br>
+       : header_seen(false), num_rows(0)<br>
+{<br>
+       unsigned int line_num = 1;<br>
+<br>
+       size_t pos = 0;<br>
+       while (pos&lt;  text.size()) {<br>
+               size_t end_of_line = text.find(&#39;\n&#39;, pos);<br>
+               if (end_of_line == std::string::npos)<br>
+                       end_of_line = text.size();<br>
+               parse_line(text.substr(pos, end_of_line), line_num++, prog);<br>
+               pos = end_of_line + 1;<br>
+       }<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Execute the necessary GL commands to set up the vertex data passed<br>
+ * to the constructor.<br>
+ */<br>
+size_t<br>
+vbo_data::setup() const<br>
+{<br>
+       GLuint buffer_handle;<br>
+       glGenBuffers(1,&amp;buffer_handle)<u></u>;<br>
+       glBindBuffer(GL_ARRAY_BUFFER, buffer_handle);<br>
+       glBufferData(GL_ARRAY_BUFFER, this-&gt;stride * this-&gt;num_rows,<br>
+               &amp;this-&gt;raw_data[0], GL_STATIC_DRAW);<br>
+<br>
+       size_t offset = 0;<br>
+       for (size_t i = 0; i&lt;  attribs.size(); ++i)<br>
+               attribs[i].setup(&amp;offset, this-&gt;stride);<br>
+<br>
+       /* Leave buffer bound for later draw calls */<br>
+<br>
+       return this-&gt;num_rows;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Set up a vertex buffer object for the program prog based on the<br>
+ * data encoded in text_start.  text_end indicates the end of the text<br>
+ * string; if it is NULL, the string is assumed to be null-terminated.<br>
+ *<br>
+ * Return value is the number of rows of vertex data found.<br>
+ *<br>
+ * For details about the format of the text string, see the comment at<br>
+ * the top of this file.<br>
+ */<br>
+size_t<br>
+setup_vbo_from_text(GLuint prog, const char *text_start, const char *text_end)<br>
+{<br>
+       if (text_end == NULL)<br>
+               text_end = text_start + strlen(text_start);<br>
+       std::string text(text_start, text_end);<br>
+       return vbo_data(text, prog).setup();<br>
+}<br>
diff --git a/tests/util/piglit-vbo.h b/tests/util/piglit-vbo.h<br>
new file mode 100644<br>
index 0000000..22935e3<br>
--- /dev/null<br>
+++ b/tests/util/piglit-vbo.h<br>
@@ -0,0 +1,35 @@<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 &quot;Software&quot;),<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 &quot;AS IS&quot;, 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>
+#pragma once<br>
+<br>
+#ifdef __cplusplus<br>
+extern &quot;C&quot; {<br>
+#endif<br>
+<br>
+size_t<br>
+setup_vbo_from_text(GLuint prog, const char *text_start, const char *text_end);<br>
+<br>
+#ifdef __cplusplus<br>
+} /* end extern &quot;C&quot; */<br>
+#endif<br>
</div></div></blockquote>
<br>
</blockquote></div><br>