[Piglit] [PATCH 2/6] Add vbo support to shader_runner.

Paul Berry stereotype441 at gmail.com
Thu Oct 20 14:12:24 PDT 2011


This patch adds the ability for shader_runner tests to include a
"[vertex data]" section containing data in columnar format, for example:

[vertex data]
vertex/float/3 foo/uint/1 bar/int/2
0.0 0.0 0.0    0xe0000000 0 0
0.0 1.0 0.0    0x70000000 1 1
1.0 1.0 0.0    0x00000000 0 1

Each column header is of the form ATTRNAME/TYPE/COUNT, where ATTRNAME
is the name of the vertex attribute to be bound to this column, TYPE
is the type of data that follows ("float", "int", or "uint"), and
COUNT is the vector length of the data (e.g. "3" for vec3 data).

To send vertex data to the shader, use the new shader_runner command
"draw arrays".  The parameters are the same as for the glDrawArrays()
command, so for example to draw triangle primitives using 3 elements
from the vertex data array starting at element 0, use the command:

draw arrays GL_TRIANGLES 0 3

More detailed examples can be found in the tests/shaders/vbo
directory.

The implementation is largely in a new file, piglit-vbo.cpp, so that
it can be re-used by other piglit tests if necessary.
---
 tests/shaders/shader_runner.c                   |   80 ++++
 tests/shaders/vbo/vbo-generic-float.shader_test |   37 ++
 tests/shaders/vbo/vbo-generic-int.shader_test   |   39 ++
 tests/shaders/vbo/vbo-generic-uint.shader_test  |   39 ++
 tests/util/CMakeLists.gl.txt                    |    1 +
 tests/util/piglit-vbo.cpp                       |  514 +++++++++++++++++++++++
 tests/util/piglit-vbo.h                         |   35 ++
 7 files changed, 745 insertions(+), 0 deletions(-)
 create mode 100644 tests/shaders/vbo/vbo-generic-float.shader_test
 create mode 100644 tests/shaders/vbo/vbo-generic-int.shader_test
 create mode 100644 tests/shaders/vbo/vbo-generic-uint.shader_test
 create mode 100644 tests/util/piglit-vbo.cpp
 create mode 100644 tests/util/piglit-vbo.h

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



More information about the Piglit mailing list