[Piglit] [PATCH] Test transform feedback of varying structs.

Ian Romanick idr at freedesktop.org
Tue Feb 19 15:57:46 PST 2013


On 02/05/2013 10:27 AM, Paul Berry wrote:
> The test may be run either against GLSL ES 3.00 or against desktop
> GLSL 1.50.  When desktop GLSL is tested, the test verifies proper
> functioning both with and without the use of interface blocks.
>
> This test is slightly meaner than what's currently necessary to verify
> proper functionality in Mesa (in particular, it tests a variety of
> different nestings of structs and arrays, even though no extra
> implementation effort was necessary to make these tests pass).  I have
> two reasons for wanting to write such a mean test:
>
> - Varying structs in Mesa are currently implemented using a naive
>    layout that packs structure and array contents in order with no gaps
>    (in much the same way that structs are laid out in memory in C).
>    It's likely that in the future we will need to replace this with a
>    more sophisticated mechanism that "flattens" varying structs to
>    their constituent fields.  Such an implementation will have
>    important corner cases involving complex nesting of structs and
>    arrays.
>
> - During my implementation of this feature in Mesa (and my development
>    of this test), I cross-checked against a competing implementation
>    and found several bugs, particularly in cases involving complex
>    nesting of structs and arrays.  It seemed worthwhile to write a test
>    capable of detecting these bugs.

The test looks really good.  I have just a few comments below.

> ---
>
> Note: I would especially appreciate feedback on the draw_rect()
> function (and draw_arrays, which it calls).  I wanted to use
> piglit_draw_rect, but I couldn't because this test needs to be able to
> run in core contexts, and piglit_draw_rect() isn't compatible with
> core contexts.
>
>
>   tests/all.tests                                    |   13 +
>   .../spec/ext_transform_feedback/CMakeLists.gl.txt  |    1 +
>   .../ext_transform_feedback/CMakeLists.gles3.txt    |    7 +
>   tests/spec/ext_transform_feedback/structs.c        | 1356 ++++++++++++++++++++
>   4 files changed, 1377 insertions(+)
>   create mode 100644 tests/spec/ext_transform_feedback/CMakeLists.gles3.txt
>   create mode 100644 tests/spec/ext_transform_feedback/structs.c
>
> diff --git a/tests/all.tests b/tests/all.tests
> index 2ffc6e1..b3f7219 100644
> --- a/tests/all.tests
> +++ b/tests/all.tests
> @@ -1962,6 +1962,19 @@ for test_case in ['base-shrink', 'base-grow', 'offset-shrink', 'offset-grow',
>           test_name = 'change-size {0}'.format(test_case)
>           ext_transform_feedback[test_name] = concurrent_test(
>                   'ext_transform_feedback-{0}'.format(test_name))
> +for api_suffix, possible_options in [('', [[], ['interface']]),
> +                                     ('_gles3', [[]])]:
> +        for subtest in ['basic-struct', 'struct-whole-array',
> +                        'struct-array-elem', 'array-struct',
> +                        'array-struct-whole-array', 'array-struct-array-elem',
> +                        'struct-struct', 'array-struct-array-struct']:
> +                for mode in ['error', 'get', 'run', 'run-no-fs']:
> +                        for options in possible_options:
> +                                args = [subtest, mode] + options
> +                                test_name = 'structs{0} {1}'.format(
> +                                        api_suffix, ' '.join(args))
> +                                ext_transform_feedback[test_name] = concurrent_test(
> +                                        'ext_transform_feedback-{0}'.format(test_name))
>
>   arb_transform_feedback2 = Group()
>   spec['ARB_transform_feedback2'] = arb_transform_feedback2
> diff --git a/tests/spec/ext_transform_feedback/CMakeLists.gl.txt b/tests/spec/ext_transform_feedback/CMakeLists.gl.txt
> index 63c3f4b..93b8aae 100644
> --- a/tests/spec/ext_transform_feedback/CMakeLists.gl.txt
> +++ b/tests/spec/ext_transform_feedback/CMakeLists.gl.txt
> @@ -32,6 +32,7 @@ piglit_add_executable (ext_transform_feedback-separate separate.c)
>   piglit_add_executable (ext_transform_feedback-output-type output-type.c)
>   piglit_add_executable (ext_transform_feedback-order order.c)
>   piglit_add_executable (ext_transform_feedback-overflow-edge-cases overflow-edge-cases.c)
> +piglit_add_executable (ext_transform_feedback-structs structs.c)
>   piglit_add_executable (ext_transform_feedback-tessellation tessellation.c)
>
>   # vim: ft=cmake:
> diff --git a/tests/spec/ext_transform_feedback/CMakeLists.gles3.txt b/tests/spec/ext_transform_feedback/CMakeLists.gles3.txt
> new file mode 100644
> index 0000000..32b92bd
> --- /dev/null
> +++ b/tests/spec/ext_transform_feedback/CMakeLists.gles3.txt
> @@ -0,0 +1,7 @@
> +link_libraries(
> +	piglitutil_${piglit_target_api}
> +	)
> +
> +piglit_add_executable (ext_transform_feedback-structs_${piglit_target_api} structs.c)
> +
> +# vim: ft=cmake:
> diff --git a/tests/spec/ext_transform_feedback/structs.c b/tests/spec/ext_transform_feedback/structs.c
> new file mode 100644
> index 0000000..1872494
> --- /dev/null
> +++ b/tests/spec/ext_transform_feedback/structs.c
> @@ -0,0 +1,1356 @@
> +/*
> + * Copyright © 2013 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 structs.c
> + *
> + * Test proper functioning of transform feedback with varying structs.
> + *
> + * The spec is ambiguous about how transform feedback is supposed to
> + * interact with varying structs.  However, the Khronos board has
> + * clarified that:
> + *
> + * - Whole structures (or array of structures) cannot be bound all at
> + *   once using glTransformFeedbackVaryings().
> + *
> + * - Instead, the caller must apply transform feedback to individual
> + *   elements of structs, by using the "." character in the string
> + *   passed to glTransformFeedbackVaryings().
> + *
> + * - The intention is for the transform feedback API to behave
> + *   similary to glGetUniformLocation() and
> + *   glGetProgramResourceLocation().
> + *
> + * This test verifies proper operation of transform feedback varyings
> + * according to the above clarifications.
> + *
> + * Because of the subtle interactions between structs and arrays, this
> + * test contains several sub-tests, each concerned with verifying a
> + * particular combination of arrays and structs:
> + *
> + * - basic-struct: Each varying is a struct containing simple types
> + *   (e.g. vec4, float, mat3).
> + *
> + * - struct-whole-array: Each varying is a struct containing arrays of
> + *   simple types, and transform feedback is applied to whole arrays.
> + *
> + * - struct-array-elem: Each varying is a struct containing arrays of
> + *   simple types, and transform feedback is applied to individual
> + *   array elements.
> + *
> + * - array-struct: Each varying is an array of structs containing
> + *   simple types.
> + *
> + * - array-struct-whole-array: Each varying is an array of structs
> + *   containing arrays of simple types, and transform feedback is
> + *   applied to whole arrays within each struct.
> + *
> + * - array-struct-array-elem: Each varying is an array of structs
> + *   containing arrays of simple types, and transform feedback is
> + *   applied to individual array elements.
> + *
> + * - struct-struct: Each varying is a struct containing structs.
> + *
> + * - array-struct-array-struct: Each varying is an array of structs
> + *   containing arrays of structs.
> + *
> + * Each of these variants may be run in one of four modes:
> + *
> + * - error: attempt to specify invalid values for
> + *   glTransformFeedbackVaryings() and verify that the shaders fail to
> + *   link.
> + *
> + * - get: link the shaders and verify that the values returned by
> + *   glGetTransformFeedbackVarying() are correct.
> + *
> + * - run: draw using the shaders, and verify that (a) the values
> + *   stored in the transform feedback buffer are correct, and (b) the
> + *   values delivered to the fragment shader are correct.
> + *
> + * - run-no-fs: link with just a vertex shader and no fragment
> + *   shader*, and draw with GL_RASTERIZER_DISCARD enabled.  Verify
> + *   that the values stored in the transform feedback buffer are
> + *   correct.
> + *
> + * (*In GLES3, a fragment shader is required, so "run-no-fs" mode
> + * links to a generic do-nothing fragment shader).
> + *
> + * Furthermore, when testing on desktop GL, the optional command-line
> + * parameter "interface" may be given, to cause the test to be run
> + * using interface blocks.
> + */
> +
> +#include "piglit-util-gl-common.h"
> +
> +PIGLIT_GL_TEST_CONFIG_BEGIN
> +
> +#ifdef PIGLIT_USE_OPENGL
> +	config.supports_gl_core_version = 32;
> +#else /* PIGLIT_USE_OPENGL_ES3 */
> +	config.supports_gl_es_version = 30;
> +#endif
> +	config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_ALPHA;
> +
> +PIGLIT_GL_TEST_CONFIG_END
> +
> +
> +#define MAX_VARYINGS 21
> +#define NUM_VERTICES 6
> +#define MAX_COMPONENTS 20
> +#define VERTEX_ATTRIB_POS 0
> +
> +
> +/**
> + * Header attached to the top of each shader when testing GLES 3.0.
> + */
> +static const char gles3_header[] =
> +	"#version 300 es\n"
> +	"#define DECLARE_VARYING(DIR, TYPE, NAME) flat DIR TYPE NAME\n"
> +	"#define VARYING(NAME) NAME\n";
> +
> +
> +/**
> + * Header attached to the top of each shader when testing desktop GL
> + * and not using interface blocks.
> + */
> +static const char desktop_header[] =
> +	"#version 150\n"
> +	"#define DECLARE_VARYING(DIR, TYPE, NAME) flat DIR TYPE NAME\n"
> +	"#define VARYING(NAME) NAME\n";
> +
> +
> +/**
> + * Header attached to the top of each shader when testing desktop GL
> + * and using interface blocks.
> + */
> +static const char desktop_header_interface[] =
> +	"#version 150\n"
> +	"#define DECLARE_VARYING(DIR, TYPE, NAME) DIR Blk { flat TYPE NAME; } blk\n"
> +	"#define VARYING(NAME) blk.NAME\n";
> +
> +
> +/**
> + * Description of each possible sub-test.
> + */
> +static struct test_desc {
> +	/** Name of the test. */
> +	const char *name;
> +
> +	/**
> +	 * Vertex shader source text.  This will be concatenated with
> +	 * one of the above "header" strings.  Accordingly, it should
> +	 * use the macros DECLARE_VARYING() to declare a varying, and
> +	 * VARYING() to access a varying.  (These macros expand
> +	 * differently depending whether the test is using interface
> +	 * blocks or not).
> +	 */
> +	const char *vs;
> +
> +	/**
> +	 * Fragment shader source text.  As with the vertex shader,
> +	 * this will be concatenated with one of the above "header"
> +	 * strings.
> +	 */
> +	const char *fs;
> +
> +	/**
> +	 * Names which, if passed to glTransformFeedbackVaryings(),
> +	 * should result in a link error.  Terminated by a NULL
> +	 * pointer.
> +	 */
> +	const char *bad_varyings[MAX_VARYINGS];
> +
> +	/**
> +	 * Names which, if passed to glTransformFeedbackVaryings(),
> +	 * should result in proper operation.  When using interface
> +	 * blocks, each of these varying names will be prefixed with
> +	 * "Blk.".  Terminated by a NULL pointer.
> +	 */
> +	const char *good_varyings[MAX_VARYINGS];
> +
> +	/**
> +	 * Expected types which should be returned by
> +	 * glGetTransformFeedbackVarying() for each of the varyings in
> +	 * \c good_varyings.
> +	 */
> +	GLenum expected_types[MAX_VARYINGS];
> +
> +	/**
> +	 * Expected sizes which should be returned by
> +	 * glGetTransformFeedbackVarying() for each of the varyings in
> +	 * \c good_varyings.
> +	 */
> +	unsigned expected_sizes[MAX_VARYINGS];
> +
> +	/**
> +	 * Expected varying values which transform feedback should
> +	 * capture when running this test (floating-point values
> +	 * only).
> +	 */
> +	float expected_floats[MAX_COMPONENTS];
> +
> +	/**
> +	 * Expected varying values which transform feedback should
> +	 * capture when running this test (integral values only).
> +	 */
> +	int expected_ints[MAX_COMPONENTS];
> +} tests[] = {
> +	{
> +		/* name */
> +		"basic-struct",
> +
> +		/* vs */
> +		"struct S { float a; vec4 b; mat3 c; ivec2 d; uvec3 e; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, S, v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  VARYING(v).a = 1.0;\n"
> +		"  VARYING(v).b = vec4(2.0, 3.0, 4.0, 5.0);\n"
> +		"  VARYING(v).c = mat3(6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0);\n"
> +		"  VARYING(v).d = ivec2(15, 16);\n"
> +		"  VARYING(v).e = uvec3(17, 18, 19);\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { float a; vec4 b; mat3 c; ivec2 d; uvec3 e; };\n"
> +		"DECLARE_VARYING(in, S, v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  if (VARYING(v).a != 1.0) pass = false;\n"
> +		"  if (VARYING(v).b != vec4(2.0, 3.0, 4.0, 5.0)) pass = false;\n"
> +		"  if (VARYING(v).c != mat3(6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0)) pass = false;\n"
> +		"  if (VARYING(v).d != ivec2(15, 16)) pass = false;\n"
> +		"  if (VARYING(v).e != uvec3(17, 18, 19)) pass = false;\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", NULL },
> +
> +		/* good_varyings */
> +		{ "v.a", "v.b", "v.c", "v.d", "v.e", NULL },
> +
> +		/* expected_types */
> +		{ GL_FLOAT, GL_FLOAT_VEC4, GL_FLOAT_MAT3, GL_INT_VEC2,
> +		  GL_UNSIGNED_INT_VEC3 },
> +
> +		/* expected_sizes */
> +		{ 1, 1, 1, 1, 1 },
> +
> +		/* expected_floats */
> +		{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0,
> +		  12.0, 13.0, 14.0 },
> +
> +		/* expected_ints */
> +		{ 15, 16, 17, 18, 19 }
> +	},
> +	{
> +		/* name */
> +		"struct-whole-array",
> +
> +		/* vs */
> +		"struct S { uvec4[4] a; vec2[2] b; int[3] c; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, S, v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  for (int i = 0; i < 4; i++) {\n"
> +		"    if (i < 4) VARYING(v).a[i] = uvec4(100, 200, 300, 400) + uint(i);\n"
> +		"    if (i < 2) VARYING(v).b[i] = vec2(500.0, 600.0) + float(i);\n"
> +		"    if (i < 3) VARYING(v).c[i] = 700 + i;\n"
> +		"  }\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { uvec4[4] a; vec2[2] b; int[3] c; };\n"
> +		"DECLARE_VARYING(in, S, v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  for (int i = 0; i < 3; i++) {\n"
> +		"    if (i < 4 && VARYING(v).a[i] != uvec4(100, 200, 300, 400) + uint(i)) pass = false;\n"
> +		"    if (i < 2 && VARYING(v).b[i] != vec2(500.0, 600.0) + float(i)) pass = false;\n"
> +		"    if (i < 3 && VARYING(v).c[i] != 700 + i) pass = false;\n"
> +		"  }\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", NULL },
> +
> +		/* good_varyings */
> +		{ "v.a", "v.b", "v.c", NULL },
> +
> +		/* expected_types */
> +		{ GL_UNSIGNED_INT_VEC4, GL_FLOAT_VEC2, GL_INT },
> +
> +		/* expected_sizes */
> +		{ 4, 2, 3 },
> +
> +		/* expected_floats */
> +		{ 500.0, 600.0, 501.0, 601.0 },
> +
> +		/* expected_ints */
> +		{ 100, 200, 300, 400, 101, 201, 301, 401, 102, 202, 302, 402,
> +		  103, 203, 303, 403, 700, 701, 702 }
> +	},
> +	{
> +		/* name */
> +		"struct-array-elem",
> +
> +		/* vs */
> +		"struct S { ivec4[2] a; uint[4] b; vec3[3] c; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, S, v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  for (int i = 0; i < 4; i++) {\n"
> +		"    if (i < 2) VARYING(v).a[i] = ivec4(100, 200, 300, 400) + i;\n"
> +		"    if (i < 4) VARYING(v).b[i] = 500u + uint(i);\n"
> +		"    if (i < 3) VARYING(v).c[i] = vec3(600.0, 700.0, 800.0) + float(i);\n"
> +		"  }\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { ivec4[2] a; uint[4] b; vec3[3] c; };\n"
> +		"DECLARE_VARYING(in, S, v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  for (int i = 0; i < 3; i++) {\n"
> +		"    if (i < 2 && VARYING(v).a[i] != ivec4(100, 200, 300, 400) + i) pass = false;\n"
> +		"    if (i < 4 && VARYING(v).b[i] != 500u + uint(i)) pass = false;\n"
> +		"    if (i < 3 && VARYING(v).c[i] != vec3(600.0, 700.0, 800.0) + float(i)) pass = false;\n"
> +		"  }\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", NULL },
> +
> +		/* good_varyings */
> +		{ "v.a[0]", "v.a[1]", "v.b[0]", "v.b[1]", "v.b[2]", "v.b[3]",
> +		  "v.c[0]", "v.c[1]", "v.c[2]", NULL },
> +
> +		/* expected_types */
> +		{ GL_INT_VEC4, GL_INT_VEC4, GL_UNSIGNED_INT, GL_UNSIGNED_INT,
> +		  GL_UNSIGNED_INT, GL_UNSIGNED_INT, GL_FLOAT_VEC3,
> +		  GL_FLOAT_VEC3, GL_FLOAT_VEC3 },
> +
> +		/* expected_sizes */
> +		{ 1, 1, 1, 1, 1, 1, 1, 1, 1 },
> +
> +		/* expected_floats */
> +		{ 600.0, 700.0, 800.0, 601.0, 701.0, 801.0, 602.0, 702.0, 802.0 },
> +
> +		/* expected_ints */
> +		{ 100, 200, 300, 400, 101, 201, 301, 401, 500, 501, 502, 503 }
> +	},
> +	{
> +		/* name */
> +		"array-struct",
> +
> +		/* vs */
> +		"struct S { mat2 a; ivec3 b; uvec2 c; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, S[3], v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  for (int i = 0; i < 3; i++) {\n"
> +		"    VARYING(v)[i].a = mat2(100.0, 200.0, 300.0, 400.0) + float(i);\n"
> +		"    VARYING(v)[i].b = ivec3(500, 600, 700) + i;\n"
> +		"    VARYING(v)[i].c = uvec2(800, 900) + uint(i);\n"
> +		"  }\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { mat2 a; ivec3 b; uvec2 c; };\n"
> +		"DECLARE_VARYING(in, S[3], v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  for (int i = 0; i < 3; i++) {\n"
> +		"    if (VARYING(v)[i].a != mat2(100.0, 200.0, 300.0, 400.0) + float(i)) pass = false;\n"
> +		"    if (VARYING(v)[i].b != ivec3(500, 600, 700) + i) pass = false;\n"
> +		"    if (VARYING(v)[i].c != uvec2(800, 900) + uint(i)) pass = false;\n"
> +		"  }\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", "v[0]", "v[1]", "v[2]", "v.a", "v.b", "v.c", NULL },
> +
> +		/* good_varyings */
> +		{ "v[0].a", "v[0].b", "v[0].c", "v[1].a", "v[1].b", "v[1].c",
> +		  "v[2].a", "v[2].b", "v[2].c", NULL },
> +
> +		/* expected_types */
> +		{ GL_FLOAT_MAT2, GL_INT_VEC3, GL_UNSIGNED_INT_VEC2,
> +		  GL_FLOAT_MAT2, GL_INT_VEC3, GL_UNSIGNED_INT_VEC2,
> +		  GL_FLOAT_MAT2, GL_INT_VEC3, GL_UNSIGNED_INT_VEC2 },
> +
> +		/* expected_sizes */
> +		{ 1, 1, 1, 1, 1, 1, 1, 1, 1 },
> +
> +		/* expected_floats */
> +		{ 100.0, 200.0, 300.0, 400.0, 101.0, 201.0, 301.0, 401.0,
> +		  102.0, 202.0, 302.0, 402.0 },
> +
> +		/* expected_ints */
> +		{ 500, 600, 700, 800, 900, 501, 601, 701, 801, 901, 502, 602,
> +		  702, 802, 902 }
> +	},
> +	{
> +		/* name */
> +		"array-struct-whole-array",
> +
> +		/* vs */
> +		"struct S { int[2] a; int[3] b; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, S[4], v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  for (int i = 0; i < 4; i++) {\n"
> +		"    for (int j = 0; j < 3; j++) {\n"
> +		"      if (j < 2) VARYING(v)[i].a[j] = 100 * i + 10 * j + 1;\n"
> +		"      if (j < 3) VARYING(v)[i].b[j] = 100 * i + 10 * j + 2;\n"
> +		"    }\n"
> +		"  }\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { int[2] a; int[3] b; };\n"
> +		"DECLARE_VARYING(in, S[4], v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  for (int i = 0; i < 4; i++) {\n"
> +		"    for (int j = 0; j < 3; j++) {\n"
> +		"      if (j < 2 && VARYING(v)[i].a[j] != 100 * i + 10 * j + 1) pass = false;\n"
> +		"      if (j < 3 && VARYING(v)[i].b[j] != 100 * i + 10 * j + 2) pass = false;\n"
> +		"    }\n"
> +		"  }\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", "v[0]", "v[1]", "v[2]", "v[3]", "v.a", "v.b", NULL },
> +
> +		/* good_varyings */
> +		{ "v[0].a", "v[0].b", "v[1].a", "v[1].b", "v[2].a", "v[2].b",
> +		  "v[3].a", "v[3].b", NULL },
> +
> +		/* expected_types */
> +		{ GL_INT, GL_INT, GL_INT, GL_INT, GL_INT, GL_INT, GL_INT,
> +		  GL_INT },
> +
> +		/* expected_sizes */
> +		{ 2, 3, 2, 3, 2, 3, 2, 3 },
> +
> +		/* expected_floats */
> +		{ },
> +
> +		/* expected_ints */
> +		{ 1, 11, 2, 12, 22, 101, 111, 102, 112, 122, 201, 211, 202,
> +		  212, 222, 301, 311, 302, 312, 322 }
> +	},
> +	{
> +		/* name */
> +		"array-struct-array-elem",
> +
> +		/* vs */
> +		"struct S { int[2] a; int[3] b; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, S[4], v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  for (int i = 0; i < 4; i++) {\n"
> +		"    for (int j = 0; j < 3; j++) {\n"
> +		"      if (j < 2) VARYING(v)[i].a[j] = 100 * i + 10 * j + 1;\n"
> +		"      if (j < 3) VARYING(v)[i].b[j] = 100 * i + 10 * j + 2;\n"
> +		"    }\n"
> +		"  }\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { int[2] a; int[3] b; };\n"
> +		"DECLARE_VARYING(in, S[4], v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  for (int i = 0; i < 4; i++) {\n"
> +		"    for (int j = 0; j < 3; j++) {\n"
> +		"      if (j < 2 && VARYING(v)[i].a[j] != 100 * i + 10 * j + 1) pass = false;\n"
> +		"      if (j < 3 && VARYING(v)[i].b[j] != 100 * i + 10 * j + 2) pass = false;\n"
> +		"    }\n"
> +		"  }\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", "v[0]", "v[1]", "v[2]", "v[3]", "v.a", "v.b", NULL },
> +
> +		/* good_varyings */
> +		{ "v[0].a[0]", "v[0].a[1]", "v[0].b[0]", "v[0].b[1]",
> +		  "v[0].b[2]", "v[1].a[0]", "v[1].a[1]", "v[1].b[0]",
> +		  "v[1].b[1]", "v[1].b[2]", "v[2].a[0]", "v[2].a[1]",
> +		  "v[2].b[0]", "v[2].b[1]", "v[2].b[2]", "v[3].a[0]",
> +		  "v[3].a[1]", "v[3].b[0]", "v[3].b[1]", "v[3].b[2]", NULL },
> +
> +		/* expected_types */
> +		{ GL_INT, GL_INT, GL_INT, GL_INT, GL_INT, GL_INT, GL_INT,
> +		  GL_INT, GL_INT, GL_INT, GL_INT, GL_INT, GL_INT, GL_INT,
> +		  GL_INT, GL_INT, GL_INT, GL_INT, GL_INT, GL_INT },
> +
> +		/* expected_sizes */
> +		{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
> +
> +		/* expected_floats */
> +		{ },
> +
> +		/* expected_ints */
> +		{ 1, 11, 2, 12, 22, 101, 111, 102, 112, 122, 201, 211, 202,
> +		  212, 222, 301, 311, 302, 312, 322 }
> +	},
> +	{
> +		/* name */
> +		"struct-struct",
> +
> +		/* vs */
> +		"struct S { int a; float b; };\n"
> +		"struct T { float c; int d; };\n"
> +		"struct U { S e; T f; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, U, v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  VARYING(v).e.a = 1;\n"
> +		"  VARYING(v).e.b = 2.0;\n"
> +		"  VARYING(v).f.c = 3.0;\n"
> +		"  VARYING(v).f.d = 4;\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { int a; float b; };\n"
> +		"struct T { float c; int d; };\n"
> +		"struct U { S e; T f; };\n"
> +		"DECLARE_VARYING(in, U, v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  if (VARYING(v).e.a != 1) pass = false;\n"
> +		"  if (VARYING(v).e.b != 2.0) pass = false;\n"
> +		"  if (VARYING(v).f.c != 3.0) pass = false;\n"
> +		"  if (VARYING(v).f.d != 4) pass = false;\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", "v.e", "v.f", NULL },
> +
> +		/* good_varyings */
> +		{ "v.e.a", "v.e.b", "v.f.c", "v.f.d", NULL },
> +
> +		/* expected_types */
> +		{ GL_INT, GL_FLOAT, GL_FLOAT, GL_INT },
> +
> +		/* expected_sizes */
> +		{ 1, 1, 1, 1 },
> +
> +		/* expected_floats */
> +		{ 2.0, 3.0 },
> +
> +		/* expected_ints */
> +		{ 1, 4 }
> +	},
> +	{
> +		/* name */
> +		"array-struct-array-struct",
> +
> +		/* vs */
> +		"struct S { int a; float b; };\n"
> +		"struct T { float c; int d; };\n"
> +		"struct U { S[2] e; T[2] f; };\n"
> +		"in vec4 pos;\n"
> +		"DECLARE_VARYING(out, U[2], v);\n"
> +		"void main()\n"
> +		"{\n"
> +		"  gl_Position = pos;\n"
> +		"  for (int i = 0; i < 2; i++) {\n"
> +		"    for (int j = 0; j < 2; j++) {\n"
> +		"      VARYING(v)[i].e[j].a = 100 * i + 10 * j + 1;\n"
> +		"      VARYING(v)[i].e[j].b = float(100 * i + 10 * j + 2);\n"
> +		"      VARYING(v)[i].f[j].c = float(100 * i + 10 * j + 3);\n"
> +		"      VARYING(v)[i].f[j].d = 100 * i + 10 * j + 4;\n"
> +		"    }\n"
> +		"  }\n"
> +		"}\n",
> +
> +		/* fs */
> +		"struct S { int a; float b; };\n"
> +		"struct T { float c; int d; };\n"
> +		"struct U { S[2] e; T[2] f; };\n"
> +		"DECLARE_VARYING(in, U[2], v);\n"
> +		"out vec4 color;\n"
> +		"void main()\n"
> +		"{\n"
> +		"  bool pass = true;\n"
> +		"  for (int i = 0; i < 2; i++) {\n"
> +		"    for (int j = 0; j < 2; j++) {\n"
> +		"      if (VARYING(v)[i].e[j].a != 100 * i + 10 * j + 1) pass = false;\n"
> +		"      if (VARYING(v)[i].e[j].b != float(100 * i + 10 * j + 2)) pass = false;\n"
> +		"      if (VARYING(v)[i].f[j].c != float(100 * i + 10 * j + 3)) pass = false;\n"
> +		"      if (VARYING(v)[i].f[j].d != 100 * i + 10 * j + 4) pass = false;\n"
> +		"    }\n"
> +		"  }\n"
> +		"  if (pass)\n"
> +		"    color = vec4(0.0, 1.0, 0.0, 1.0);\n"
> +		"  else\n"
> +		"    color = vec4(1.0, 0.0, 0.0, 1.0);\n"
> +		"}\n",
> +
> +		/* bad_varyings */
> +		{ "v", "v[0]", "v[1]", "v[0].e", "v[0].f", "v[1].e",
> +		  "v[1].f", "v[0].e[0]", "v[0].e[1]", "v[0].f[0]", "v[0].f[1]",
> +		  "v[1].e[0]", "v[1].e[1]", "v[1].f[0]", "v[1].f[1]", "v.e.a",
> +		  "v.e.b", "v.f.c", "v.f.d", NULL },
> +
> +		/* good_varyings */
> +		{ "v[0].e[0].a", "v[0].e[1].a", "v[1].e[0].a", "v[1].e[1].a",
> +		  "v[0].e[0].b", "v[0].e[1].b", "v[1].e[0].b", "v[1].e[1].b",
> +		  "v[0].f[0].c", "v[0].f[1].c", "v[1].f[0].c", "v[1].f[1].c",
> +		  "v[0].f[0].d", "v[0].f[1].d", "v[1].f[0].d", "v[1].f[1].d",
> +		  NULL },
> +
> +		/* expected_types */
> +		{ GL_INT, GL_INT, GL_INT, GL_INT, GL_FLOAT, GL_FLOAT, GL_FLOAT,
> +		  GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_INT,
> +		  GL_INT, GL_INT, GL_INT },
> +
> +		/* expected_sizes */
> +		{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
> +
> +		/* expected_floats */
> +		{ 2.0, 12.0, 102.0, 112.0, 3.0, 13.0, 103.0, 113.0 },
> +
> +		/* expected_ints */
> +		{ 1, 11, 101, 111, 4, 14, 104, 114 }
> +	},
> +};
> +
> +
> +#ifdef PIGLIT_USE_OPENGL_ES3
> +/**
> + * Generic do-nothing fragment shader used when running tests in
> + * "run-no-fs" mode on GLES3, since GLES3 always requires a fragment
> + * shader to be present.
> + */
> +static const char *generic_gles3_fs_text =
> +	"out vec4 color;\n"
> +	"void main()\n"
> +	"{\n"
> +	"  color = vec4(0.5);\n"
> +	"}\n";
> +#endif
> +
> +
> +static struct test_desc *test = NULL;
> +static GLuint prog;
> +static bool use_interface_blocks = false;
> +
> +
> +/**
> + * Count the number of strings in the given NULL-terminated array of
> + * strings.
> + */
> +static unsigned
> +count_strings(const char * const *strings)
> +{
> +	unsigned i;
> +	for (i = 0; ; i++) {
> +		if (strings[i] == NULL)
> +			return i;
> +	}
> +}
> +
> +
> +/**
> + * Choose which header should be prepended to each of the shaders
> + * being tested, based on whether GL or GLSL is being used, and based
> + * on whether the test uses interface blocks.
> + */
> +static const char *
> +choose_header()
> +{
> +#ifdef PIGLIT_USE_OPENGL
> +	(void) gles3_header;
> +	if (use_interface_blocks)
> +		return desktop_header_interface;
> +	else
> +		return desktop_header;
> +#else /* PIGLIT_USE_OPENGL_ES3 */
> +	(void) desktop_header_interface;
> +	(void) desktop_header;
> +	return gles3_header;
> +#endif
> +}
> +
> +
> +/**
> + * Attach the appropriate header to the shader and compile it.
> + */
> +static GLuint
> +compile_shader(GLenum target, const char *shader_text)
> +{
> +	const char *header = choose_header();
> +	unsigned len = strlen(header) + strlen(shader_text) + 1;
> +	char *concatenated_text = malloc(len);
> +	GLuint shader;
> +	strcpy(concatenated_text, header);
> +	strcat(concatenated_text, shader_text);

asprintf?

> +	shader = piglit_compile_shader_text(target, concatenated_text);
> +	free(concatenated_text);
> +	return shader;
> +}
> +
> +
> +/**
> + * Given an array of strings representing the names of varyings,
> + * return a newly allocated array with \c prefix prepended to each
> + * varying name.
> + */
> +static const char **
> +prepend_varyings(const char *prefix, const char * const *varyings)
> +{
> +	unsigned prefix_len = strlen(prefix);
> +	unsigned num_varyings = count_strings(varyings);
> +	char **result = calloc(num_varyings + 1, sizeof(char *));
> +	unsigned i;
> +	for (i = 0; i < num_varyings; i++) {
> +		unsigned varying_len = strlen(varyings[i]);
> +		result[i] = malloc(prefix_len + varying_len + 1);
> +		strcpy(result[i], prefix);
> +		strcat(result[i], varyings[i]);

asprintf?

> +	}
> +	result[num_varyings] = NULL;
> +	return (const char **) result;
> +}
> +
> +
> +/**
> + * Free an array allocated by \c prepend_varyings.
> + */
> +static void
> +free_varyings(const char **varyings)
> +{
> +	unsigned i;
> +	for (i = 0; varyings[i] != NULL; i++)
> +		free((char *) varyings[i]);
> +	free(varyings);
> +}
> +
> +
> +/**
> + * Link the appropriate set of shaders for running a positive test,
> + * calling glTransformFeedbackVaryings() to set up transform feedback.
> + */
> +static void
> +link_shaders(bool use_fs)
> +{
> +	GLuint vs;
> +	const char **varyings;
> +	prog = glCreateProgram();
> +	vs = compile_shader(GL_VERTEX_SHADER, test->vs);
> +	glAttachShader(prog, vs);
> +	glDeleteShader(vs);
> +	if (use_fs) {
> +		GLuint fs = compile_shader(GL_FRAGMENT_SHADER, test->fs);
> +		glAttachShader(prog, fs);
> +		glDeleteShader(fs);
> +	} else {
> +#ifdef PIGLIT_USE_OPENGL_ES3
> +		GLuint fs = compile_shader(GL_FRAGMENT_SHADER, generic_gles3_fs_text);
> +		glAttachShader(prog, fs);
> +		glDeleteShader(fs);
> +#endif
> +	}
> +	if (use_interface_blocks)
> +		varyings = prepend_varyings("Blk.", test->good_varyings);
> +	else
> +		varyings = test->good_varyings;
> +	glTransformFeedbackVaryings(prog, count_strings(test->good_varyings),
> +				    (const GLchar **) varyings,
> +				    GL_INTERLEAVED_ATTRIBS);
> +	if (use_interface_blocks)
> +		free_varyings(varyings);
> +	if (!piglit_check_gl_error(GL_NO_ERROR)) {
> +		glDeleteProgram(prog);
> +		piglit_report_result(PIGLIT_FAIL);

Since there are a lot of combinations of test modes and shaders.  In 
addition to dumping out the reason for the failure, it may be useful to 
dump the shader too.

> +	}
> +	glBindAttribLocation(prog, VERTEX_ATTRIB_POS, "pos");
> +	glLinkProgram(prog);
> +	if (!piglit_link_check_status(prog)) {
> +		glDeleteProgram(prog);
> +		piglit_report_result(PIGLIT_FAIL);
> +	}
> +}
> +
> +
> +/**
> + * Verify that passing the name \c varying to
> + * glTransformFeedbackVaryings() produces a link error.
> + *
> + * If it does not, a description of the problem is printed and false
> + * is returned.
> + */
> +static bool
> +test_bad_varying(GLuint vs, GLuint fs, const char *varying)
> +{
> +	GLint ok;
> +
> +	prog = glCreateProgram();
> +	glAttachShader(prog, vs);
> +	glAttachShader(prog, fs);
> +	glTransformFeedbackVaryings(prog, 1, &varying, GL_INTERLEAVED_ATTRIBS);
> +	if (!piglit_check_gl_error(GL_NO_ERROR))
> +		goto fail;
> +	glLinkProgram(prog);
> +	glGetProgramiv(prog, GL_LINK_STATUS, &ok);
> +	if (ok) {
> +		printf("Varying %s linked successfully, should have failed.\n",
> +		       varying);
> +		goto fail;
> +	}
> +	/* Link failure shouldn't produce a GL error */
> +	if (!piglit_check_gl_error(GL_NO_ERROR))
> +		goto fail;
> +	glDeleteProgram(prog);
> +	prog = 0;
> +	if (!piglit_check_gl_error(GL_NO_ERROR))
> +		goto fail;
> +	return true;
> +
> + fail:
> +	if (prog != 0)
> +		glDeleteProgram(prog);
> +	return false;
> +}
> +
> +
> +/**
> + * Verify that "bad" varying names produces the expected link error.
> + *
> + * When using interface blocks, this function also verifies that (a)
> + * "bad" varying names produce a link error if they are prepended with
> + * "Blk.", and (b) "good" varying names produce a link error if they
> + * are *not* prepended with "Blk.".
> + */
> +static enum piglit_result
> +test_errors()
> +{
> +	unsigned i;
> +	unsigned num_bad_varyings = count_strings(test->bad_varyings);
> +	unsigned num_good_varyings = count_strings(test->good_varyings);
> +	GLuint vs, fs;
> +	bool pass = true;
> +
> +	prog = 0;
> +	vs = compile_shader(GL_VERTEX_SHADER, test->vs);
> +	fs = compile_shader(GL_FRAGMENT_SHADER, test->fs);
> +
> +	/* Test one bad varying at a time to make sure they all
> +	 * produce the proper error.
> +	 */
> +	for (i = 0; i < num_bad_varyings; i++)
> +		pass = test_bad_varying(vs, fs, test->bad_varyings[i]) && pass;
> +
> +	if (use_interface_blocks) {
> +		/* Test that the "bad" varyings fail if prepended with
> +		 * "Blk."
> +		 */
> +		const char **varyings
> +			= prepend_varyings("Blk.", test->bad_varyings);
> +		for (i = 0; i < num_bad_varyings; i++)
> +			pass = test_bad_varying(vs, fs, varyings[i]) && pass;
> +		free_varyings(varyings);
> +
> +		/* Test that the "good" varyings fail if *not*
> +		 * prepended with "Blk."
> +		 */
> +		for (i = 0; i < num_good_varyings; i++) {
> +			pass = test_bad_varying(vs, fs, test->good_varyings[i])
> +				&& pass;
> +		}
> +	}
> +
> +	glDeleteShader(vs);
> +	glDeleteShader(fs);
> +
> +	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
> +}
> +
> +
> +/**
> + * Verify that glGetTransformFeedbackVarying() returns the proper
> + * information for all "good" varying names.
> + *
> + * The program should already be linked and stored in the global \c
> + * prog.
> + */
> +static enum piglit_result
> +test_gets()
> +{
> +	unsigned i;
> +	unsigned num_good_varyings = count_strings(test->good_varyings);
> +	const char **varyings;
> +	bool pass = true;
> +
> +	if (use_interface_blocks)
> +		varyings = prepend_varyings("Blk.", test->good_varyings);
> +	else
> +		varyings = test->good_varyings;
> +
> +	for (i = 0; i < num_good_varyings; i++) {
> +		const char *exp_name = varyings[i];
> +		GLsizei exp_length = strlen(exp_name);
> +		GLsizei exp_size = test->expected_sizes[i];
> +		GLenum exp_type = test->expected_types[i];
> +		GLsizei length;
> +		GLsizei size;
> +		GLenum type;
> +		char name[100];
> +		glGetTransformFeedbackVarying(prog, i, sizeof(name), &length,
> +					      &size, &type, name);
> +		if (length != exp_length || size != exp_size
> +		    || type != exp_type || strcmp(name, exp_name) != 0) {
> +			pass = false;
> +			printf("glGetTransformFeedbackVarying() returned "
> +			       "unexpected data for varying %u:\n", i);
> +			printf("  length: expected %u, got %u\n",
> +			       exp_length, length);
> +			printf("  size: expected %u, got %u\n",
> +			       exp_size, size);
> +			printf("  type: expected %u (%s), got %u (%s)\n",
> +			       exp_type, piglit_get_gl_enum_name(exp_type),
> +			       type, piglit_get_gl_enum_name(type));
> +			printf("  name: expected %s, got %s\n",
> +			       exp_name, name);
> +		}
> +	}
> +
> +	if (use_interface_blocks)
> +		free_varyings(varyings);
> +
> +	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
> +}
> +
> +
> +/**
> + * Compute the number of varying slots occupied by a given type.
> + */
> +static unsigned
> +size_of_type(GLenum type)
> +{
> +	switch (type) {
> +	case GL_FLOAT:             return  1;
> +	case GL_FLOAT_VEC2:        return  2;
> +	case GL_FLOAT_VEC3:        return  3;
> +	case GL_FLOAT_VEC4:        return  4;
> +	case GL_FLOAT_MAT2:        return  4;
> +	case GL_FLOAT_MAT2x3:      return  6;
> +	case GL_FLOAT_MAT2x4:      return  8;
> +	case GL_FLOAT_MAT3x2:      return  6;
> +	case GL_FLOAT_MAT3:        return  9;
> +	case GL_FLOAT_MAT3x4:      return 12;
> +	case GL_FLOAT_MAT4x2:      return  8;
> +	case GL_FLOAT_MAT4x3:      return 12;
> +	case GL_FLOAT_MAT4:        return 16;
> +	case GL_INT:               return  1;
> +	case GL_INT_VEC2:          return  2;
> +	case GL_INT_VEC3:          return  3;
> +	case GL_INT_VEC4:          return  4;
> +	case GL_UNSIGNED_INT:      return  1;
> +	case GL_UNSIGNED_INT_VEC2: return  2;
> +	case GL_UNSIGNED_INT_VEC3: return  3;
> +	case GL_UNSIGNED_INT_VEC4: return  4;
> +	default:
> +		printf("Unexpected type: %u (%s)\n", type,
> +		       piglit_get_gl_enum_name(type));
> +		piglit_report_result(PIGLIT_FAIL);
> +		return 0;
> +	}
> +}
> +
> +
> +/**
> + * Determine whether the given type contains floating-point values.
> + */
> +static bool
> +is_floating_type(GLenum type)
> +{
> +	switch (type) {
> +	case GL_FLOAT:
> +	case GL_FLOAT_VEC2:
> +	case GL_FLOAT_VEC3:
> +	case GL_FLOAT_VEC4:
> +	case GL_FLOAT_MAT2:
> +	case GL_FLOAT_MAT2x3:
> +	case GL_FLOAT_MAT2x4:
> +	case GL_FLOAT_MAT3x2:
> +	case GL_FLOAT_MAT3:
> +	case GL_FLOAT_MAT3x4:
> +	case GL_FLOAT_MAT4x2:
> +	case GL_FLOAT_MAT4x3:
> +	case GL_FLOAT_MAT4:
> +		return true;
> +	case GL_INT:
> +	case GL_INT_VEC2:
> +	case GL_INT_VEC3:
> +	case GL_INT_VEC4:
> +	case GL_UNSIGNED_INT:
> +	case GL_UNSIGNED_INT_VEC2:
> +	case GL_UNSIGNED_INT_VEC3:
> +	case GL_UNSIGNED_INT_VEC4:
> +		return false;
> +	default:
> +		printf("Unexpected type: %u (%s)\n", type,
> +		       piglit_get_gl_enum_name(type));
> +		piglit_report_result(PIGLIT_FAIL);
> +		return false;
> +	}
> +}
> +
> +
> +/**
> + * Compute the expected number of transform feedback outputs for the
> + * current test.  This is used to size the transform feedback buffer.
> + */
> +static unsigned
> +count_outputs()
> +{
> +	unsigned num_good_varyings = count_strings(test->good_varyings);
> +	unsigned i;
> +	unsigned num_outputs = 0;
> +
> +	for (i = 0; i < num_good_varyings; i++) {
> +		num_outputs += size_of_type(test->expected_types[i])
> +			* test->expected_sizes[i];
> +	}
> +	return num_outputs;
> +}
> +
> +
> +/**
> + * Check that the buffer pointed to by \c readback contains the
> + * expected transform feedback output data.
> + */
> +static bool
> +check_outputs(const void *readback)
> +{
> +	const float *readback_f = readback;
> +	const int *readback_i = readback;
> +	unsigned num_good_varyings = count_strings(test->good_varyings);
> +	unsigned i;
> +	unsigned output_component = 0;
> +	unsigned float_index = 0;
> +	unsigned int_index = 0;
> +	bool pass = true;
> +
> +	for (i = 0; i < num_good_varyings; i++) {
> +		const char *varying_name = test->good_varyings[i];
> +		unsigned varying_size = test->expected_sizes[i]
> +			* size_of_type(test->expected_types[i]);
> +		if (is_floating_type(test->expected_types[i])) {
> +			unsigned j;
> +			for (j = 0; j < varying_size; j++) {
> +				float actual = readback_f[output_component];
> +				float expected
> +					= test->expected_floats[float_index];
> +				if (actual != expected) {
> +					printf("Output %s element %u: "
> +					       "expected %f, got %f\n",
> +					       varying_name, j, expected,
> +					       actual);
> +					pass = false;
> +				}
> +				output_component++;
> +				float_index++;
> +			}
> +		} else {
> +			unsigned j;
> +			for (j = 0; j < varying_size; j++) {
> +				int actual = readback_i[output_component];
> +				int expected = test->expected_ints[int_index];
> +				if (actual != expected) {
> +					printf("Output %s element %u: "
> +					       "expected %i, got %i\n",
> +					       varying_name, j, expected,
> +					       actual);
> +					pass = false;
> +				}
> +				output_component++;
> +				int_index++;
> +			}
> +		}
> +	}
> +
> +	return pass;
> +}
> +
> +
> +/**
> + * Call glDrawArrays, passing the given vertex data using a VAO and a
> + * VBO.
> + */
> +static void
> +draw_arrays(const void *verts, unsigned verts_size)
> +{
> +	GLuint vao, vbo;
> +	glGenVertexArrays(1, &vao);
> +	glBindVertexArray(vao);
> +	glGenBuffers(1, &vbo);
> +	glBindBuffer(GL_ARRAY_BUFFER, vbo);
> +	glBufferData(GL_ARRAY_BUFFER, verts_size, verts, GL_STREAM_DRAW);
> +
> +	glVertexAttribPointer(VERTEX_ATTRIB_POS, 4, GL_FLOAT, GL_FALSE, 0,
> +			      NULL);
> +	glEnableVertexAttribArray(VERTEX_ATTRIB_POS);
> +
> +	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
> +
> +	glDisableVertexAttribArray(VERTEX_ATTRIB_POS);
> +
> +	glBindBuffer(GL_ARRAY_BUFFER, 0);
> +	glDeleteBuffers(1, &vbo);
> +	glBindVertexArray(0);
> +	glDeleteVertexArrays(1, &vao);
> +}
> +
> +
> +/**
> + * Draw a rectangle using the given coordinates.
> + *
> + * In an ideal world, instead of using this function we would do the
> + * drawing using piglit_draw_rect(), however that function doesn't use
> + * VBO's or VAO's, and hence isn't compatible with core contexts.
> + */
> +static void
> +draw_rect(float x, float y, float w, float h)
> +{
> +	float verts[4][4];
> +
> +	verts[0][0] = x;
> +	verts[0][1] = y;
> +	verts[0][2] = 0.0;
> +	verts[0][3] = 1.0;
> +	verts[1][0] = x + w;
> +	verts[1][1] = y;
> +	verts[1][2] = 0.0;
> +	verts[1][3] = 1.0;
> +	verts[2][0] = x;
> +	verts[2][1] = y + h;
> +	verts[2][2] = 0.0;
> +	verts[2][3] = 1.0;
> +	verts[3][0] = x + w;
> +	verts[3][1] = y + h;
> +	verts[3][2] = 0.0;
> +	verts[3][3] = 1.0;
> +
> +	draw_arrays(verts, sizeof(verts));
> +}

We have a similar function for ES2.  We should make a 
piglit_draw_rect_core that works correct for core contexts (uses a VAO 
with a VBO with generic vertex inputs), and make that available everywhere.

> +
> +
> +/**
> + * Render using the program and verify that it outputs the proper data
> + * to the transform feedback buffer.
> + *
> + * The program should already be linked and stored in the global \c
> + * prog.
> + */
> +static enum piglit_result
> +test_xfb(bool use_rasterizer_discard)
> +{
> +	GLuint buf;
> +	void *initial_data;
> +	const void *readback;
> +	unsigned expected_num_outputs = count_outputs();
> +	unsigned buf_size
> +		= expected_num_outputs * NUM_VERTICES * sizeof(float);
> +	bool pass = true;
> +
> +	/* Create transform feedback buffer and pre-load it with
> +	 * garbage.
> +	 */
> +	glGenBuffers(1, &buf);
> +	initial_data = malloc(buf_size);
> +	memset(initial_data, 0xcc, buf_size);
> +	glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf);
> +	glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, buf_size, initial_data,
> +		     GL_STREAM_READ);
> +	free(initial_data);
> +
> +	/* Draw a quad filling the window, with transform feedback
> +	 * enabled.
> +	 */
> +	glUseProgram(prog);
> +	glBeginTransformFeedback(GL_TRIANGLES);
> +	if (use_rasterizer_discard)
> +		glEnable(GL_RASTERIZER_DISCARD);
> +	draw_rect(-1, -1, 2, 2);
> +	if (use_rasterizer_discard)
> +		glDisable(GL_RASTERIZER_DISCARD);
> +	glEndTransformFeedback();
> +	pass = piglit_check_gl_error(GL_NO_ERROR) && pass;
> +
> +	/* Inspect transform feedback output. */
> +	readback = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf_size,
> +				    GL_MAP_READ_BIT);
> +	pass = check_outputs(readback) && pass;
> +	glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
> +
> +	glDeleteBuffers(1, &buf);
> +
> +	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
> +}
> +
> +
> +static void
> +print_usage_and_exit(const char *prog_name)
> +{
> +	unsigned i;
> +	printf("Usage: %s <subtest> <mode> {options}\n"
> +	       "  where <subtest> is one of the following:\n", prog_name);
> +	for (i = 0; i < ARRAY_SIZE(tests); i++)
> +		printf("    %s\n", tests[i].name);
> +	printf("  <mode> is one of the following:\n"
> +	       "    error\n"
> +	       "    get\n"
> +	       "    run\n"
> +	       "    run-no-fs\n"
> +	       "  and possible options are:\n"
> +	       "    interface - use interface blocks\n");
> +	piglit_report_result(PIGLIT_FAIL);
> +}
> +
> +
> +void
> +piglit_init(int argc, char **argv)
> +{
> +	unsigned i;
> +
> +	/* Parse first param. */
> +	if (argc < 3)
> +		print_usage_and_exit(argv[0]);
> +	for (i = 0; i < ARRAY_SIZE(tests); i++) {
> +		if (strcmp(argv[1], tests[i].name) == 0) {
> +			test = &tests[i];
> +			break;
> +		}
> +	}
> +	if (test == NULL)
> +		print_usage_and_exit(argv[0]);
> +
> +	/* Parse options. */
> +	for (i = 3; i < argc; i++) {
> +		if (strcmp(argv[i], "interface") == 0)
> +			use_interface_blocks = true;
> +		else
> +			print_usage_and_exit(argv[0]);
> +	}
> +
> +	/* Parse second param and setup test */
> +	if (strcmp(argv[2], "error") == 0) {
> +		piglit_report_result(test_errors());
> +	} else if (strcmp(argv[2], "get") == 0) {
> +		link_shaders(true);
> +		piglit_report_result(test_gets());
> +	} else if (strcmp(argv[2], "run") == 0) {
> +		link_shaders(true);
> +		/* Testing will occur in piglit_display */
> +	} else if (strcmp(argv[2], "run-no-fs") == 0) {
> +		link_shaders(false);
> +		piglit_report_result(test_xfb(true));
> +	} else {
> +		print_usage_and_exit(argv[0]);
> +	}
> +}
> +
> +
> +enum piglit_result
> +piglit_display(void)
> +{
> +	static const float green[4] = {0.0, 1.0, 0.0, 1.0};
> +	enum piglit_result result;
> +
> +	glClear(GL_COLOR_BUFFER_BIT);
> +	result = test_xfb(false);
> +
> +	/* test_xfb() sends a set of vertices down the pipeline that
> +	 * should cause the entire window to be drawn, so all we need
> +	 * to do to make sure that the correct data got to the
> +	 * fragment shader is verify that it painted a green window.
> +	 */
> +	if (!piglit_probe_rect_rgba(0, 0, piglit_width, piglit_height, green))
> +		result = PIGLIT_FAIL;
> +
> +	piglit_present_results();
> +
> +	return result;
> +}
>



More information about the Piglit mailing list