[Piglit] [PATCH 4/5] Add gles2_shader_runner to ease OpenGL ES 2.0 test case development

Shuang He shuang.he at intel.com
Fri Oct 29 00:47:38 PDT 2010


---
 tests/CMakeLists.txt              |    3 +
 tests/gles2/CMakeLists.txt        |   23 ++
 tests/gles2/gles2_shader_runner.c |  762 +++++++++++++++++++++++++++++++++++++
 3 files changed, 788 insertions(+), 0 deletions(-)
 create mode 100644 tests/gles2/CMakeLists.txt
 create mode 100644 tests/gles2/gles2_shader_runner.c

diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 3d3a892..58f8070 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -13,6 +13,9 @@ add_subdirectory (spec)
 
 IF(OPENGL_egl_LIBRARY)
 	add_subdirectory (egl)
+	IF(OPENGL_gles2_LIBRARY)
+		add_subdirectory (gles2)
+	ENDIF(OPENGL_gles2_LIBRARY)
 ENDIF(OPENGL_egl_LIBRARY)
 
 IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
diff --git a/tests/gles2/CMakeLists.txt b/tests/gles2/CMakeLists.txt
new file mode 100644
index 0000000..f9c426e
--- /dev/null
+++ b/tests/gles2/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_definitions ( -DSOURCE_DIR="${piglit_SOURCE_DIR}/" )
+
+include_directories(
+	${OPENGL_INCLUDE_PATH}
+	${piglit_SOURCE_DIR}/tests/util/
+)
+
+link_directories (
+	${piglit_SOURCE_DIR}/tests/util/
+	${piglit_SOURCE_DIR}/tests/util/eglut
+)
+
+link_libraries (
+	${OPENGL_gles2_LIBRARY}
+	${OPENGL_egl_LIBRARY}
+	${GLEW_glew_LIBRARY}
+	pigliteglut
+	piglitgles2util
+)
+
+
+add_executable (gles2_shader_runner gles2_shader_runner.c ../util/shader-load.c)
+target_link_libraries(gles2_shader_runner X11)
diff --git a/tests/gles2/gles2_shader_runner.c b/tests/gles2/gles2_shader_runner.c
new file mode 100644
index 0000000..9a42d56
--- /dev/null
+++ b/tests/gles2/gles2_shader_runner.c
@@ -0,0 +1,762 @@
+/*
+ * Copyright © 2010 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.
+ */
+
+#define _GNU_SOURCE
+#if defined(_MSC_VER)
+#define bool BOOL
+#define true 1
+#define false 0
+#else
+#include <stdbool.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include "piglit-egl-util.h"
+#include <GLES2/gl2.h>
+
+int piglit_width = 250, piglit_height = 250;
+
+static float gles_version = 0.0;
+static float essl_version = 0.0;
+
+const char *path = NULL;
+const char *test_start = NULL;
+
+GLuint vertex_shaders[256];
+unsigned num_vertex_shaders = 0;
+GLuint fragment_shaders[256];
+unsigned num_fragment_shaders = 0;
+
+/**
+ * List of strings loaded from files
+ *
+ * Some test script sections, such as "[vertex shader file]", can supply shader
+ * source code from multiple disk files.  This array stores those strings.
+ */
+
+char *shader_strings[256];
+GLsizei shader_string_sizes[256];
+unsigned num_shader_strings = 0;
+GLuint prog;
+
+enum states {
+	none = 0,
+	requirements,
+	vertex_shader,
+	vertex_shader_file,
+	vertex_program,
+	fragment_shader,
+	fragment_shader_file,
+	fragment_program,
+	test,
+};
+
+
+enum comparison {
+	equal = 0,
+	not_equal,
+	less,
+	greater_equal,
+	greater,
+	less_equal
+};
+
+
+void
+compile_glsl(GLenum target, bool release_text)
+{
+	GLuint shader = glCreateShader(target);
+	GLint ok;
+	unsigned i;
+
+	glShaderSource(shader, num_shader_strings,
+		       (const GLchar **) shader_strings, shader_string_sizes);
+
+	glCompileShader(shader);
+
+	glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
+
+	if (!ok) {
+		GLchar *info;
+		GLint size;
+
+		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
+		info = malloc(size);
+
+		glGetShaderInfoLog(shader, size, NULL, info);
+
+		fprintf(stderr, "Failed to compile %s: %s\n",
+			target == GL_FRAGMENT_SHADER ? "FS" : "VS",
+			info);
+
+		free(info);
+		piglit_report_result(PIGLIT_FAILURE);
+	}
+
+	if (release_text) {
+		for (i = 0; i < num_shader_strings; i++)
+			free(shader_strings[i]);
+	}
+
+	switch (target) {
+	case GL_VERTEX_SHADER:
+		vertex_shaders[num_vertex_shaders] = shader;
+		num_vertex_shaders++;
+		break;
+	case GL_FRAGMENT_SHADER:
+		fragment_shaders[num_fragment_shaders] = shader;
+		num_fragment_shaders++;
+		break;
+	}
+}
+
+
+/**
+ * Copy a string until either whitespace or the end of the string
+ */
+const char *
+strcpy_to_space(char *dst, const char *src)
+{
+	while (!isspace(*src) && (*src != '\0'))
+		*(dst++) = *(src++);
+
+	*dst = '\0';
+	return src;
+}
+
+
+/**
+ * Skip over whitespace upto the end of line
+ */
+const char *
+eat_whitespace(const char *src)
+{
+	while (isspace(*src) && (*src != '\n'))
+		src++;
+
+	return src;
+}
+
+
+/**
+ * Skip over non-whitespace upto the end of line
+ */
+const char *
+eat_text(const char *src)
+{
+	while (!isspace(*src) && (*src != '\0'))
+		src++;
+
+	return src;
+}
+
+
+/**
+ * Compare two values given a specified comparison operator
+ */
+bool
+compare(float ref, float value, enum comparison cmp)
+{
+	switch (cmp) {
+	case equal:         return value == ref;
+	case not_equal:     return value != ref;
+	case less:          return value <  ref;
+	case greater_equal: return value >= ref;
+	case greater:       return value >  ref;
+	case less_equal:    return value <= ref;
+	}
+
+	assert(!"Should not get here.");
+}
+
+
+/**
+ * Get the string representation of a comparison operator
+ */
+const char *
+comparison_string(enum comparison cmp)
+{
+	switch (cmp) {
+	case equal:         return "==";
+	case not_equal:     return "!=";
+	case less:          return "<";
+	case greater_equal: return ">=";
+	case greater:       return ">";
+	case less_equal:    return "<=";
+	}
+
+	assert(!"Should not get here.");
+}
+
+
+void
+load_shader_file(const char *line)
+{
+	GLsizei *const size = &shader_string_sizes[num_shader_strings];
+	char buf[256];
+	char *text;
+
+	strcpy_to_space(buf, line);
+
+	text = piglit_load_text_file(buf, (unsigned *) size);
+	if ((text == NULL) && (path != NULL)) {
+		const size_t len = strlen(path);
+
+		memcpy(buf, path, len);
+		buf[len] = '/';
+		strcpy_to_space(&buf[len + 1], line);
+
+		text = piglit_load_text_file(buf, (unsigned *) size);
+	}
+
+	if (text == NULL) {
+		strcpy_to_space(buf, line);
+
+		printf("could not load file \"%s\"\n", buf);
+		piglit_report_result(PIGLIT_FAILURE);
+	}
+
+	shader_strings[num_shader_strings] = text;
+	num_shader_strings++;
+}
+
+
+/**
+ * Parse a binary comparison operator and return the matching token
+ */
+const char *
+process_comparison(const char *src, enum comparison *cmp)
+{
+	char buf[32];
+
+	switch (src[0]) {
+	case '=':
+		if (src[1] == '=') {
+			*cmp = equal;
+			return src + 2;
+		}
+		break;
+	case '<':
+		if (src[1] == '=') {
+			*cmp = less_equal;
+			return src + 2;
+		} else {
+			*cmp = less;
+			return src + 1;
+		}
+	case '>':
+		if (src[1] == '=') {
+			*cmp = greater_equal;
+			return src + 2;
+		} else {
+			*cmp = greater;
+			return src + 1;
+		}
+	case '!':
+		if (src[1] == '=') {
+			*cmp = not_equal;
+			return src + 2;
+		}
+		break;
+	}
+
+	strncpy(buf, src, sizeof(buf));
+	buf[sizeof(buf) - 1] = '\0';
+	printf("invalid comparison in test script:\n%s\n", buf);
+	piglit_report_result(PIGLIT_FAILURE);
+
+	/* Won't get here. */
+	return NULL;
+}
+
+
+/**
+ * Parse and check a line from the requirement section of the test
+ */
+void
+process_requirement(const char *line)
+{
+	char buffer[4096];
+
+	/* There are three types of requirements that a test can currently
+	 * have:
+	 *
+	 *    * Require that some GL extension be supported
+	 *    * Require some particular versions of GL
+	 *    * Require some particular versions of GLSL
+	 *
+	 * The tests for GL and GLSL versions can be equal, not equal,
+	 * less, less-or-equal, greater, or greater-or-equal.  Extension tests
+	 * can also require that a particular extension not be supported by
+	 * prepending ! to the extension name.
+	 */
+	if (strncmp("GL_OES_", line, 7) == 0) {
+		strcpy_to_space(buffer, line + 7);
+		piglit_require_extension(buffer);
+	} else if (strncmp("!GL_OES_", line, 8) == 0) {
+		strcpy_to_space(buffer, line + 8);
+		piglit_require_not_extension(buffer);
+	} else if (strncmp("ESSL", line, 4) == 0) {
+		enum comparison cmp;
+		float version;
+
+		line = eat_whitespace(line + 4);
+
+		line = process_comparison(line, &cmp);
+
+		version = strtod(line, NULL);
+		if (!compare(version, essl_version, cmp)) {
+			printf("Test requires GLSL ES version %s %.1f.  "
+			       "Actual version is %.1f.\n",
+			       comparison_string(cmp),
+			       version,
+			       essl_version);
+			piglit_report_result(PIGLIT_SKIP);
+		}
+	} else if (strncmp("GLES", line, 4) == 0) {
+		enum comparison cmp;
+		float version;
+
+		line = eat_whitespace(line + 4);
+
+		line = process_comparison(line, &cmp);
+
+		version = strtod(line, NULL);
+		if (!compare(version, gles_version, cmp)) {
+			printf("Test requires GL version %s %.1f.  "
+			       "Actual version is %.1f.\n",
+			       comparison_string(cmp),
+			       version,
+			       gles_version);
+			piglit_report_result(PIGLIT_SKIP);
+		}
+	}
+}
+
+
+void
+leave_state(enum states state, const char *line)
+{
+	switch (state) {
+	case none:
+		break;
+
+	case requirements:
+		break;
+
+	case vertex_shader:
+		shader_string_sizes[0] = line - shader_strings[0];
+		num_shader_strings = 1;
+		compile_glsl(GL_VERTEX_SHADER, false);
+		break;
+
+	case vertex_shader_file:
+		compile_glsl(GL_VERTEX_SHADER, true);
+		break;
+
+	case vertex_program:
+		break;
+
+	case fragment_shader:
+		shader_string_sizes[0] = line - shader_strings[0];
+		num_shader_strings = 1;
+		compile_glsl(GL_FRAGMENT_SHADER, false);
+		break;
+
+	case fragment_shader_file:
+		compile_glsl(GL_FRAGMENT_SHADER, true);
+		break;
+
+	case fragment_program:
+		break;
+
+	case test:
+		break;
+
+	default:
+		assert(!"Not yet supported.");
+	}
+}
+
+
+void
+link_and_use_shaders(void)
+{
+	unsigned i;
+	GLenum err;
+	GLint ok;
+
+	if ((num_vertex_shaders == 0)
+	    && (num_fragment_shaders == 0))
+		return;
+
+	prog = glCreateProgram();
+
+	for (i = 0; i < num_vertex_shaders; i++) {
+		glAttachShader(prog, vertex_shaders[i]);
+	}
+
+	for (i = 0; i < num_fragment_shaders; i++) {
+		glAttachShader(prog, fragment_shaders[i]);
+	}
+
+	glLinkProgram(prog);
+
+	for (i = 0; i < num_vertex_shaders; i++) {
+		glDeleteShader(vertex_shaders[i]);
+	}
+
+	for (i = 0; i < num_fragment_shaders; i++) {
+		glDeleteShader(fragment_shaders[i]);
+	}
+
+	glGetProgramiv(prog, GL_LINK_STATUS, &ok);
+	if (!ok) {
+		GLchar *info;
+		GLint size;
+
+		glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
+		info = malloc(size);
+
+		glGetProgramInfoLog(prog, size, NULL, info);
+
+		fprintf(stderr, "Failed to link:\n%s\n",
+			info);
+
+		free(info);
+		piglit_report_result(PIGLIT_FAILURE);
+	}
+
+	glUseProgram(prog);
+
+	err = glGetError();
+	if (err) {
+		GLchar *info;
+		GLint size;
+
+		printf("GL error after linking program: 0x%04x\n", err);
+
+		glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
+		info = malloc(size);
+
+		glGetProgramInfoLog(prog, size, NULL, info);
+		fprintf(stderr, "Info log: %s\n", info);
+
+		piglit_report_result(PIGLIT_FAILURE);
+	}
+}
+
+
+void
+process_test_script(const char *script_name)
+{
+	unsigned text_size;
+	char *text = piglit_load_text_file(script_name, &text_size);
+	enum states state = none;
+	const char *line = text;
+
+	if (line == NULL) {
+		printf("could not read file \"%s\"\n", script_name);
+		piglit_report_result(PIGLIT_FAILURE);
+	}
+
+	while (line[0] != '\0') {
+		if (line[0] == '[') {
+			leave_state(state, line);
+
+			if (strncmp(line, "[require]", 9) == 0) {
+				state = requirements;
+			} else if (strncmp(line, "[vertex shader]", 15) == 0) {
+				state = vertex_shader;
+				shader_strings[0] = NULL;
+			} else if (strncmp(line, "[vertex shader file]", 20) == 0) {
+				state = vertex_shader_file;
+				shader_strings[0] = NULL;
+				num_shader_strings = 0;
+			} else if (strncmp(line, "[fragment shader]", 17) == 0) {
+				state = fragment_shader;
+				shader_strings[0] = NULL;
+			} else if (strncmp(line, "[fragment shader file]", 22) == 0) {
+				state = fragment_shader_file;
+				shader_strings[0] = NULL;
+				num_shader_strings = 0;
+			} else if (strncmp(line, "[test]", 6) == 0) {
+				test_start = strchrnul(line, '\n');
+				if (test_start[0] != '\0')
+					test_start++;
+				return;
+			}
+		} else {
+			switch (state) {
+			case none:
+				break;
+
+			case requirements:
+				process_requirement(line);
+				break;
+
+			case vertex_shader:
+			case vertex_program:
+			case fragment_shader:
+			case fragment_program:
+				if (shader_strings[0] == NULL)
+					shader_strings[0] = (char *) line;
+				break;
+
+			case vertex_shader_file:
+			case fragment_shader_file:
+				line = eat_whitespace(line);
+				if ((line[0] != '\n') && (line[0] != '#'))
+				    load_shader_file(line);
+				break;
+
+			case test:
+				break;
+			}
+		}
+
+		line = strchrnul(line, '\n');
+		if (line[0] != '\0')
+			line++;
+	}
+
+	leave_state(state, line);
+}
+
+
+void
+get_floats(const char *line, float *f, unsigned count)
+{
+	unsigned i;
+
+	for (i = 0; i < count; i++)
+		f[i] = strtod(line, (char **) &line);
+}
+
+void
+set_uniform(const char *line)
+{
+	char name[512];
+	float f[16];
+	GLuint prog;
+	GLint loc;
+	const char *type;
+
+	glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *) &prog);
+
+	type = eat_whitespace(line);
+	line = eat_text(type);
+
+	line = strcpy_to_space(name, eat_whitespace(line));
+	loc = glGetUniformLocation(prog, name);
+	if (loc < 0) {
+		printf("cannot get location of uniform \"%s\"\n",
+		       name);
+		piglit_report_result(PIGLIT_FAILURE);
+	}
+
+	if (strncmp("float", type, 5) == 0) {
+		get_floats(line, f, 1);
+		glUniform1fv(loc, 1, f);
+		return;
+	} else if (strncmp("int", type, 3) == 0) {
+		int val = atoi(line);
+		glUniform1i(loc, val);
+		return;
+	} else if (strncmp("vec", type, 3) == 0) {
+		switch (type[3]) {
+		case '2':
+			get_floats(line, f, 2);
+			glUniform2fv(loc, 1, f);
+			return;
+		case '3':
+			get_floats(line, f, 3);
+			glUniform3fv(loc, 1, f);
+			return;
+		case '4':
+			get_floats(line, f, 4);
+			glUniform4fv(loc, 1, f);
+			return;
+		}
+	}
+
+	strcpy_to_space(name, type);
+	printf("unknown uniform type \"%s\"", name);
+	piglit_report_result(PIGLIT_FAILURE);
+
+	return;
+}
+
+static GLboolean
+string_match(const char *string, const char *line)
+{
+	return (strncmp(string, line, strlen(string)) == 0);
+}
+
+enum piglit_result
+piglit_egl_display(void)
+{
+	const char *line;
+	bool pass = true;
+	GLbitfield clear_bits = 0;
+
+	if (test_start == NULL)
+		return PIGLIT_SUCCESS;
+
+	line = test_start;
+	while (line[0] != '\0') {
+		float c[32];
+		int x, y, w, h, tex, level;
+
+		line = eat_whitespace(line);
+
+		if (string_match("clear color", line)) {
+			get_floats(line + 11, c, 4);
+			glClearColor(c[0], c[1], c[2], c[3]);
+			clear_bits |= GL_COLOR_BUFFER_BIT;
+		} else if (string_match("clear", line)) {
+			glClear(clear_bits);
+		} else if (string_match("draw rect", line)) {
+			get_floats(line + 9, c, 4);
+			piglit_draw_rect(c[0], c[1], c[2], c[3]);
+		} else if (string_match("probe rgba", line)) {
+			get_floats(line + 10, c, 6);
+			if (!piglit_probe_pixel_rgba((int) c[0], (int) c[1],
+						    & c[2])) {
+				pass = false;
+			}
+		} else if (sscanf(line,
+				  "relative probe rgba ( %f , %f ) "
+				  "( %f , %f , %f , %f )",
+				  c + 0, c + 1,
+				  c + 2, c + 3, c + 4, c + 5) == 6) {
+			x = c[0] * piglit_width;
+			y = c[1] * piglit_width;
+			if (x >= piglit_width)
+				x = piglit_width - 1;
+			if (y >= piglit_height)
+				y = piglit_height - 1;
+
+			if (!piglit_probe_pixel_rgba(x, y, &c[2])) {
+				pass = false;
+			}
+		} else if (string_match("probe rgb", line)) {
+			get_floats(line + 9, c, 5);
+			if (!piglit_probe_pixel_rgb((int) c[0], (int) c[1],
+						    & c[2])) {
+				pass = false;
+			}
+		} else if (sscanf(line,
+				  "relative probe rgb ( %f , %f ) "
+				  "( %f , %f , %f )",
+				  c + 0, c + 1,
+				  c + 2, c + 3, c + 4) == 5) {
+			x = c[0] * piglit_width;
+			y = c[1] * piglit_width;
+			if (x >= piglit_width)
+				x = piglit_width - 1;
+			if (y >= piglit_height)
+				y = piglit_height - 1;
+
+			if (!piglit_probe_pixel_rgb(x, y, &c[2])) {
+				pass = false;
+			}
+		} else if (string_match("probe all rgba", line)) {
+			get_floats(line + 14, c, 4);
+			pass = pass &&
+				piglit_probe_rect_rgba(0, 0, piglit_width,
+						       piglit_height, c);
+		} else if (string_match("probe all rgb", line)) {
+			get_floats(line + 13, c, 3);
+			pass = pass &&
+				piglit_probe_rect_rgb(0, 0, piglit_width,
+						      piglit_height, c);
+		} else if (sscanf(line,
+				  "texture rgbw %d ( %d , %d )",
+				  &tex, &w, &h) == 3) {
+			glActiveTexture(GL_TEXTURE0 + tex);
+			piglit_rgbw_texture(GL_RGBA, w, h, GL_FALSE, GL_FALSE);
+			glEnable(GL_TEXTURE_2D);
+		} else if (sscanf(line,
+				  "texture checkerboard %d %d ( %d , %d ) "
+				  "( %f , %f , %f , %f ) "
+				  "( %f , %f , %f , %f )",
+				  &tex, &level, &w, &h,
+				  c + 0, c + 1, c + 2, c + 3,
+				  c + 4, c + 5, c + 6, c + 7) == 12) {
+			glActiveTexture(GL_TEXTURE0 + tex);
+			piglit_checkerboard_texture(0, level,
+						    w, h,
+						    w / 2, h / 2,
+						    c + 0, c + 4);
+			glEnable(GL_TEXTURE_2D);
+		} else if (string_match("uniform", line)) {
+			set_uniform(line + 7);
+		} else if ((line[0] != '\n') && (line[0] != '\0')
+			   && (line[0] != '#')) {
+			printf("unknown command \"%s\"", line);
+			piglit_report_result(PIGLIT_FAILURE);
+		}
+
+		line = strchrnul(line, '\n');
+		if (line[0] != '\0')
+			line++;
+	}
+
+	/* XXX eglutSwapBuffers() */
+
+	if (piglit_automatic) {
+		/* Free our resources, useful for valgrinding. */
+		glDeleteProgram(prog);
+		glUseProgram(0);
+	}
+	return pass ? PIGLIT_SUCCESS : PIGLIT_FAILURE;
+}
+
+
+void
+piglit_egl_init(int argc, char **argv)
+{
+	const char *essl_version_string;
+
+	/* OpenGL ES version string is in the form of :
+	 * OpenGL<space>ES<space><version number><space><vendor-specific information>
+	 */
+	gles_version = strtod((char *) glGetString(GL_VERSION)+10, NULL);
+
+	essl_version_string = (char *)
+		glGetString(GL_SHADING_LANGUAGE_VERSION);
+
+	/* OpenGL ES SHADING_LANGUAGE_VERSION string is in the form of :
+	 * OpenGL<space>ES<space><GLSL><space>ES<space><version number><space>
+	 * <vendor-specific information>
+	 */
+	essl_version = (essl_version_string == NULL)
+		? 0.0 : strtod(essl_version_string+18, NULL);
+
+	if (argc > 2)
+		path = argv[2];
+
+	process_test_script(argv[1]);
+	link_and_use_shaders();
+}
-- 
1.7.2.3



More information about the Piglit mailing list