[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