On 2014-02-24 20:59, Paul Berry wrote:
On 24 February 2014 00:36, Ilia Mirkin <imir...@alum.mit.edu
<mailto:imir...@alum.mit.edu>> wrote:

    Before you read any further, this is nowhere close to working.
    However it's in
    a state where I think most of the structure is there, albeit with a
    lot of XXX
    comments. And I haven't actually implemented the new opcodes I've added.

    I was hoping one or two Intel people could take a look at this and
    let me know
    of any pitfalls I'm likely to run into. I've already gotten a lot of
    help and
    advice from Ken, but wanted to put something out publicly.

    Any and all feedback much appreciated!

    Not-Signed-off-by: Ilia Mirkin <imir...@alum.mit.edu
    <mailto:imir...@alum.mit.edu>>


I'm really excited to see you're working on this!  A lot of people are
going to be really happy to see geometry shader support added to Gen6.

AFAICT, your approach is good.  I plan to look at it in more detail
later today or sometime tomorrow.  But before I do, here are some
general comments:

1. For long-term readability, it might be useful to have 3 classes:
vec4_gs_visitor (for stuff that's common to all gs visitors),
gen6_gs_visitor (for the stuff that's specific to gen6 geometry
shaders), and gen7plus_gs_visitor (for the stuff that's specific to
gen7+ geometry shaders).  What you currently have is a little harder to
read, because it means that to understand gen6 geometry shaders, you
have to keep in mind that some of the code in vec4_gs_visitor is never
going to be executed because it will always be overridden by
gen6_gs_visitor.

2. Stream output (a.k.a. transform feedback): As you've probably already
figured out, transform feedback on gen6 is tricky because the hardware
doesn't have a dedicated pipeline stage for it; you have to emit code in
the geometry shader to do it.  This is a problem because the GL spec
says that two things are supposed to happen in between the GS stage and
transform feedback: dangling vertex removal (ignoring line strips
containing just a single vertex and ignoring triangle strips containing
less than 2 vertices), and expanding line/triangle strips into
individual line segments/triangles.  In Gen6, these operations happen
after the GS stage, so the code you emit in the geometry shader to do
transform feedback needs to simulate them.

Back when I first implemented transform feedback for Gen6, supporting
user-defined geometry shaders was the furthest thing from my mind, so I
implemented the transform feedback code in raw assembly rather than
re-using the vec4_visitor/vec4_generator infrastructure (see
gen6_sol_program()).  Unfortunately that's going to make it hard to
re-use that code in user-defined geometry shaders.  Also, when I
implemented that code, I was able to make some major simplifying
assumptions because there was no user-defined geometry shader--that
meant that the number of vertices to process was known at compile time,
and I didn't have to worry about dangling vertex removal or expanding
line/triangle strips into individual line segments/triangles (because
the hardware automatically performs those operations before invoking the
GS).  You're not going to have that luxury.  My recommendation is: feel
free to use gen6_sol_program() as a reference point, but don't bother
trying to re-use it--the simplifying assumptions are just going to make
it too different from what you want.  I'd recommend leaving
gen6_sol_program() around to handle the case where there's no
user-defined geometry shader, and then you can write fresh code for
handling transform feedback when there *is* a user-defined geometry shader.

Before you go too far on the implementation, I'd also recommend writing
some new piglit tests in tests/spec/glsl-1.50/execution/geometry/ to
verify that dangling vertex removal and strip expansion are handled
properly for transform feedback data.  If you don't have gs-supporting
hardware at your disposal to validate those tests against, I (or one of
the Intel folks) would be happy to validate the tests for you.  Note in
particular that in order to expand a triangle strip to triangles, you
have to be careful with the order to make sure that both the provoking
vertex and the triangle orientation are preserved (it's not necessary to
follow ARB_provoking_vertex, though; you can just assume the GL default
of LAST_VERTEX_CONVENTION applies).  For example, if the pre-expansion
vertex order is ABCDEF, then the only correct post-expansion order is
ABC,CBD,CDE,EDF.  I'm not sure whether Gen7+ hardware gets this order
exactly right in all circumstances, but it seems like it should be easy
to get it right on Gen6, because the order is entirely determined by
software :)

I'd also recommend adding some piglit tests to make sure that transform
feedback overflow conditions are handled properly when a geometry shader
is in use, since overflow handling on Gen6 is (partially) the
responsibility of the shader code you generate, and you're not going to
be able to piggy-back on the overflow handling in gen6_sol_program().
I have two old piglit tests for arb geometry shaders for these cases I've never gotten around to submit (see attachments). If you're interested I can rebase them and port them to 1.5 geometry shaders.

Fabian

One other note: check out this text from the Gen6 programmer's reference
manual (page 172 of Vol 2 part 1): "The final contents of Stream Output
buffers must follow the strict pipeline ordering of vertices. Given this
ordering requirement, it will be necessary to run the GS stage in a
single-threaded fashion (Maximum Number of Threads == 1). Otherwise
concurrent GS threads might append vertices to the output buffer out of
order."  I believe this actually incorrect for gen6--I think it only
applies to Gen5 and earlier, and was left in the documentation for Gen6
by accident.  Here's how I think it actually works in Gen6.  There are
two modes: either (a) the SVBI (the pointer that indicates where we are
writing to in the transform feedback buffer) is incremented
automatically by the hardware when spawning a GS thread, or (b) the SVBI
is incremented manually by the GS thread when issuing the FF_SYNC
message.  The choice of mode (a) or (b) is selected by the
GEN6_GS_SVBI_POSTINCREMENT_ENABLE flag in 3DSTATE_GS.  The existing
gen6_sol_program() logic uses mode (a).  But that only works because the
correct increment is known before the GS thread starts executing.
You're going to have to use mode (b), since the correct increment
depends on how many vertices the user-provided GS program outputs.
(Actually, it's even more complicated than that, because it has to
account for dangling vertex removal and expansion of triangle and line
strips).  So here's what I think you're going to have to do:

- Ignore the SVBI values that are provided to the GS thread when it is
spawned (in fact, don't even bother setting the
GEN6_GS_SVBI_PAYLOAD_ENABLE bit--that way you won't have to waste
register space for them).  These values are garbage when using mode (b)
because lots of GS threads may be spawned before the first one gets a
chance to start writing transform feedback output.
- Before issuing the FF_SYNC message, compute the number of transform
feedback vertices you're going to output, based on the pattern of calls
to EmitVertex() and EndPrimitive().  To compute this correctly you're
going to have to think about dangling vertex removal and line/triangle
strip expansion.
- In emit_thread_end(), when sending the FF_SYNC message, tell the
hardware how many vertices you're going to output--this determines how
much it will post-increment the SVBI pointer.
- In the response to the FF_SYNC message, the hardware tells you the
value the SVBI pointer had just before it post-incremented it.  This
tells you where to store the transform feedback output.
- Now, at the same time you're sending your vertices down the pipeline
using URB writes, you can also output them to the transform feedback
buffer using the SVBI pointer you got back from the FF_SYNC message.
This is where you do the actual dangling vertex removal and
line/triangle strip expansion.

Note that you only need to do dangling vertex removal and line/triangle
strip transformation to the transform feedback data.  It's ok (and
probably easiest) for the data you deliver down the pipeline using URB
writes to have dangling vertices and unexpanded triangle/line strips.

Of course, I'm working from the same code and docs that you are, so I
could be mistaken about some of this :)

Once again, thanks for working on this!  I'll try to look over your code
sometime today or tomorrow and make more comments.

Paul


_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev

>From 031e8be4a9ecc2bf095ea355646f89de3cae8cd6 Mon Sep 17 00:00:00 2001
From: Fabian Bieler <fabianbie...@fastmail.fm>
Date: Wed, 12 Jun 2013 19:37:21 +0200
Subject: [PATCH] arb_geometry_shader4: Draw more vertices than allowed.

Test a geometry shader that emits more vertices than allowed per
GEOMETRY_VERTICES_OUT.

>From the ARB_geometry_shader4 spec (section 8.10):
"If a geometry shader, in one invocation, emits more vertices than the value
GEOMETRY_VERTICES_OUT_ARB, these emits may have no effect."

However the shader should execute without error and the valid vertices should
be drawn.

Test this with a geometry shader that tries to emit 14 but is only allowed to
emit 8 and one that tries to emit 510 but is only allowed to emit 256 vertices.
---
 .../execution/output-overrun-14verts.shader_test   | 78 ++++++++++++++++++++++
 .../execution/output-overrun-510verts.shader_test  | 78 ++++++++++++++++++++++
 2 files changed, 156 insertions(+)
 create mode 100644 tests/spec/arb_geometry_shader4/execution/output-overrun-14verts.shader_test
 create mode 100644 tests/spec/arb_geometry_shader4/execution/output-overrun-510verts.shader_test

diff --git a/tests/spec/arb_geometry_shader4/execution/output-overrun-14verts.shader_test b/tests/spec/arb_geometry_shader4/execution/output-overrun-14verts.shader_test
new file mode 100644
index 0000000..e25d159
--- /dev/null
+++ b/tests/spec/arb_geometry_shader4/execution/output-overrun-14verts.shader_test
@@ -0,0 +1,78 @@
+# Emit more vertices than allowed.
+#
+# From the ARB_geometry_shader4 spec (section 8.10):
+# "If a geometry shader, in one invocation, emits more vertices than the value
+# GEOMETRY_VERTICES_OUT_ARB, these emits may have no effect."
+#
+# The geometry shader emits a triangle strip with 14 vertices (12 triangles)
+# that cover the entire screen in a single row of isosceles triangles. Only
+# allow the geomtry shader to emit a total of 8 vertices (6 triangles) via
+# GEOMETRY_VERTICES_OUT. Invoke the geometry shader twice. The first time it
+# draws the strip left to right, the second time vice versa. Thus, regardless
+# whether the supernumerous vertices are drawn or ignored the entire screen
+# should be filled.
+[require]
+GL >= 2.0
+GLSL >= 1.10
+GL_ARB_geometry_shader4
+
+[vertex shader]
+#version 110
+
+attribute vec4 vertex;
+
+void main()
+{
+	gl_Position = vertex;
+}
+
+[geometry shader]
+#version 110
+#extension GL_ARB_geometry_shader4: enable
+
+/* Should always be even and >= 4. */
+uniform int count;
+
+void main()
+{
+	for (int i = 0; i < count / 2; ++i) {
+		float p = float(i) / float(count / 2 - 1);
+		gl_Position = gl_PositionIn[0] + p *
+			(gl_PositionIn[2] - gl_PositionIn[0]);
+		EmitVertex();
+		gl_Position = gl_PositionIn[1] + p *
+			(gl_PositionIn[3] - gl_PositionIn[1]);
+		EmitVertex();
+	}
+}
+
+[geometry layout]
+input type GL_LINES_ADJACENCY
+output type GL_TRIANGLE_STRIP
+vertices out 8
+
+[fragment shader]
+#version 110
+
+void main()
+{
+	gl_FragColor = vec4(0, 1, 0, 1);
+}
+
+[vertex data]
+vertex/float/2
+-1.0  1.0
+-1.0 -1.0
+ 1.0  1.0
+ 1.0 -1.0
+ 1.0 -1.0
+ 1.0  1.0
+-1.0 -1.0
+-1.0  1.0
+
+[test]
+uniform int count 14
+clear color 0.0 0.0 0.0 0.0
+clear
+draw arrays GL_LINES_ADJACENCY 0 8
+probe all rgb 0.0 1.0 0.0
diff --git a/tests/spec/arb_geometry_shader4/execution/output-overrun-510verts.shader_test b/tests/spec/arb_geometry_shader4/execution/output-overrun-510verts.shader_test
new file mode 100644
index 0000000..2c5a293
--- /dev/null
+++ b/tests/spec/arb_geometry_shader4/execution/output-overrun-510verts.shader_test
@@ -0,0 +1,78 @@
+# Emit more vertices than allowed.
+#
+# From the ARB_geometry_shader4 spec (section 8.10):
+# "If a geometry shader, in one invocation, emits more vertices than the value
+# GEOMETRY_VERTICES_OUT_ARB, these emits may have no effect."
+#
+# The geometry shader emits a triangle strip with 510 vertices (508 triangles)
+# that cover the entire screen in a single row of isosceles triangles. Only
+# allow the geomtry shader to emit a total of 256 vertices (254 triangles) via
+# GEOMETRY_VERTICES_OUT. Invoke the geometry shader twice. The first time it
+# draws the strip left to right, the second time vice versa. Thus, regardless
+# whether the supernumerous vertices are drawn or ignored the entire screen
+# should be filled.
+[require]
+GL >= 2.0
+GLSL >= 1.10
+GL_ARB_geometry_shader4
+
+[vertex shader]
+#version 110
+
+attribute vec4 vertex;
+
+void main()
+{
+	gl_Position = vertex;
+}
+
+[geometry shader]
+#version 110
+#extension GL_ARB_geometry_shader4: enable
+
+/* Should always be even and >= 4. */
+uniform int count;
+
+void main()
+{
+	for (int i = 0; i < count / 2; ++i) {
+		float p = float(i) / float(count / 2 - 1);
+		gl_Position = gl_PositionIn[0] + p *
+			(gl_PositionIn[2] - gl_PositionIn[0]);
+		EmitVertex();
+		gl_Position = gl_PositionIn[1] + p *
+			(gl_PositionIn[3] - gl_PositionIn[1]);
+		EmitVertex();
+	}
+}
+
+[geometry layout]
+input type GL_LINES_ADJACENCY
+output type GL_TRIANGLE_STRIP
+vertices out 256
+
+[fragment shader]
+#version 110
+
+void main()
+{
+	gl_FragColor = vec4(0, 1, 0, 1);
+}
+
+[vertex data]
+vertex/float/2
+-1.0  1.0
+-1.0 -1.0
+ 1.0  1.0
+ 1.0 -1.0
+ 1.0 -1.0
+ 1.0  1.0
+-1.0 -1.0
+-1.0  1.0
+
+[test]
+uniform int count 510
+clear color 0.0 0.0 0.0 0.0
+clear
+draw arrays GL_LINES_ADJACENCY 0 8
+probe all rgb 0.0 1.0 0.0
-- 
1.8.3.2

>From 450aa1d5976bcf80306b088ae2ec3dcafc6d2fb4 Mon Sep 17 00:00:00 2001
From: Fabian Bieler <fabianbie...@fastmail.fm>
Date: Sat, 18 May 2013 13:38:47 +0200
Subject: [PATCH] arb_geometry_shader4: Test primitive assembly.

Test primitive assembly before and after geometry shader execution using
transform feedback. The test uses all valid combinations of geometry shader
input and output primitive type as well as drawn primitive type.

It draws up to 12 vertices (set via command line). The only vertex attribute
is a zero-base vertex id. The vertex shader simple passes through the vertex
attribute. The geometry shader writes zero to two identical primitives (also
set via command line). For each primitive it uses the first n input vertices
and m extra vertices (where n,m can be set via command line options).
---
 tests/all.tests                                    |  23 +
 .../execution/CMakeLists.gl.txt                    |   1 +
 .../execution/transform-feedback.c                 | 562 +++++++++++++++++++++
 3 files changed, 586 insertions(+)
 create mode 100644 tests/spec/arb_geometry_shader4/execution/transform-feedback.c

diff --git a/tests/all.tests b/tests/all.tests
index dce6afc..5014c82 100644
--- a/tests/all.tests
+++ b/tests/all.tests
@@ -2340,6 +2340,29 @@ add_concurrent_test(arb_geometry_shader4, 'arb_geometry_shader4-program-paramete
 add_concurrent_test(arb_geometry_shader4, 'arb_geometry_shader4-program-parameter-output-type')
 for mode in ['1', 'tf 1', 'max', 'tf max']:
 	add_concurrent_test(arb_geometry_shader4, 'arb_geometry_shader4-program-parameter-vertices-out {0}'.format(mode))
+for prim_in in ['GL_POINTS', 'GL_LINES', 'GL_LINE_STRIP', 'GL_LINE_LOOP', 'GL_TRIANGLES',
+		'GL_TRIANGLE_STRIP', 'GL_TRIANGLE_FAN', 'GL_LINES_ADJACENCY', 
+		'GL_LINE_STRIP_ADJACENCY', 'GL_TRIANGLES_ADJACENCY', 'GL_TRIANGLE_STRIP_ADJACENCY']:
+	for prim_out in ['GL_POINTS', 'GL_LINE_STRIP', 'GL_TRIANGLE_STRIP']:
+		if 'ADJACENCY' in prim_in:
+			if 'LINE' in prim_in:
+				min = 4
+			else:
+				min = 6
+		else:
+			if 'LINE' in prim_in:
+				min = 2
+			elif 'TRIANGLE' in prim_in:
+				min = 3
+			else:
+				min = 1
+		for vert_in_count in [min, 12]:
+			for prim_out_count in [1, 2]:
+				for max_vert_count in [min]:
+					for extra_vert_count in [0, 3]:
+						add_concurrent_test(arb_geometry_shader4,
+						'arb_geometry_shader4-transform-feedback {0} {1} {2} {3} {4} {5}'.format(
+						prim_in, prim_out, vert_in_count, prim_out_count, max_vert_count, extra_vert_count))
 spec['ARB_geometry_shader4'] = arb_geometry_shader4
 add_shader_test_dir(spec['ARB_geometry_shader4'],
                     os.path.join(testsDir, 'spec', 'arb_geometry_shader4'),
diff --git a/tests/spec/arb_geometry_shader4/execution/CMakeLists.gl.txt b/tests/spec/arb_geometry_shader4/execution/CMakeLists.gl.txt
index 408a939..30ffe5a 100644
--- a/tests/spec/arb_geometry_shader4/execution/CMakeLists.gl.txt
+++ b/tests/spec/arb_geometry_shader4/execution/CMakeLists.gl.txt
@@ -12,3 +12,4 @@ link_libraries (
 
 piglit_add_executable (arb_geometry_shader4-ignore-adjacent-vertices ignore-adjacent-vertices.c)
 piglit_add_executable (arb_geometry_shader4-point-size point-size.c)
+piglit_add_executable (arb_geometry_shader4-transform-feedback transform-feedback.c)
diff --git a/tests/spec/arb_geometry_shader4/execution/transform-feedback.c b/tests/spec/arb_geometry_shader4/execution/transform-feedback.c
new file mode 100644
index 0000000..237aa4b
--- /dev/null
+++ b/tests/spec/arb_geometry_shader4/execution/transform-feedback.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright © 2013 The Piglit project
+ *
+ * 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 transform-feedback.c
+ *
+ * Test primitive assembly before and after geometry shader with all possible
+ * combinations of input and output primitive types using
+ * transform feedback.
+ */
+
+#include "piglit-util-gl-common.h"
+
+
+struct primitive_info {
+	GLenum type;
+	GLenum base_type;
+	int elements;
+	int unique_elements;
+	int initial_elements;
+	/* Given n vertices ((n - initial_elements) / unique_elements)
+	 * primitives are assembled.
+	 */
+};
+
+static const struct primitive_info primitives_in[] = {
+	{GL_POINTS, GL_POINTS, 1, 1, 0},
+
+	{GL_LINES, GL_LINES, 2, 2, 0},
+	{GL_LINE_STRIP, GL_LINES, 2, 1, 1},
+	{GL_LINE_LOOP, GL_LINES, 2, 1, 0},
+
+	{GL_TRIANGLES, GL_TRIANGLES, 3, 3, 0},
+	{GL_TRIANGLE_STRIP, GL_TRIANGLES, 3, 1, 2},
+	{GL_TRIANGLE_FAN, GL_TRIANGLES, 3, 1, 2},
+
+	{GL_LINES_ADJACENCY, GL_LINES_ADJACENCY, 4, 4, 0},
+	{GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, 4, 1, 3},
+
+	{GL_TRIANGLES_ADJACENCY, GL_TRIANGLES_ADJACENCY, 6, 6, 0},
+	{GL_TRIANGLE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY, 6, 2, 4},
+};
+
+static const struct primitive_info primitives_out[] = {
+	{GL_POINTS, GL_POINTS, 1, 1, 0},
+	{GL_LINE_STRIP, GL_LINES, 2, 1, 1},
+	{GL_TRIANGLE_STRIP, GL_TRIANGLES, 3, 1, 2},
+};
+
+
+/* Enable geometry shader extension in vertex shader to make writing to
+ * gl_Position optional.
+ */
+static const char vs_text[] =
+	"attribute float vertex;\n"
+	"varying float vertex_id;\n"
+	"void main()\n"
+	"{\n"
+	"	vertex_id = vertex;\n"
+	"	gl_Position = vec4(0);\n"
+	"}\n";
+
+static const char gs_text[] =
+	"#extension GL_ARB_geometry_shader4: enable\n"
+	"uniform int primitive_out_count;\n"
+	"uniform int max_vertex_count;\n"
+	"uniform int extra_vertex_count;\n"
+	"varying in float vertex_id[];\n"
+	"varying out vec4 piglit_trafo_fdbk0;\n"
+	"void main()\n"
+	"{\n"
+	"	for (int j = 0; j < primitive_out_count; j++) {\n"
+	"		for (int i = 0; i < int(min(float(gl_VerticesIn), float(max_vertex_count))); i++) {\n"
+	"			piglit_trafo_fdbk0.x = vertex_id[i];\n"
+	"			piglit_trafo_fdbk0.y = float(i);\n"
+	"			piglit_trafo_fdbk0.z = float(j);\n"
+	"			piglit_trafo_fdbk0.w = float(gl_PrimitiveIDIn);\n"
+	"			EmitVertex();\n"
+	"		}\n"
+	"		for (int i = 0; i < extra_vertex_count; i++) {\n"
+	"			piglit_trafo_fdbk0.x = -1.0;\n"
+	"			piglit_trafo_fdbk0.y = float(i);\n"
+	"			piglit_trafo_fdbk0.z = float(j);\n"
+	"			piglit_trafo_fdbk0.w = float(gl_PrimitiveIDIn);\n"
+	"			EmitVertex();\n"
+	"		}\n"
+	"		EndPrimitive();\n"
+	"	}\n"
+	"}\n";
+
+
+/* Expected result computation */
+
+typedef struct {
+	float x,y,z,w;
+} vec4;
+static struct {
+	int verts_in_current_prim;
+	int verts_per_prim;
+	vec4 *out;
+	unsigned out_size;
+	int vert_out_count;
+	int prim_out_count;
+} cer_tmps;
+static vec4 piglit_trafo_fdbk0;
+
+static void
+assemble_primitive(float *const out, const float *const in_data,
+		   const int primID, const struct primitive_info info,
+		   const int total_primitives_count)
+{
+	int i;
+	const float *const in = in_data + primID * info.unique_elements;
+	const bool first_primitive = primID == 0;
+	const bool last_primitive = primID == total_primitives_count - 1;
+	const bool odd_primitive = (primID % 2);
+
+	for (i = 0; i < info.elements; ++i)
+		out[i] = in[i];
+
+	if (info.type == GL_LINE_LOOP && last_primitive)
+		out[1] = in_data[0];
+	if (info.type == GL_TRIANGLE_STRIP && odd_primitive) {
+		out[0] = in[1];
+		out[1] = in[0];
+	}
+	if (info.type == GL_TRIANGLE_FAN)
+		out[0] = in_data[0];
+
+	if (info.type == GL_TRIANGLE_STRIP_ADJACENCY) {
+		if (odd_primitive) {
+			out[0] = in[2];
+			out[1] = in[-2];
+			out[2] = in[0];
+			if (!last_primitive)
+				out[5] = in[6];
+		} else {
+			if (!first_primitive)
+				out[1] = in[-2];
+			if (last_primitive)
+				out[3] = in[5];
+			else
+				out[3] = in[6];
+			out[5] = in[3];
+		}
+	}
+}
+
+static void
+EndPrimitive(void)
+{
+	cer_tmps.verts_in_current_prim = 0;
+}
+
+static void
+_EmitVertex(const vec4 v)
+{
+	assert(cer_tmps.vert_out_count < cer_tmps.out_size);
+	cer_tmps.out[cer_tmps.vert_out_count] = v;
+	cer_tmps.verts_in_current_prim++;
+	cer_tmps.vert_out_count++;
+}
+
+static void
+EmitVertex(void)
+{
+	if (cer_tmps.verts_per_prim == 2 &&
+	    (cer_tmps.verts_in_current_prim >= 2)) {
+		_EmitVertex(cer_tmps.out[cer_tmps.vert_out_count - 1]);
+	}
+	if (cer_tmps.verts_per_prim == 3 &&
+	    (cer_tmps.verts_in_current_prim >= 3)) {
+		if (cer_tmps.verts_in_current_prim % 2) {
+			_EmitVertex(cer_tmps.out[cer_tmps.vert_out_count - 1]);
+			_EmitVertex(cer_tmps.out[cer_tmps.vert_out_count - 3]);
+		} else {
+			_EmitVertex(cer_tmps.out[cer_tmps.vert_out_count - 3]);
+			_EmitVertex(cer_tmps.out[cer_tmps.vert_out_count - 2]);
+		}
+	}
+	_EmitVertex(piglit_trafo_fdbk0);
+	if (cer_tmps.verts_in_current_prim >= cer_tmps.verts_per_prim)
+		cer_tmps.prim_out_count++;
+}
+
+static void
+compute_expected_result(float data_out[], const unsigned data_out_size,
+			unsigned *const verts_out, unsigned *const prims_out,
+			const float *const input, const int vertex_in_count,
+			const struct primitive_info prim_in,
+			const struct primitive_info prim_out,
+			const int primitive_out_count,
+			const int max_vertex_count,
+			const int extra_vertex_count)
+{
+	const int gl_VerticesIn = prim_in.elements;
+
+	int k;
+	const int primitive_in_count =
+		(vertex_in_count - prim_in.initial_elements) /
+		prim_in.unique_elements;
+
+	/* The primitive count is incorrect for line loops with 1 vertex. */
+	if (prim_in.type == GL_LINE_LOOP && vertex_in_count == 1)
+		return;
+
+	cer_tmps.verts_per_prim = prim_out.elements;
+	cer_tmps.out = (vec4*)data_out;
+	cer_tmps.out_size = data_out_size;
+	cer_tmps.vert_out_count = 0;
+	cer_tmps.prim_out_count = 0;
+	for (k = 0; k < primitive_in_count; ++k) {
+		const int gl_PrimitiveIDIn = k;
+		int j, i;
+		float vertex_id[6];
+		assemble_primitive(vertex_id, input, k, prim_in,
+				   primitive_in_count);
+		cer_tmps.verts_in_current_prim = 0;
+
+		/* Begin geometry shader main */
+		for (j = 0; j < primitive_out_count; j++) {
+			const int max_verts =
+				MIN2(gl_VerticesIn, max_vertex_count);
+			for (i = 0; i < max_verts; i++) {
+				piglit_trafo_fdbk0.x = vertex_id[i];
+				piglit_trafo_fdbk0.y = (float)i;
+				piglit_trafo_fdbk0.z = (float)j;
+				piglit_trafo_fdbk0.w = (float)gl_PrimitiveIDIn;
+				EmitVertex();
+			}
+			for (i = 0; i < extra_vertex_count; i++) {
+				piglit_trafo_fdbk0.x = -1.0;
+				piglit_trafo_fdbk0.y = (float)i;
+				piglit_trafo_fdbk0.z = (float)j;
+				piglit_trafo_fdbk0.w = (float)gl_PrimitiveIDIn;
+				EmitVertex();
+			}
+			EndPrimitive();
+		}
+		/* End geometry shader main */
+	}
+
+	*verts_out = cer_tmps.vert_out_count;
+	*prims_out = cer_tmps.prim_out_count;
+}
+
+
+/* Command line parsing */
+
+struct {
+	const struct primitive_info *in;
+	const struct primitive_info *out;
+	int vert_in_count;
+	int prim_out_count;
+	int max_vert_count;
+	int extra_vert_count;
+} parameters;
+
+static void
+print_usage_and_exit(const char *const name)
+{
+	printf("Usage: %s <primitive_type_in> <primitive_type_out> "
+	       "<vertex_count> <primitive_count> <max_vertex_count> "
+	       "<extra_vertex_count>\n", name);
+	piglit_report_result(PIGLIT_FAIL);
+}
+
+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);
+}
+
+/* Command line arguments in order:
+ *
+ * primitive_type_in -- the type of primitves to draw (as passed to glBegin())
+ * primitive_type_out -- the type of primitives the geometry shader emits
+ * vertex_count -- the number of vertices to draw (as passed to glDrawArrays())
+ * primitive_count -- the number of primitives the geometry shader emits (the
+ *                    number of times it calls EndPrimitive() including
+ *                    implicit call to EndPrimitive() at the end)
+ * max_vertex_count -- the number of vertices the geometry shader sources from
+ *                     the input primitive per primitive it emits (clamped to
+ *                     the number of vertices the input primitive has)
+ * extra_vertex_count -- the number of extra vertices the geometry shader
+ *                       emits per output primitive
+ */
+static void
+parse_cmd_line(const int argc, char *const *const argv)
+{
+	int i, j, k;
+
+	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_in); j++)
+				if (strcmp(piglit_get_prim_name(primitives_in[j].type), argv[i]) == 0)
+					parameters.in = &primitives_in[j];
+			if (parameters.in == NULL)
+				print_usage_and_exit(argv[0]);
+		} else if (k == 1) {
+			for (j = 0; j < ARRAY_SIZE(primitives_out); j++)
+				if (strcmp(piglit_get_prim_name(primitives_out[j].type), argv[i]) == 0)
+					parameters.out = &primitives_out[j];
+			if (parameters.out == NULL)
+				print_usage_and_exit(argv[0]);
+		} else if (k == 2) {
+			parameters.vert_in_count = atoi(argv[i]);
+		} else if (k == 3) {
+			parameters.prim_out_count = atoi(argv[i]);
+		} else if (k == 4) {
+			parameters.max_vert_count = atoi(argv[i]);
+		} else if (k == 5) {
+			parameters.extra_vert_count = atoi(argv[i]);
+		} else {
+			print_usage_and_exit(argv[0]);
+		}
+		k++;
+	}
+
+	check_range(parameters.vert_in_count, "vertex_count", 0, 12);
+	check_range(parameters.prim_out_count, "primitive_count", 0, 2);
+	check_range(parameters.max_vert_count, "max_vertex_count", 0, 6);
+	check_range(parameters.extra_vert_count, "extra_vertex_count", 0, 3);
+}
+
+
+/* Actual test */
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+	config.supports_gl_compat_version = 20;
+	config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA;
+PIGLIT_GL_TEST_CONFIG_END
+
+static const GLuint vertex_attrib0 = 0;
+static const GLuint geometry_vertices_out = 18;
+
+static GLuint trafo_fdbk_query, prog, trafo_fdbk_buf;
+
+static unsigned expected_primitives_written;
+static unsigned expected_vertices_written;
+
+static const float vertex_in_data[12] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+};
+
+static void
+draw(const struct primitive_info prim_in, const struct primitive_info prim_out,
+     const int vertex_in_count, const int primitive_out_count,
+     const int max_vertex_count, const int extra_vertex_count)
+{
+	void *initial_data;
+
+	/* Link shader. */
+	glProgramParameteri(prog, GL_GEOMETRY_INPUT_TYPE_ARB, prim_in.base_type);
+	glProgramParameteri(prog, GL_GEOMETRY_OUTPUT_TYPE_ARB, prim_out.type);
+	glLinkProgram(prog);
+	if (!piglit_link_check_status(prog) ||
+	    !piglit_check_gl_error(GL_NO_ERROR)) {
+		piglit_report_result(PIGLIT_FAIL);
+	}
+	glUseProgram(prog);
+	glUniform1i(glGetUniformLocation(prog, "primitive_out_count"),
+		    primitive_out_count);
+	glUniform1i(glGetUniformLocation(prog, "max_vertex_count"),
+		    max_vertex_count);
+	glUniform1i(glGetUniformLocation(prog, "extra_vertex_count"),
+		    extra_vertex_count);
+
+	/* Create transform feedback buffer and pre-load it with garbage.
+	 */
+	initial_data = malloc((expected_vertices_written + 1) * 16);
+	memset(initial_data, 0xcc, (expected_vertices_written + 1) * 16);
+	glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
+		     (expected_vertices_written + 1) * 16, initial_data,
+		     GL_STREAM_READ);
+	free(initial_data);
+
+	/* Run the test */
+	glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
+		     trafo_fdbk_query);
+	glEnable(GL_RASTERIZER_DISCARD);
+	glBeginTransformFeedback(prim_out.base_type);
+	glDrawArrays(prim_in.type, 0, vertex_in_count);
+	glEndTransformFeedback();
+	glDisable(GL_RASTERIZER_DISCARD);
+	glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
+}
+
+static bool
+test_output(const float expected_data[])
+{
+	bool pass = true;
+	const float *readback;
+	GLuint result;
+	int i;
+
+	/* Check primitives written */
+	glGetQueryObjectuiv(trafo_fdbk_query, GL_QUERY_RESULT, &result);
+	if (result != expected_primitives_written) {
+		printf("Primitives written: expected=%u, actual=%u\n",
+		       expected_primitives_written, result);
+		pass = false;
+	}
+
+	if (expected_primitives_written == 0)
+		return pass;
+
+	/* Check output */
+	readback = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,
+				    expected_vertices_written * 16,
+				    GL_MAP_READ_BIT);
+	for (i = 0; i < expected_vertices_written; i++) {
+		if (memcmp(&readback[i*4], &expected_data[i*4], 16) == 0)
+			continue;
+		printf("geometry_shader_output_data[%d]:"
+		       " expected=(%f %f %f %f), actual=(%f %f %f %f)\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;
+	}
+	glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
+
+	return pass;
+}
+
+static bool
+run_test(const struct primitive_info prim_in,
+	 const struct primitive_info prim_out, const int vertex_count,
+	 const int prim_out_count, const int max_vert_count,
+	 const int extra_vert_count)
+{
+	bool pass = true;
+	static float expected_vertex_out_data[294*4];
+
+	assert(vertex_count <= ARRAY_SIZE(vertex_in_data));
+	assert((6 + extra_vert_count) * prim_out_count <= 256);
+	assert(prim_out_count * (max_vert_count + extra_vert_count) <=
+	       geometry_vertices_out);
+
+	printf("Drawing %s to %s with %d vertices, writing %d primitives using "
+	       "(at most) %d vertices of input primitive and %d extra vertices."
+	       "\n", piglit_get_prim_name(prim_in.type),
+	       piglit_get_prim_name(prim_out.type),
+	       vertex_count, prim_out_count, max_vert_count, extra_vert_count);
+
+	compute_expected_result(expected_vertex_out_data,
+				ARRAY_SIZE(expected_vertex_out_data),
+				&expected_vertices_written,
+				&expected_primitives_written, vertex_in_data,
+				vertex_count, prim_in, prim_out, prim_out_count,
+				max_vert_count, extra_vert_count);
+
+	draw(prim_in, prim_out, vertex_count, prim_out_count, max_vert_count,
+	     extra_vert_count);
+	pass = test_output(expected_vertex_out_data) && pass;
+	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
+
+	if (!pass) {
+		printf("Failed test was %s to %s with %d vertices, writing %d "
+		       "primitives using (at most) %d vertices of input "
+		       "primitive and %d extra vertices.\n",
+		       piglit_get_prim_name(prim_in.type),
+		       piglit_get_prim_name(prim_out.type),
+		       vertex_count, prim_out_count, max_vert_count,
+		       extra_vert_count);
+	}
+
+	return pass;
+}
+
+void
+piglit_init(int argc, char **argv)
+{
+	bool pass;
+	GLuint array_buf;
+	GLuint array;
+	GLuint vs, gs;
+	const char *const varying = "piglit_trafo_fdbk0";
+
+	piglit_require_extension("GL_ARB_geometry_shader4");
+	piglit_require_extension("GL_EXT_transform_feedback");
+	parse_cmd_line(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, &array);
+	glBindVertexArray(array);
+	glGenBuffers(1, &array_buf);
+	glBindBuffer(GL_ARRAY_BUFFER, array_buf);
+	glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_in_data),
+		     vertex_in_data, GL_STREAM_DRAW);
+	glVertexAttribPointer(vertex_attrib0, 1, GL_FLOAT, GL_FALSE, 0, NULL);
+	glEnableVertexAttribArray(vertex_attrib0);
+	glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+	/* Create shader. */
+	prog = glCreateProgram();
+	vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vs_text);
+	gs = piglit_compile_shader_text(GL_GEOMETRY_SHADER, gs_text);
+	glAttachShader(prog, vs);
+	glAttachShader(prog, gs);
+	glDeleteShader(vs);
+	glDeleteShader(gs);
+	glTransformFeedbackVaryings(prog, 1, &varying,
+				    GL_INTERLEAVED_ATTRIBS);
+	glProgramParameteri(prog, GL_GEOMETRY_VERTICES_OUT_ARB,
+			    geometry_vertices_out);
+	glBindAttribLocation(prog, vertex_attrib0, "vertex");
+
+	pass = run_test(*parameters.in, *parameters.out,
+			parameters.vert_in_count, parameters.prim_out_count,
+			parameters.max_vert_count, parameters.extra_vert_count);
+
+	glDeleteBuffers(1, &trafo_fdbk_buf);
+	glDeleteQueries(1, &trafo_fdbk_query);
+	glDeleteProgram(prog);
+	glDeleteVertexArrays(1, &array);
+	glDeleteBuffers(1, &array_buf);
+
+	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
+}
+
+enum piglit_result
+piglit_display(void)
+{
+	/* Should never be reached */
+	return PIGLIT_FAIL;
+}
-- 
1.8.3.2

_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to