/* * Copyright © 2015 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 resource-query.c * * Tests querying resources. * * From the GL_ARB_program_interface_query spec: * "The command * * void GetProgramInterfaceiv(uint program, enum programInterface, * enum pname, int *params); * * queries a property of the interface in program * , returning its value in . The property to return is * specified by . * * If is ACTIVE_RESOURCES, the value returned is the number of * resources in the active resource list for . If the * list of active resources for is empty, zero is * returned. * * If is MAX_NAME_LENGTH, the value returned is the length of the * longest active name string for an active resource in . * This length includes an extra character for the null terminator. If * the list of active resources for is empty, zero is * returned. The error INVALID_OPERATION is generated if * is ATOMIC_COUNTER_BUFFER, since active atomic counter * buffer resources are not assigned name strings. * * If is MAX_NUM_ACTIVE_VARIABLES, the value returned is the number * of active variables belonging to the interface block or atomic counter * buffer resource in with the most active variables. * If the list of active resources for is empty, zero is * returned. The error INVALID_OPERATION is generated if * is not UNIFORM_BLOCK, ATOMIC_COUNTER_BUFFER, or * SHADER_STORAGE_BLOCK. * * If is MAX_NUM_COMPATIBLE_SUBROUTINES, the value returned is the * number of compatible subroutines belonging to the active subroutine * uniform in with the most compatible subroutines. If * the list of active resources for is empty, zero is * returned. The error INVALID_OPERATION is generated unless * is VERTEX_SUBROUTINE_UNIFORM, * TESS_CONTROL_SUBROUTINE_UNIFORM, TESS_EVALUATION_SUBROUTINE_UNIFORM, * GEOMETRY_SUBROUTINE_UNIFORM, FRAGMENT_SUBROUTINE_UNIFORM, or * COMPUTE_SUBROUTINE_UNIFORM. * * The command * * uint GetProgramResourceIndex(uint program, enum programInterface, * const char *name); * * returns the unsigned integer index assigned to a resource named * in the interface type of program object . * The error INVALID_ENUM is generated if is * ATOMIC_COUNTER_BUFFER, since active atomic counter buffer resources are * not assigned name strings. * * If exactly matches the name string of one of the active resources * for , the index of the matched resource is returned. * Additionally, if would exactly match the name string of an active * resource if "[0]" were appended to , the index of the matched * resource is returned. Otherwise, is considered not to be the * name of an active resource, and INVALID_INDEX is returned. Note that if * an interface enumerates a single active resource list entry for an array * variable (e.g., "a[0]"), a identifying any array element other * than the first (e.g., "a[1]") is not considered to match. * * For the interface TRANSFORM_FEEDBACK_VARYING, the value INVALID_INDEX * should be returned when querying the index assigned to the special names * "gl_NextBuffer", "gl_SkipComponents1", "gl_SkipComponents2", * "gl_SkipComponents3", and "gl_SkipComponents4". * * The command * * void GetProgramResourceName(uint program, enum programInterface, * uint index, sizei bufSize, sizei *length, * char *name); * * returns the name string assigned to the single active resource with an * index of in the interface of program object * . The error INVALID_VALUE is generated if is greater * than or equal to the number of entries in the active resource list for * . The error INVALID_ENUM is generated if * is ATOMIC_COUNTER_BUFFER, since active atomic counter * buffer resources are not assigned name strings. * * The name string assigned to the active resource identified by is * returned as a null-terminated string in . The actual number of * characters written into , excluding the null terminator, is * returned in . If is NULL, no length is returned. The * maximum number of characters that may be written into , including * the null terminator, is specified by . If the length of the * name string (including the null terminator) is greater than , * the first -1 characters of the name string will be written to * , followed by a null terminator. If is zero, no error * will be generated but no characters will be written to . The * length of the longest name string for , including a * null terminator, can be queried by calling GetProgramInterfaceiv with a * of MAX_NAME_LENGTH. */ #include "piglit-util-gl.h" #include "common.h" PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_core_version = 32; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END /* Naming conventions, from the GL_ARB_program_interface_query extension: * * "When building a list of active variable or interface blocks, resources * with aggregate types (such as arrays or structures) may produce multiple * entries in the active resource list for the corresponding interface. * Additionally, each active variable, interface block, or subroutine in the * list is assigned an associated name string that can be used by * applications to refer to the resources. For interfaces involving * variables, interface blocks, or subroutines, the entries of active * resource lists are generated as follows: * * * For an active variable declared as a single instance of a basic type, * a single entry will be generated, using the variable name from the * shader source. * * * For an active variable declared as an array of basic types, a single * entry will be generated, with its name string formed by concatenating * the name of the array and the string "[0]". * * * For an active variable declared as a structure, a separate entry will * be generated for each active structure member. The name of each entry * is formed by concatenating the name of the structure, the "." * character, and the name of the structure member. If a structure * member to enumerate is itself a structure or array, these enumeration * rules are applied recursively. * * * For an active variable declared as an array of an aggregate data type * (structures or arrays), a separate entry will be generated for each * active array element, unless noted immediately below. The name of * each entry is formed by concatenating the name of the array, the "[" * character, an integer identifying the element number, and the "]" * character. These enumeration rules are applied recursively, treating * each enumerated array element as a separate active variable. * * * For an active shader storage block member declared as an array, an * entry will be generated only for the first array element, regardless * of its type. For arrays of aggregate types, the enumeration rules are * applied recursively for the single enumerated array element. * * * For an active interface block not declared as an array of block * instances, a single entry will be generated, using the block name from * the shader source. * * * For an active interface block declared as an array of instances, * separate entries will be generated for each active instance. The name * of the instance is formed by concatenating the block name, the "[" * character, an integer identifying the instance number, and the "]" * character. * * * For an active subroutine, a single entry will be generated, using the * subroutine name from the shader source. * * When an integer array element or block instance number is part of the name * string, it will be specified in decimal form without a "+" or "-" sign or * any extra leading zeroes. Additionally, the name string will not include * white space anywhere in the string. */ static const char *st_r_uniform[] = {"vs_test", "gs_test", "fs_color", "fs_array[0]", "fs_array_uniform_block.fs_color", "fs_array_uniform_block.fs_array[0]", "sa[0].a[0]", "sa[1].a[0]", NULL}; static const char *st_r_tess_uniform[] = {"tcs_test", "tes_test", NULL}; static const char *st_r_cs_uniform[] = {"cs_test", "tex", NULL}; static const char *st_r_uniform_block[] = {"vs_uniform_block", "gs_uniform_block", "fs_uniform_block", "fs_array_uniform_block[0]", "fs_array_uniform_block[1]", "fs_array_uniform_block[2]", "fs_array_uniform_block[3]", NULL}; static const char *st_r_tess_uniform_block[] = {"tcs_uniform_block", "tes_uniform_block", NULL}; static const char *st_r_cs_uniform_block[] = {"cs_uniform_block", NULL}; static const char *st_r_in_vs[] = {"vs_input0", "vs_input1", NULL}; static const char *st_r_in_gs[] = {"vs_output1[0]", "gl_PerVertex.gl_Position", NULL}; static const char *st_r_in_fs[] = {"fs_input1", NULL}; static const char *st_r_in_tes[] = {"tes_input1[0]", "gl_PerVertex.gl_Position", NULL}; static const char *st_r_in_tcs[] = {"vs_output1[0]", "gl_InvocationID", "gl_PerVertex.gl_Position", NULL}; static const char *st_r_out_vs[] = {"vs_output1", "gl_Position", NULL}; static const char *st_r_out_gs[] = {"fs_input1", "gl_Position", NULL}; static const char *st_r_out_fs[] = {"fs_output0", "fs_output1", NULL}; static const char *st_r_out_tes[] = {"vs_output1", "gl_Position", NULL}; static const char *st_r_out_tcs[] = {"tes_input1[0]", "tcs_patch", "gl_PerVertex.gl_Position", NULL}; static const char *st_r_buffer[] = {"vs_buf_var", "gs_buf_var", "fs_buf_var", NULL}; static const char *st_r_stor_block[] = {"vs_buffer_block", "gs_buffer_block", "fs_buffer_block", NULL}; static const char *st_r_tf_varying[] = {"gl_Position", "fs_input1", NULL}; static const char *st_r_vs_sub[] = {"vss", "vss2", NULL}; static const char *st_r_gs_sub[] = {"gss", NULL}; static const char *st_r_fs_sub[] = {"fss", NULL}; static const char *st_r_cs_sub[] = {"css", NULL}; static const char *st_r_tcs_sub[] = {"tcss", NULL}; static const char *st_r_tes_sub[] = {"tess", NULL}; static const char *st_r_vs_sub_uni[] = {"VERTEX", NULL}; static const char *st_r_gs_sub_uni[] = {"GEOMETRY", NULL}; static const char *st_r_fs_sub_uni[] = {"FRAGMENT", NULL}; static const char *st_r_cs_sub_uni[] = {"COMPUTE", NULL}; static const char *st_r_tcs_sub_uni[] = {"TESS_CONTROL", NULL}; static const char *st_r_tes_sub_uni[] = {"TESS_EVALUATION", NULL}; /* From the GL_ARB_program_interface_query extension: * * "The GL provides a number of commands to query properties of the interfaces * of a program object. Each such command accepts a * token, identifying a specific interface. The supported values for * are as follows: * * UNIFORM corresponds to the set of active uniform variables (section * 2.14.7) used by . * * * UNIFORM_BLOCK corresponds to the set of active uniform blocks (section * 2.14.7) used by . * * * ATOMIC_COUNTER_BUFFER corresponds to the set of active atomic counter * buffer binding points (section 2.14.7) used by . * * PROGRAM_INPUT corresponds to the set of active input variables used by * the first shader stage of . If includes multiple * shader stages, input variables from any shader stage other than the * first will not be enumerated. * * * PROGRAM_OUTPUT corresponds to the set of active output variables * (section 2.14.11) used by the last shader stage of . If * includes multiple shader stages, output variables from any * shader stage other than the last will not be enumerated. * * * VERTEX_SUBROUTINE, TESS_CONTROL_SUBROUTINE, * TESS_EVALUATION_SUBROUTINE, GEOMETRY_SUBROUTINE, FRAGMENT_SUBROUTINE, * and COMPUTE_SUBROUTINE correspond to the set of active subroutines for * the vertex, tessellation control, tessellation evaluation, geometry, * fragment, and compute shader stages of , respectively * (section 2.14.8). * * * VERTEX_SUBROUTINE_UNIFORM, TESS_CONTROL_SUBROUTINE_UNIFORM, * TESS_EVALUATION_SUBROUTINE_UNIFORM, GEOMETRY_SUBROUTINE_UNIFORM, * FRAGMENT_SUBROUTINE_UNIFORM, and COMPUTE_SUBROUTINE_UNIFORM correspond * to the set of active subroutine uniform variables used by the vertex, * tessellation control, tessellation evaluation, geometry, fragment, and * compute shader stages of , respectively (section 2.14.8). * * * TRANSFORM_FEEDBACK_VARYING corresponds to the set of output variables * in the last non-fragment stage of that would be captured * when transform feedback is active (section 2.20.2). * * * BUFFER_VARIABLE corresponds to the set of active buffer variables (see * the ARB_shader_storage_buffer_object extension) used by . * * * SHADER_STORAGE_BLOCK corresponds to the set of active shader storage * blocks (see the ARB_shader_storage_buffer_object extension) used by * ." * * Additionally, from the GL_ARB_program_interface_query extension: * * "For the ATOMIC_COUNTER_BUFFER interface, the list of active buffer binding * points is built by identifying each unique binding point associated with * one or more active atomic counter uniform variables. Active atomic * counter buffers do not have an associated name string. * * For the UNIFORM, PROGRAM_INPUT, PROGRAM_OUTPUT, and * TRANSFORM_FEEDBACK_VARYING interfaces, the active resource list will * include all active variables for the interface, including any active * built-in variables. * * For PROGRAM_INPUT and PROGRAM_OUTPUT interfaces for shaders that recieve * or produce patch primitves, the active resource list will include both * per-vertex and per-patch inputs and outputs. * * For the TRANSFORM_FEEDBACK_VARYING interface, the active resource list * will entries for the special varying names gl_NextBuffer, * gl_SkipComponents1, gl_SkipComponents2, gl_SkipComponents3, and * gl_SkipComponents4 (section 2.14.11). These variables are used to control * how varying values are written to transform feedback buffers. When * enumerating the properties of such resources, these variables are * considered to have a TYPE of NONE and an ARRAY_SIZE of 0 (gl_NextBuffer), * 1, 2, 3, and 4, respectively." */ struct subtest_t { GLenum programInterface; const char *programInterface_str; const char *active_resources_str; const char *max_length_name_str; const char *max_num_active_str; const char *max_num_compat_sub_str; /* set to -1 to disable the test */ int active_resources; int max_length_name; int max_num_active; int max_num_compat_sub; const char *vs_text; const char *gs_text; const char *fs_text; const char *tcs_text; const char *tes_text; const char *cs_text; const char **resources; }; #define ST(active_r, max_len, max_num_active, max_num_compat_sub, vs, tcs, \ tes, gs, fs, cs, name, suffix, resources) { \ (name), #name suffix, #name suffix " active resources", \ #name suffix " max length name", \ #name suffix " max num active", \ #name suffix " max num compat sub", \ (active_r), (max_len), (max_num_active), (max_num_compat_sub), \ (vs), (gs), (fs), (tcs), (tes), (cs), (resources) \ } static const struct subtest_t subtests[] = { ST( 8, 35, -1, -1, vs_std, NULL, NULL, gs_std, fs_in, NULL, GL_UNIFORM, "(vs,gs,fs)", st_r_uniform), ST( 2, 9, -1, -1, NULL, tcs_sub, tes_in, NULL, NULL, NULL, GL_UNIFORM, "(tes,tcs)", st_r_tess_uniform), ST( 2, 8, -1, -1, NULL, NULL, NULL, NULL, NULL, cs_sub, GL_UNIFORM, "(cs)", st_r_cs_uniform), ST( 7, 26, 2, -1, vs_std, NULL, NULL, gs_std, fs_in, NULL, GL_UNIFORM_BLOCK, "(vs,gs,fs)", st_r_uniform_block), ST( 2, 18, -1, -1, NULL, tcs_sub, tes_in, NULL, NULL, NULL, GL_UNIFORM_BLOCK, "(tcs,tes)", st_r_tess_uniform_block), ST( 1, 17, -1, -1, NULL, NULL, NULL, NULL, NULL, cs_sub, GL_UNIFORM_BLOCK, "(cs)", st_r_cs_uniform_block), ST( 2, 10, -1, -1, vs_std, NULL, NULL, NULL, NULL, NULL, GL_PROGRAM_INPUT, "(vs)", st_r_in_vs), ST( 2, 25, -1, -1, NULL, NULL, NULL, gs_std, NULL, NULL, GL_PROGRAM_INPUT, "(gs)", st_r_in_gs), ST( 1, 10, -1, -1, NULL, NULL, NULL, NULL, fs_in, NULL, GL_PROGRAM_INPUT, "(fs)", st_r_in_fs), ST( 2, 10, -1, -1, vs_std, NULL, NULL, NULL, fs_std, NULL, GL_PROGRAM_INPUT, "(vs,fs)", st_r_in_vs), ST( 2, 10, -1, -1, vs_std, NULL, NULL, gs_std, NULL, NULL, GL_PROGRAM_INPUT, "(vs,gs)", st_r_in_vs), ST( 2, 25, -1, -1, NULL, NULL, NULL, gs_std, fs_in, NULL, GL_PROGRAM_INPUT, "(gs,fs)", st_r_in_gs), ST( 2, 10, -1, -1, vs_std, NULL, NULL, gs_std, fs_in, NULL, GL_PROGRAM_INPUT, "(vs,gs,fs)", st_r_in_vs), ST( 2, 25, -1, -1, NULL, NULL, tes_in, NULL, NULL, NULL, GL_PROGRAM_INPUT, "(tes)", st_r_in_tes), ST( 3, 25, -1, -1, NULL, tcs_sub, NULL, NULL, NULL, NULL, GL_PROGRAM_INPUT, "(tcs)", st_r_in_tcs), ST( 3, 25, -1, -1, NULL, tcs_sub, tes_in, NULL, NULL, NULL, GL_PROGRAM_INPUT, "(tcs,tes)", st_r_in_tcs), ST( 2, 10, -1, -1, vs_std, tcs_sub, tes_in, NULL, NULL, NULL, GL_PROGRAM_INPUT, "(vs,tcs,tes)", st_r_in_vs), ST( 0, 0, -1, -1, NULL, NULL, NULL, NULL, NULL, cs_sub, GL_PROGRAM_INPUT, "(cs)", NULL), ST( 2, 12, -1, -1, vs_std, NULL, NULL, NULL, NULL, NULL, GL_PROGRAM_OUTPUT, "(vs)", st_r_out_vs), ST( 2, 12, -1, -1, NULL, NULL, NULL, gs_std, NULL, NULL, GL_PROGRAM_OUTPUT, "(gs)", st_r_out_gs), ST( 2, 11, -1, -1, NULL, NULL, NULL, NULL, fs_std, NULL, GL_PROGRAM_OUTPUT, "(fs)", st_r_out_fs), ST( 2, 11, -1, -1, vs_std, NULL, NULL, NULL, fs_std, NULL, GL_PROGRAM_OUTPUT, "(vs,fs)", st_r_out_fs), ST( 2, 12, -1, -1, vs_std, NULL, NULL, gs_std, NULL, NULL, GL_PROGRAM_OUTPUT, "(vs,gs)", st_r_out_gs), ST( 2, 11, -1, -1, NULL, NULL, NULL, gs_std, fs_in, NULL, GL_PROGRAM_OUTPUT, "(gs,fs)", st_r_out_fs), ST( 2, 11, -1, -1, vs_std, NULL, NULL, gs_std, fs_in, NULL, GL_PROGRAM_OUTPUT, "(vs,gs,fs)", st_r_out_fs), ST( 2, 12, -1, -1, NULL, NULL, tes_in, NULL, NULL, NULL, GL_PROGRAM_OUTPUT, "(tes)", st_r_out_tes), ST( 3, 25, -1, -1, NULL, tcs_sub, NULL, NULL, NULL, NULL, GL_PROGRAM_OUTPUT, "(tcs)", st_r_out_tcs), ST( 2, 12, -1, -1, NULL, tcs_sub, tes_in, NULL, NULL, NULL, GL_PROGRAM_OUTPUT, "(tcs,tes)", st_r_out_tes), ST( 2, 12, -1, -1, NULL, tcs_sub, tes_in, gs_std, NULL, NULL, GL_PROGRAM_OUTPUT, "(tcs,tes,gs)", st_r_out_gs), ST( 0, 0, -1, -1, NULL, NULL, NULL, NULL, NULL, cs_sub, GL_PROGRAM_OUTPUT, "(cs)", st_r_cs_sub), ST( 3, 11, -1, -1, vs_stor, NULL, NULL, gs_stor, fs_stor, NULL, GL_BUFFER_VARIABLE, "", st_r_buffer), ST( 3, 16, 1, -1, vs_stor, NULL, NULL, gs_stor, fs_stor, NULL, GL_SHADER_STORAGE_BLOCK, "", st_r_stor_block), ST( 3, -1, 1, -1, vs_atom, NULL, NULL, gs_atom, fs_atom, NULL, GL_ATOMIC_COUNTER_BUFFER, "", NULL), ST( 2, 12, -1, -1, vs_std, NULL, NULL, gs_std, NULL, NULL, GL_TRANSFORM_FEEDBACK_VARYING, "", st_r_tf_varying), ST( 2, 5, -1, -1, vs_sub, NULL, NULL, NULL, NULL, NULL, GL_VERTEX_SUBROUTINE, "", st_r_vs_sub), ST( 1, 4, -1, -1, vs_sub, NULL, NULL, gs_sub, NULL, NULL, GL_GEOMETRY_SUBROUTINE, "", st_r_gs_sub), ST( 1, 4, -1, -1, vs_sub, NULL, NULL, gs_sub, fs_sub, NULL, GL_FRAGMENT_SUBROUTINE, "", st_r_fs_sub), ST( 1, 4, -1, -1, NULL, NULL, NULL, NULL, NULL, cs_sub, GL_COMPUTE_SUBROUTINE, "", st_r_cs_sub), ST( 1, 5, -1, -1, vs_sub, tcs_sub, NULL, NULL, NULL, NULL, GL_TESS_CONTROL_SUBROUTINE, "", st_r_tcs_sub), ST( 1, 5, -1, -1, vs_sub, NULL, tes_sub, NULL, NULL, NULL, GL_TESS_EVALUATION_SUBROUTINE, "", st_r_tes_sub), ST( 1, 7, -1, 2, vs_sub, NULL, NULL, NULL, NULL, NULL, GL_VERTEX_SUBROUTINE_UNIFORM, "", st_r_vs_sub_uni), ST( 1, 9, -1, 1, vs_sub, NULL, NULL, gs_sub, NULL, NULL, GL_GEOMETRY_SUBROUTINE_UNIFORM, "", st_r_gs_sub_uni), ST( 1, 9, -1, 1, vs_sub, NULL, NULL, gs_sub, fs_sub, NULL, GL_FRAGMENT_SUBROUTINE_UNIFORM, "", st_r_fs_sub_uni), ST( 1, 13, -1, 1, vs_sub, tcs_sub, NULL, NULL, NULL, NULL, GL_TESS_CONTROL_SUBROUTINE_UNIFORM, "", st_r_tcs_sub_uni), ST( 1, 16, -1, 1, vs_sub, NULL, tes_sub, NULL, NULL, NULL, GL_TESS_EVALUATION_SUBROUTINE_UNIFORM, "", st_r_tes_sub_uni), ST( 1, 8, -1, 1, NULL, NULL, NULL, NULL, NULL, cs_sub, GL_COMPUTE_SUBROUTINE_UNIFORM, "", st_r_cs_sub_uni), }; static void check_pname(GLuint prog, GLenum programInterface, GLenum pname, bool *pass, const char *subtest, int expected_value) { int value; if (expected_value < 0) { return; } glGetProgramInterfaceiv(prog, programInterface, pname, &value); if (!piglit_check_gl_error(GL_NO_ERROR)) { printf(" Latest error generated while running '%s'\n", subtest); } if (value != expected_value) { fprintf(stderr, "'%s' expected %i but got %i\n", subtest, expected_value, value); *pass = false; } } static bool is_resource_in_list(const char **list, const char *resource, int index, bool check_order) { int i = 0; while (list && list[i]) { if (strcmp(list[i], resource) == 0) { return !check_order || index == i; } i++; } return false; } static bool consistency_check(GLuint prog, GLenum programInterface, const char *name, GLint index) { bool subroutine = false; const GLchar *names[] = { name }; GLuint old_idx = 0xdeadcafe; GLenum shader; /* Validate result against old API. */ switch (programInterface) { case GL_UNIFORM: glGetUniformIndices(prog, 1, names, &old_idx); piglit_check_gl_error(GL_NO_ERROR); break; case GL_UNIFORM_BLOCK: old_idx = glGetUniformBlockIndex(prog, name); piglit_check_gl_error(GL_NO_ERROR); break; case GL_VERTEX_SUBROUTINE: shader = GL_VERTEX_SHADER; subroutine = true; break; case GL_TESS_CONTROL_SUBROUTINE: shader = GL_TESS_CONTROL_SHADER; subroutine = true; break; case GL_TESS_EVALUATION_SUBROUTINE: shader = GL_TESS_EVALUATION_SHADER; subroutine = true; break; case GL_GEOMETRY_SUBROUTINE: shader = GL_GEOMETRY_SHADER; subroutine = true; break; case GL_FRAGMENT_SUBROUTINE: shader = GL_FRAGMENT_SHADER; subroutine = true; break; case GL_COMPUTE_SUBROUTINE: shader = GL_COMPUTE_SHADER; subroutine = true; break; default: /* There are no old APIs for this program interface */ return true; } if (subroutine) { old_idx = glGetSubroutineIndex(prog, shader, name); piglit_check_gl_error(GL_NO_ERROR); } if (index != old_idx) { printf("Index inconsistent with the old API: %i vs %i\n", index, old_idx); return false; } else return true; } static void validate_resources(const struct subtest_t st, GLuint prog, bool *pass) { GLsizei max_size = 0, size, i; char * name; /* Do not run the test for GL_ATOMIC_COUNTER_BUFFER. * From the GL_ARB_program_interface_query extension: * * "The error INVALID_OPERATION is generated if * is ATOMIC_COUNTER_BUFFER, since active atomic counter buffer * resources are not assigned name strings." */ if (st.programInterface == GL_ATOMIC_COUNTER_BUFFER) return; name = (char *) malloc(st.max_length_name); for (i = 0; i < st.active_resources; i++) { GLuint index; glGetProgramResourceName(prog, st.programInterface, i, st.max_length_name, &size, name); piglit_check_gl_error(GL_NO_ERROR); /* keep track of the maximum size */ if (size > max_size) { max_size = size; } /* Check the names. Transform feedback requires the order to be * the same as the one given in glTransformFeedbackVaryings. * From the GL_ARB_program_interface_query extension: * * "The order of the active resource list is * implementation-dependent for all interfaces except for * TRANSFORM_FEEDBACK_VARYING. For TRANSFORM_FEEDBACK_VARYING, * the active resource list will use the variable order * specified in the the most recent call to * TransformFeedbackVaryings before the last call to * LinkProgram. */ if (st.resources && !is_resource_in_list(st.resources, name, i, st.programInterface == GL_TRANSFORM_FEEDBACK_VARYING)) { fprintf(stderr, "Resource '%s' not found in '%s' " "resource list or found at the wrong " "index\n", name, st.programInterface_str); *pass = false; } /* Check the position of the arguments and see if it matches * with the current position we are in. */ index = glGetProgramResourceIndex(prog, st.programInterface, name); if (index != i) { fprintf(stderr, "%s: Resource '%s' is not at the " "position reported by " "glGetProgramResourceIndex (%i instead " "of %i)\n", st.programInterface_str, name, index, i); *pass = false; } /* check the equivalence with the old API */ if (!consistency_check(prog, st.programInterface, name, index)) { *pass = false; } } free(name); /* glGetProgramResourceName does not count the NULL terminator as part * of the size contrarily to glGetProgramInterfaceiv. * From the GL_ARB_program_interface_query extension: * * "void GetProgramInterfaceiv(uint program, enum programInterface, * enum pname, int *params); * [...] * If is MAX_NAME_LENGTH, the value returned is the length of * the longest active name string for an active resource in * . This length includes an extra character for the * null terminator." * * "void GetProgramResourceName(uint program, enum programInterface, * uint index, sizei bufSize, * sizei *length, char *name); * [...] * The actual number of characters written into , excluding the * null terminator, is returned in ." */ if (max_size != MAX2(0, st.max_length_name - 1)) { fprintf(stderr, "'%s actual max length' expected %i but got " "%i\n", st.programInterface_str, st.max_length_name - 1, max_size); *pass = false; } } static bool check_extensions(const struct subtest_t st) { if (st.programInterface == GL_ATOMIC_COUNTER_BUFFER && !piglit_is_extension_supported("GL_ARB_shader_atomic_counters")) { return false; } if ((st.programInterface == GL_BUFFER_VARIABLE || st.programInterface == GL_SHADER_STORAGE_BLOCK) && !piglit_is_extension_supported("GL_ARB_shader_storage_buffer_object")) { return false; } if ((st.programInterface == GL_VERTEX_SUBROUTINE || st.programInterface == GL_GEOMETRY_SUBROUTINE || st.programInterface == GL_FRAGMENT_SUBROUTINE || st.programInterface == GL_COMPUTE_SUBROUTINE || st.programInterface == GL_VERTEX_SUBROUTINE_UNIFORM || st.programInterface == GL_GEOMETRY_SUBROUTINE_UNIFORM || st.programInterface == GL_FRAGMENT_SUBROUTINE_UNIFORM || st.programInterface == GL_COMPUTE_SUBROUTINE_UNIFORM || st.programInterface == GL_TESS_CONTROL_SUBROUTINE || st.programInterface == GL_TESS_EVALUATION_SUBROUTINE || st.programInterface == GL_TESS_CONTROL_SUBROUTINE_UNIFORM || st.programInterface == GL_TESS_EVALUATION_SUBROUTINE_UNIFORM || st.programInterface == GL_COMPUTE_SUBROUTINE_UNIFORM) && !piglit_is_extension_supported("GL_ARB_shader_subroutine")) { return false; } if ((st.programInterface == GL_TESS_CONTROL_SUBROUTINE || st.programInterface == GL_TESS_EVALUATION_SUBROUTINE || st.programInterface == GL_TESS_CONTROL_SUBROUTINE_UNIFORM || st.programInterface == GL_TESS_EVALUATION_SUBROUTINE_UNIFORM || st.tcs_text || st.tes_text) && (!piglit_is_extension_supported("GL_ARB_shader_subroutine") || !piglit_is_extension_supported("GL_ARB_tessellation_shader"))) { return false; } if ((st.programInterface == GL_COMPUTE_SUBROUTINE || st.programInterface == GL_COMPUTE_SUBROUTINE_UNIFORM || st.cs_text) && (!piglit_is_extension_supported("GL_ARB_compute_shader") || !piglit_is_extension_supported("GL_ARB_shader_image_load_store"))) { return false; } return true; } static void run_subtest(const struct subtest_t st, bool *pass) { enum piglit_result result; bool local_pass = true; GLuint prog; if (!check_extensions(st)) { result = PIGLIT_SKIP; goto report_result; } prog = piglit_build_simple_program_unlinked_multiple_shaders( GL_VERTEX_SHADER, st.vs_text, GL_GEOMETRY_SHADER, st.gs_text, GL_FRAGMENT_SHADER, st.fs_text, GL_TESS_CONTROL_SHADER, st.tcs_text, GL_TESS_EVALUATION_SHADER, st.tes_text, GL_COMPUTE_SHADER, st.cs_text, 0); if (st.programInterface == GL_TRANSFORM_FEEDBACK_VARYING) { glTransformFeedbackVaryings(prog, 2, st_r_tf_varying, GL_INTERLEAVED_ATTRIBS); piglit_check_gl_error(GL_NO_ERROR); } /* force the compiler not to optimise away inputs/outputs */ glProgramParameteri(prog, GL_PROGRAM_SEPARABLE, GL_TRUE); piglit_check_gl_error(GL_NO_ERROR); glLinkProgram(prog); if (!piglit_link_check_status(prog)) { glDeleteProgram(prog); result = PIGLIT_FAIL; *pass = false; goto report_result; } check_pname(prog, st.programInterface, GL_ACTIVE_RESOURCES, &local_pass, st.active_resources_str, st.active_resources); check_pname(prog, st.programInterface, GL_MAX_NAME_LENGTH, &local_pass, st.max_length_name_str, st.max_length_name); /* do not test fetching the names if the previous tests failed */ if (local_pass) { validate_resources(st, prog, &local_pass); } check_pname(prog, st.programInterface, GL_MAX_NUM_ACTIVE_VARIABLES, &local_pass, st.max_num_active_str, st.max_num_active); check_pname(prog, st.programInterface, GL_MAX_NUM_COMPATIBLE_SUBROUTINES, &local_pass, st.max_num_compat_sub_str, st.max_num_compat_sub); glDeleteProgram(prog); *pass = *pass && local_pass; result = local_pass ? PIGLIT_PASS : PIGLIT_FAIL; report_result: piglit_report_subtest_result(result, "%s", st.programInterface_str); } void piglit_init(int argc, char **argv) { piglit_require_extension("GL_ARB_program_interface_query"); piglit_require_extension("GL_ARB_separate_shader_objects"); } enum piglit_result piglit_display(void) { bool pass = true; int i; /* run all the getprograminterfaceiv tests */ for (i = 0; i < sizeof(subtests) / sizeof(struct subtest_t); i++) { run_subtest(subtests[i], &pass); } return pass ? PIGLIT_PASS : PIGLIT_FAIL; }