[Piglit] [PATCH 4/4] glsl-1.50: Test primitive assembly after gs.

Fabian Bieler fabianbieler at fastmail.fm
Mon Mar 3 00:38:27 PST 2014


Test primitive assembly after geometry shader execution using
transform feedback. The test can use all valid geometry shader
output primitive types.

The geometry shader writes zero to two identical primitives (set via
command line). For each primitive it emits 0 to 12 vertices (set via
command line).

Signed-off-by: Fabian Bieler <fabianbieler at fastmail.fm>
---
 tests/all.py                                       |   4 +
 .../glsl-1.50/execution/geometry/CMakeLists.gl.txt |   1 +
 .../geometry/primitive-assembly-after-gs.c         | 452 +++++++++++++++++++++
 3 files changed, 457 insertions(+)
 create mode 100644 tests/spec/glsl-1.50/execution/geometry/primitive-assembly-after-gs.c

diff --git a/tests/all.py b/tests/all.py
index 4dcb9b4..7a0c30f 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -1338,6 +1338,10 @@ for subtest in ['unnamed', 'named', 'array']:
     add_concurrent_test(
         spec['glsl-1.50'],
         'glsl-1.50-interface-block-centroid {0}'.format(subtest))
+for prim in ['points', 'line_strip', 'triangle_strip']:
+    add_concurrent_test(
+        spec['glsl-1.50'],
+        'glsl-1.50-primitive-assembly-after-gs {0}'.format(prim))
 
 spec['glsl-3.30'] = Group()
 spec['glsl-3.30']['built-in constants'] = concurrent_test('built-in-constants tests/spec/glsl-3.30/minimum-maximums.txt')
diff --git a/tests/spec/glsl-1.50/execution/geometry/CMakeLists.gl.txt b/tests/spec/glsl-1.50/execution/geometry/CMakeLists.gl.txt
index cb73c76..ecd3373 100644
--- a/tests/spec/glsl-1.50/execution/geometry/CMakeLists.gl.txt
+++ b/tests/spec/glsl-1.50/execution/geometry/CMakeLists.gl.txt
@@ -18,6 +18,7 @@ piglit_add_executable (glsl-1.50-geometry-tri-strip-ordering-with-prim-restart t
 piglit_add_executable (glsl-1.50-gs-emits-too-few-verts gs-emits-too-few-verts.c)
 piglit_add_executable (glsl-1.50-getshaderiv-may-return-GS getshaderiv-may-return-GS.c)
 piglit_add_executable (glsl-1.50-gs-mismatch-prim-type gs-mismatch-prim-type.c)
+piglit_add_executable (glsl-1.50-primitive-assembly-after-gs primitive-assembly-after-gs.c)
 piglit_add_executable (glsl-1.50-query-gs-prim-types query-gs-prim-types.c)
 piglit_add_executable (glsl-1.50-gs-input-layout-qualifiers gs-input-layout-qualifiers.c)
 piglit_add_executable (glsl-1.50-gs-output-layout-qualifiers gs-output-layout-qualifiers.c)
diff --git a/tests/spec/glsl-1.50/execution/geometry/primitive-assembly-after-gs.c b/tests/spec/glsl-1.50/execution/geometry/primitive-assembly-after-gs.c
new file mode 100644
index 0000000..ca56684
--- /dev/null
+++ b/tests/spec/glsl-1.50/execution/geometry/primitive-assembly-after-gs.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright © 2014 Intel
+ *
+ * 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 primitive-assembly-after-gs.c
+ *
+ * Test primitive assembly after geometry shader execution with all possible
+ * output primitive types using transform feedback.
+ *
+ * Note: In this file 'primitive' refers to a primitive post primitive
+ * assembly, i.e. a single triangle or line segment, not a whole strip.
+ */
+
+static const char *const usage =
+"Usage: %s <primitive_type> [<strips> [<vertices>]]\n"
+"\n"
+"primitive_type -- the type of primitives the geometry shader emits\n"
+"                  (points, line_strip or triangle_strip)\n"
+"strips -- the number of triangle or line strips the geometry shader emits\n"
+"          per invocation (the number of times it calls EndPrimitive()\n"
+"          including the implicit call at the end of the shader)\n"
+"          (optional, defaults to 1)\n"
+"vertices -- the number of vertices the geometry shader emits for each output\n"
+"            primitive strip (independent of number of input vertices)\n"
+"            (optional; default: test 0 to 12)\n"
+"\n"
+"Note: with primitive_type=GL_POINTS EndPrimitve is a no-op so the geometry\n"
+"shader effectively just emits strips * vertices points.\n";
+
+
+#include "piglit-util-gl-common.h"
+
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+	config.supports_gl_compat_version = 32;
+	config.supports_gl_core_version = 32;
+PIGLIT_GL_TEST_CONFIG_END
+
+
+static int
+parse_cmd_line(int *, const int, char *const *const);
+static void
+compute_expected_result(GLint **, int *const, int *const,
+			const int, const int);
+
+
+static const int MAX_STRIP_OUT_COUNT = 2;
+static const int MAX_VERTICES_PER_STRIP = 128;
+
+const struct primitive_info {
+	char *name;
+	GLenum type;
+	GLenum base_type;
+	int elements;
+} *primitive;
+static const int vertex_in_count = 2;
+
+static unsigned int trafo_fdbk_query, prog, trafo_fdbk_buf;
+
+static const char *const vs_source =
+"#version 150\n"
+"out int vertex_id;\n"
+"void main()\n"
+"{\n"
+"	vertex_id = gl_VertexID;\n"
+"	gl_Position = vec4(0);\n"
+"}\n";
+
+static const char *const gs_source_template =
+"#version 150\n"
+"layout(points) in;\n"
+"layout(%s, max_vertices = %d) out;\n"
+"const int vertices_in = 1;\n"
+"uniform int strip_out_count;\n"
+"uniform int vertices_per_strip;\n"
+"in int vertex_id[];\n"
+"out ivec4 piglit_trafo_fdbk;\n"
+"void main()\n"
+"{\n"
+"	for (int j = 0; j < strip_out_count; j++) {\n"
+"		for (int i = 0; i < vertices_per_strip; i++) {\n"
+"			piglit_trafo_fdbk.x = gl_PrimitiveIDIn;\n"
+"			piglit_trafo_fdbk.y = j;\n"
+"			piglit_trafo_fdbk.z = i;\n"
+"			piglit_trafo_fdbk.w = 0;\n"
+"			EmitVertex();\n"
+"		}\n"
+"		EndPrimitive();\n"
+"	}\n"
+"}\n";
+
+
+static void
+draw(const int strip_out_count, const int vertices_per_strip,
+     const int expected_vertex_count)
+{
+	void *initial_data;
+	const int buffer_size = (expected_vertex_count + 1) *
+				4 * sizeof(GLint);
+
+	glUseProgram(prog);
+	glUniform1i(glGetUniformLocation(prog, "strip_out_count"),
+		    strip_out_count);
+	glUniform1i(glGetUniformLocation(prog, "vertices_per_strip"),
+		    vertices_per_strip);
+
+	/* Create transform feedback buffer and pre-load it with garbage.
+	 */
+	initial_data = malloc(buffer_size);
+	memset(initial_data, 0xcc, buffer_size);
+	glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
+		     buffer_size, initial_data,
+		     GL_STREAM_READ);
+	free(initial_data);
+
+	/* draw */
+	glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
+		     trafo_fdbk_query);
+	glEnable(GL_RASTERIZER_DISCARD);
+	glBeginTransformFeedback(primitive->base_type);
+	glDrawArrays(GL_POINTS, 0, vertex_in_count);
+	glEndTransformFeedback();
+	glDisable(GL_RASTERIZER_DISCARD);
+	glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
+}
+
+
+static bool
+test_output(const GLint expected_data[],
+	    const int expected_vertex_count,
+	    const int expected_primitive_count)
+{
+	bool pass = true;
+	const int *readback;
+	int result;
+	int i;
+	const int vsize = 4 * sizeof(GLint);
+	const int buffer_size = (expected_vertex_count + 1) * vsize;
+	static const int initial_data[4] = {0xcccccccc, 0xcccccccc,
+					    0xcccccccc, 0xcccccccc};
+
+	/* Check number of primitives written */
+	glGetQueryObjectiv(trafo_fdbk_query, GL_QUERY_RESULT, &result);
+	if (result != expected_primitive_count) {
+		fprintf(stderr, "Primitives written: "
+			"expected=%d, actual=%d\n",
+			expected_primitive_count, result);
+		pass = false;
+	}
+
+	/* Check output */
+	readback = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,
+				    buffer_size, GL_MAP_READ_BIT);
+	for (i = 0; i < expected_vertex_count; i++) {
+		if (memcmp(&readback[i*4], &expected_data[i*4], vsize) == 0)
+			continue;
+		fprintf(stderr, "geometry shader output data[%d]:"
+		       " expected=(%d %d %d %d), actual=(%d %d %d %d)\n", i,
+		       expected_data[i*4+0], expected_data[i*4+1],
+		       expected_data[i*4+2], expected_data[i*4+3],
+		       readback[i*4+0], readback[i*4+1],
+		       readback[i*4+2], readback[i*4+3]);
+		pass = false;
+	}
+	if (memcmp(&readback[i*4], initial_data, vsize) != 0) {
+		fprintf(stderr, "geometry shader output data overrun\n");
+		pass = false;
+	}
+	glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
+
+	return pass;
+}
+
+
+static bool
+run_test(const int strip_out_count, const int vertices_per_strip)
+{
+	bool pass = true;
+	GLint *expected_trafo_fdbk_data;
+	int expected_primitive_count;
+	int expected_vertex_count;
+
+	compute_expected_result(&expected_trafo_fdbk_data,
+				&expected_vertex_count,
+				&expected_primitive_count,
+				strip_out_count,
+				vertices_per_strip);
+
+	printf("Invoking gs %d times emitting %d %s with %d vertices each. "
+	       "Expecting %d vertices out.\n",
+	       vertex_in_count, strip_out_count,
+	       piglit_get_prim_name(primitive->type),
+	       vertices_per_strip, expected_vertex_count);
+
+	draw(strip_out_count, vertices_per_strip, expected_vertex_count);
+	pass = test_output(expected_trafo_fdbk_data, expected_vertex_count,
+				expected_primitive_count) && pass;
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	free(expected_trafo_fdbk_data);
+	return pass;
+}
+
+
+void
+piglit_init(int argc, char **argv)
+{
+	bool pass = true;
+	unsigned int vao;
+	const char *const varying = "piglit_trafo_fdbk";
+	char *gs_source;
+	const int max_vertices = MAX_STRIP_OUT_COUNT * MAX_VERTICES_PER_STRIP;
+	int strip_out_count;
+	int vertices_per_strip;
+
+	vertices_per_strip = parse_cmd_line(&strip_out_count, argc, argv);
+
+	glGenQueries(1, &trafo_fdbk_query);
+	glGenBuffers(1, &trafo_fdbk_buf);
+	glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, trafo_fdbk_buf);
+
+	/* Bind Vertex Data */
+	glGenVertexArrays(1, &vao);
+	glBindVertexArray(vao);
+
+	/* Create shader. */
+	gs_source = malloc(strlen(gs_source_template) + 32);
+	sprintf(gs_source, gs_source_template,
+		primitive->name,
+		max_vertices);
+	prog = piglit_build_simple_program_unlinked_multiple_shaders(
+			GL_VERTEX_SHADER, vs_source,
+			GL_GEOMETRY_SHADER, gs_source,
+			0);
+	free(gs_source);
+	glTransformFeedbackVaryings(prog, 1, &varying,
+				    GL_INTERLEAVED_ATTRIBS);
+	glLinkProgram(prog);
+	if (!piglit_link_check_status(prog) ||
+	    !piglit_check_gl_error(GL_NO_ERROR)) {
+		piglit_report_result(PIGLIT_FAIL);
+	}
+
+	if (vertices_per_strip < 0) {
+		int i;
+		for (i = 0; i <= 12; ++i)
+			pass = run_test(strip_out_count, i) && pass;
+	} else {
+		pass = run_test(strip_out_count, vertices_per_strip) && pass;
+	}
+
+	glDeleteBuffers(1, &trafo_fdbk_buf);
+	glDeleteQueries(1, &trafo_fdbk_query);
+	glDeleteProgram(prog);
+	glDeleteVertexArrays(1, &vao);
+
+	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
+}
+
+
+/* Expected result computation */
+
+typedef struct {
+	int x,y,z,w;
+} ivec4;
+
+/* Perform strip expansion.
+ * Returns number of vertices written to out
+ */
+static int
+assemble_primitives(ivec4 *out, const ivec4 *const in, const int length)
+{
+	int i, j;
+
+	if (primitive->type == GL_LINE_STRIP && length < 2)
+		return 0;
+	if (primitive->type == GL_TRIANGLE_STRIP && length < 3)
+		return 0;
+
+	j = 0;
+	for (i = 0; i < length; ++i) {
+		if (primitive->type == GL_LINE_STRIP && (i >= 2)) {
+			out[j++] = in[i - 1];
+		}
+		if (primitive->type == GL_TRIANGLE_STRIP && (i >= 3)) {
+			if (i % 2) {
+				out[j++] = in[i - 1];
+				out[j++] = in[i - 2];
+			} else {
+				out[j++] = in[i - 2];
+				out[j++] = in[i - 1];
+			}
+		}
+		out[j++] = in[i];
+	}
+
+	return j;
+}
+
+static void
+compute_expected_result(GLint **data_out,
+			int *const total_vertices_out,
+			int *const total_primitives_out,
+			const int strip_out_count,
+			const int vertices_per_strip)
+{
+	int i, j, k;
+	ivec4 *temp, *vertices_out;
+	int vertices_in_strip;
+	int vertices_written = 0;
+	const int unique_elements = 1;
+	const int initial_elements = primitive->elements - 1;
+	/* Given n vertices (max(n - initial_elements, 0) / unique_elements)
+	 * primitives are assembled.
+	 */
+
+	const int primitive_in_count = vertex_in_count;
+
+	const int primitives_per_strip =
+		MAX2(vertices_per_strip - initial_elements, 0) /
+		unique_elements;
+	const int primitives_per_gs_invocation =
+		primitives_per_strip * strip_out_count;
+	*total_primitives_out =
+		primitives_per_gs_invocation * primitive_in_count;
+	*total_vertices_out = *total_primitives_out * primitive->elements;
+
+	temp = malloc(sizeof(ivec4) * MAX_VERTICES_PER_STRIP);
+	vertices_out = malloc(*total_vertices_out * 4 * sizeof(GLint));
+
+	for (k = 0; k < primitive_in_count; ++k) {
+		const int gl_PrimitiveIDIn = k;
+		vertices_in_strip = 0;
+
+		/* Begin geometry shader main */
+		for (j = 0; j < strip_out_count; j++) {
+			for (i = 0; i < vertices_per_strip; i++) {
+				temp[vertices_in_strip].x = gl_PrimitiveIDIn;
+				temp[vertices_in_strip].y = j;
+				temp[vertices_in_strip].z = i;
+				temp[vertices_in_strip].w = 0;
+				/* EmitVertex */
+				vertices_in_strip++;
+			}
+			/* EndPrimitive */
+			vertices_written +=
+				assemble_primitives(vertices_out + vertices_written,
+						    temp, vertices_in_strip);
+			vertices_in_strip = 0;
+		}
+		/* End geometry shader main */
+	}
+
+	assert(*total_vertices_out == vertices_written);
+	free(temp);
+	*data_out = (GLint *)vertices_out;
+}
+
+
+/* Command line parsing */
+
+static void
+check_range(const int value, const char *const name, const int low,
+	    const int high)
+{
+	if (value >= low && value <= high)
+		return;
+	printf("%s must be in range [%d, %d] (inclusive).\n",
+	       name, low, high);
+	piglit_report_result(PIGLIT_FAIL);
+}
+
+static void
+print_usage_and_exit(const char *const name)
+{
+	printf(usage, name);
+	piglit_report_result(PIGLIT_FAIL);
+}
+
+static int
+parse_cmd_line(int *strip_out_count, const int argc, char *const *const argv)
+{
+	int i, j, k;
+	static const struct primitive_info primitives[] = {
+		{"points", GL_POINTS, GL_POINTS, 1},
+		{"line_strip", GL_LINE_STRIP, GL_LINES, 2},
+		{"triangle_strip", GL_TRIANGLE_STRIP, GL_TRIANGLES, 3},
+	};
+	int vertices_per_strip = -1;
+
+	*strip_out_count = 1;
+
+	k = 0;
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (arg[0] == '-')
+			continue;
+
+		if (k == 0) {
+			for (j = 0; j < ARRAY_SIZE(primitives); j++)
+				if (strcmp(primitives[j].name, argv[i]) == 0)
+					primitive = &primitives[j];
+			if (primitive == NULL)
+				print_usage_and_exit(argv[0]);
+		} else if (k == 1) {
+			*strip_out_count = atoi(argv[i]);
+		} else if (k == 2) {
+			vertices_per_strip = atoi(argv[i]);
+		} else {
+			print_usage_and_exit(argv[0]);
+		}
+		k++;
+	}
+
+	if (primitive == NULL)
+		print_usage_and_exit(argv[0]);
+	check_range(*strip_out_count, "strips", 0, MAX_STRIP_OUT_COUNT);
+	if (vertices_per_strip != -1)
+		check_range(vertices_per_strip, "vertices",
+			    0, MAX_VERTICES_PER_STRIP);
+
+	return vertices_per_strip;
+}
+
+
+enum piglit_result
+piglit_display(void)
+{
+	/* Should never be reached */
+	return PIGLIT_FAIL;
+}
+
-- 
1.8.3.2



More information about the Piglit mailing list