/* * 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.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_RGBA; config.khr_no_error_support = PIGLIT_NO_ERRORS; 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" "precision highp float;\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 */ { 0 }, /* 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 */ { 0 }, /* 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 } /** * Report the result, and if it's a failure, describe the shaders used * in the test. */ static void report_result(enum piglit_result result) { if (result == PIGLIT_FAIL) { printf("Vertex shader:\n%s%s\n", choose_header(), test->vs); printf("Fragment shader:\n%s%s\n", choose_header(), test->fs); } piglit_report_result(result); } /** * 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(); char *concatenated_text = NULL; GLuint shader; (void)!asprintf(&concatenated_text, "%s%s", header, shader_text); 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 num_varyings = count_strings(varyings); char **result = calloc(num_varyings + 1, sizeof(char *)); unsigned i; for (i = 0; i < num_varyings; i++) (void)!asprintf(&result[i], "%s%s", prefix, varyings[i]); 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); report_result(PIGLIT_FAIL); } glBindAttribLocation(prog, VERTEX_ATTRIB_POS, "pos"); glLinkProgram(prog); if (!piglit_link_check_status(prog)) { glDeleteProgram(prog); 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)); } /** * 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 {options}\n" " where is one of the following:\n", prog_name); for (i = 0; i < ARRAY_SIZE(tests); i++) printf(" %s\n", tests[i].name); printf(" 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) { report_result(test_errors()); } else if (strcmp(argv[2], "get") == 0) { link_shaders(true); 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); 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(); report_result(result); return result; }