/* * Copyright © 2012 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 simple.c * * This file checks that simple cases of varying packing work * correctly. Specifically, it tests that for each basic type allowed * in varyings, it is possible to create a shader with the maximum * possible number of that type of varying (determined by the * implementation's reported value of GL_MAX_VARYING_FLOATS). If the * size of the basic type being tested does not evenly divide * GL_MAX_VARYING_FLOATS, the remaining varyings are taken up by * individual floats. * * The test may be run in two modes: "array" mode, in which the test * uses a single varying whose type is an array (e.g. mat3[7]), and * "separate" mode, in which the test uses separate individual * varyings of the given type. * * The test operates by first querying the implementation's value of * GL_MAX_VARYING_FLOATS, then creating a vertex and fragment shader * that use up all possible varying components. The vertex shader * fills the varying components with consecutive integer values (where * the starting value is determined by a uniform), and the fragment * shader checks that all of the varying components were received * correctly. The shaders are compiled and run, to ensure that the * implementation not only claims to be able to pack the varyings, but * actually packs them correctly too. * * For example, on an implementation where GL_MAX_VARYING_FLOATS is * 64, when testing the mat3 type in "array" mode, the vertex shader * looks like this: * * #version 110 * uniform int i; * varying mat3 var0[7]; * varying float var1; * * void main() * { * gl_Position = gl_Vertex; * var0[0][0][0] = float(i + 0); * var0[0][0][1] = float(i + 1); * var0[0][0][2] = float(i + 2); * var0[0][1][0] = float(i + 3); * ... * var0[6][2][1] = float(i + 61); * var0[6][2][2] = float(i + 62); * var1 = float(i + 63); * } * * And the fragment shader looks like this: * * #version 110 * uniform int i; * varying mat3 var0[7]; * varying float var1; * * void main() * { * bool failed = false; * if (var0[0][0][0] != float(i + 0)) * failed = true; * if (var0[0][0][1] != float(i + 1)) * failed = true; * ... * if (var0[6][2][2] != float(i + 62)) * failed = true; * if (var1 != float(i + 63)) * failed = true; * if (failed) * gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); * else * gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); * } */ #include "piglit-util-gl.h" static void parse_args(int argc, char *argv[], struct piglit_gl_test_config *config); static const int outer_dim_size = 2; PIGLIT_GL_TEST_CONFIG_BEGIN config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE; config.khr_no_error_support = PIGLIT_NO_ERRORS; parse_args(argc, argv, &config); PIGLIT_GL_TEST_CONFIG_END static GLuint prog; static GLint i_location; enum base_type { BASE_TYPE_FLOAT, BASE_TYPE_UINT, BASE_TYPE_INT, BASE_TYPE_DOUBLE, }; enum test_array_type { SEPARATE, ARRAY, ARRAYS_OF_ARRAYS, }; static const char * get_base_type_name(enum base_type t) { switch (t) { case BASE_TYPE_FLOAT: return "float"; case BASE_TYPE_UINT: return "uint"; case BASE_TYPE_INT: return "int"; case BASE_TYPE_DOUBLE:return "double"; default: return "???"; } } struct type_desc { const char *name; enum base_type base; unsigned num_cols; unsigned num_rows; unsigned glsl_version_required; }; struct type_desc int_type = { "int", BASE_TYPE_INT, 1, 1, 130 }; struct type_desc uint_type = { "uint", BASE_TYPE_UINT, 1, 1, 130 }; struct type_desc float_type = { "float", BASE_TYPE_FLOAT, 1, 1, 110 }; struct type_desc double_type = { "double", BASE_TYPE_DOUBLE, 1, 1, 150 }; struct type_desc vec2_type = { "vec2", BASE_TYPE_FLOAT, 1, 2, 110 }; struct type_desc vec3_type = { "vec3", BASE_TYPE_FLOAT, 1, 3, 110 }; struct type_desc vec4_type = { "vec4", BASE_TYPE_FLOAT, 1, 4, 110 }; struct type_desc ivec2_type = { "ivec2", BASE_TYPE_INT, 1, 2, 130 }; struct type_desc ivec3_type = { "ivec3", BASE_TYPE_INT, 1, 3, 130 }; struct type_desc ivec4_type = { "ivec4", BASE_TYPE_INT, 1, 4, 130 }; struct type_desc uvec2_type = { "uvec2", BASE_TYPE_UINT, 1, 2, 130 }; struct type_desc uvec3_type = { "uvec3", BASE_TYPE_UINT, 1, 3, 130 }; struct type_desc uvec4_type = { "uvec4", BASE_TYPE_UINT, 1, 4, 130 }; struct type_desc dvec2_type = { "dvec2", BASE_TYPE_DOUBLE, 1, 2, 150 }; struct type_desc dvec3_type = { "dvec3", BASE_TYPE_DOUBLE, 1, 3, 150 }; struct type_desc dvec4_type = { "dvec4", BASE_TYPE_DOUBLE, 1, 4, 150 }; struct type_desc mat2_type = { "mat2", BASE_TYPE_FLOAT, 2, 2, 110 }; struct type_desc mat3_type = { "mat3", BASE_TYPE_FLOAT, 3, 3, 110 }; struct type_desc mat4_type = { "mat4", BASE_TYPE_FLOAT, 4, 4, 110 }; struct type_desc mat2x3_type = { "mat2x3", BASE_TYPE_FLOAT, 2, 3, 120 }; struct type_desc mat2x4_type = { "mat2x4", BASE_TYPE_FLOAT, 2, 4, 120 }; struct type_desc mat3x2_type = { "mat3x2", BASE_TYPE_FLOAT, 3, 2, 120 }; struct type_desc mat3x4_type = { "mat3x4", BASE_TYPE_FLOAT, 3, 4, 120 }; struct type_desc mat4x2_type = { "mat4x2", BASE_TYPE_FLOAT, 4, 2, 120 }; struct type_desc mat4x3_type = { "mat4x3", BASE_TYPE_FLOAT, 4, 3, 120 }; struct type_desc dmat2_type = { "dmat2", BASE_TYPE_DOUBLE, 2, 2, 150 }; struct type_desc dmat3_type = { "dmat3", BASE_TYPE_DOUBLE, 3, 3, 150 }; struct type_desc dmat4_type = { "dmat4", BASE_TYPE_DOUBLE, 4, 4, 150 }; struct type_desc dmat2x3_type = { "dmat2x3", BASE_TYPE_DOUBLE, 2, 3, 150 }; struct type_desc dmat2x4_type = { "dmat2x4", BASE_TYPE_DOUBLE, 2, 4, 150 }; struct type_desc dmat3x2_type = { "dmat3x2", BASE_TYPE_DOUBLE, 3, 2, 150 }; struct type_desc dmat3x4_type = { "dmat3x4", BASE_TYPE_DOUBLE, 3, 4, 150 }; struct type_desc dmat4x2_type = { "dmat4x2", BASE_TYPE_DOUBLE, 4, 2, 150 }; struct type_desc dmat4x3_type = { "dmat4x3", BASE_TYPE_DOUBLE, 4, 3, 150 }; const struct type_desc *all_types[] = { &int_type, &uint_type, &float_type, &double_type, &vec2_type, &vec3_type, &vec4_type, &ivec2_type, &ivec3_type, &ivec4_type, &uvec2_type, &uvec3_type, &uvec4_type, &dvec2_type, &dvec3_type, &dvec4_type, &mat2_type, &mat3_type, &mat4_type, &mat2x3_type, &mat2x4_type, &mat3x2_type, &mat3x4_type, &mat4x2_type, &mat4x3_type, &dmat2_type, &dmat3_type, &dmat4_type, &dmat2x3_type, &dmat2x4_type, &dmat3x2_type, &dmat3x4_type, &dmat4x2_type, &dmat4x3_type, NULL, }; /** * Type used to communicate to get_shader() the set of varyings to * test. */ struct varying_desc { const struct type_desc *type; unsigned one_dim_array_elems; unsigned two_dim_array_elems; }; /** * Generate a vertex or fragment shader to test the given set of * varyings. */ static GLint get_shader(bool is_vs, unsigned glsl_version, int num_varyings, struct varying_desc *varyings, enum test_array_type array_type) { GLuint shader; char *full_text = malloc(1000 * 100 + num_varyings); char *text = full_text; unsigned i, j, k, l, m; const char *varying_keyword; unsigned offset = 0; GLenum shader_type = is_vs ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER; bool fp64 = false; if (glsl_version >= 130) { if (is_vs) varying_keyword = "out"; else varying_keyword = "in"; } else { varying_keyword = "varying"; } text += sprintf(text, "#version %u\n", glsl_version); if (array_type == ARRAYS_OF_ARRAYS) text += sprintf(text, "#extension GL_ARB_arrays_of_arrays: enable\n"); for (i = 0; i < num_varyings; ++i) { const char *opt_flat_keyword = ""; if (!fp64 && varyings[i].type->base == BASE_TYPE_DOUBLE) { text += sprintf(text, "#extension GL_ARB_gpu_shader_fp64: enable\n"); fp64 = true; } if (varyings[i].type->base != BASE_TYPE_FLOAT) opt_flat_keyword = "flat "; if (varyings[i].two_dim_array_elems != 0) { text += sprintf(text, "%s%s %s var%03u[%u][%u];\n", opt_flat_keyword, varying_keyword, varyings[i].type->name, i, outer_dim_size, varyings[i].two_dim_array_elems); } else if (varyings[i].one_dim_array_elems != 0) { text += sprintf(text, "%s%s %s var%03u[%u];\n", opt_flat_keyword, varying_keyword, varyings[i].type->name, i, varyings[i].one_dim_array_elems); } else { text += sprintf(text, "%s%s %s var%03u;\n", opt_flat_keyword, varying_keyword, varyings[i].type->name, i); } } if (glsl_version >= 150 && is_vs) { text += sprintf(text, "in vec4 piglit_vertex;\n"); text += sprintf(text, "#define gl_Vertex piglit_vertex\n"); } text += sprintf(text, "uniform int i;\n"); text += sprintf(text, "\n" "void main()\n" "{\n"); if (is_vs) text += sprintf(text, " gl_Position = gl_Vertex;\n"); else text += sprintf(text, " bool failed = false;\n"); for (i = 0; i < num_varyings; ++i) { unsigned array_loop_bound; unsigned outer_array_loop_bound = 1; const char *base_type_name = get_base_type_name(varyings[i].type->base); if (varyings[i].two_dim_array_elems != 0) { outer_array_loop_bound = outer_dim_size; array_loop_bound = varyings[i].two_dim_array_elems; } else { array_loop_bound = varyings[i].one_dim_array_elems; } if (array_loop_bound == 0) array_loop_bound = 1; for (j = 0; j < outer_array_loop_bound; ++j) { for (k = 0; k < array_loop_bound; ++k) { for (l = 0; l < varyings[i].type->num_cols; ++l) { for (m = 0; m < varyings[i].type->num_rows; ++m) { text += sprintf(text, " "); if (!is_vs) text += sprintf(text, "failed = failed || "); text += sprintf(text, "var%03u", i); if (varyings[i].two_dim_array_elems) text += sprintf(text, "[%u]", j); if (varyings[i].one_dim_array_elems || varyings[i].two_dim_array_elems) text += sprintf(text, "[%u]", k); if (varyings[i].type->num_cols > 1) text += sprintf(text, "[%u]", l); if (varyings[i].type->num_rows > 1) text += sprintf(text, "[%u]", m); if (is_vs) text += sprintf(text, " = "); else text += sprintf(text, " != "); text += sprintf(text, "%s(i + %u);\n", base_type_name, offset++); } } } } } if (!is_vs) { text += sprintf(text, " if (failed)\n" " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" " else\n" " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"); } text += sprintf(text, "}\n"); shader = piglit_compile_shader_text(shader_type, full_text); free(full_text); return shader; } /** * Choose the set of varyings necessary to properly run the given test * configuration, given the implementation's reported value of * max_varying_floats. */ static unsigned choose_varyings(struct varying_desc *varyings, const struct type_desc *test_type, enum test_array_type array_type, unsigned max_varying_floats) { unsigned num_varyings = 0; unsigned element_size = test_type->base == BASE_TYPE_DOUBLE ? 2 : 1; unsigned components_in_test_type = test_type->num_cols * test_type->num_rows * element_size; unsigned num_test_varyings = max_varying_floats / components_in_test_type; unsigned num_two_dim_test_varyings = num_test_varyings / outer_dim_size; unsigned num_extra_arrays = 0; unsigned num_extra_varyings = (max_varying_floats - num_test_varyings * components_in_test_type) / element_size; unsigned i; if (array_type == ARRAYS_OF_ARRAYS) { varyings[num_varyings].type = test_type; varyings[num_varyings].two_dim_array_elems = num_two_dim_test_varyings; varyings[num_varyings].one_dim_array_elems = 0; num_extra_arrays = num_test_varyings - (num_two_dim_test_varyings * outer_dim_size); ++num_varyings; if (num_extra_arrays > 0) { varyings[num_varyings].type = test_type; varyings[num_varyings].two_dim_array_elems = 0; varyings[num_varyings].one_dim_array_elems = num_extra_arrays; ++num_varyings; } } else if (array_type == ARRAY) { varyings[num_varyings].type = test_type; varyings[num_varyings].two_dim_array_elems = 0; varyings[num_varyings].one_dim_array_elems = num_test_varyings; ++num_varyings; } else { for (i = 0; i < num_test_varyings; ++i) { varyings[num_varyings].type = test_type; varyings[num_varyings].two_dim_array_elems = 0; varyings[num_varyings].one_dim_array_elems = 0; ++num_varyings; } } for (i = 0; i < num_extra_varyings; ++i) { switch(test_type->base) { case BASE_TYPE_UINT: varyings[num_varyings].type = &uint_type; break; case BASE_TYPE_INT: varyings[num_varyings].type = &int_type; break; case BASE_TYPE_FLOAT: varyings[num_varyings].type = &float_type; break; case BASE_TYPE_DOUBLE: varyings[num_varyings].type = &double_type; break; } varyings[num_varyings].two_dim_array_elems = 0; varyings[num_varyings].one_dim_array_elems = 0; ++num_varyings; } return num_varyings; } void NORETURN print_usage_and_exit(const char *prog_name) { unsigned i; printf("Usage: %s \n" " where is one of:\n", prog_name); for (i = 0; all_types[i]; ++i) printf(" %s\n", all_types[i]->name); printf(" and is one of:\n" " array: test using an array of the above type\n" " arrays_of_arrays: test using a multidimensional array" " of the above type\n" " separate: test using separately declared varyings\n"); piglit_report_result(PIGLIT_FAIL); } static const struct type_desc *test_type; static void parse_args(int argc, char *argv[], struct piglit_gl_test_config *config) { unsigned i; if (argc < 3) print_usage_and_exit(argv[0]); for (i = 0; all_types[i]; ++i) { if (strcmp(argv[1], all_types[i]->name) == 0) break; } if (all_types[i]) test_type = all_types[i]; else print_usage_and_exit(argv[0]); if (test_type->glsl_version_required <= 110) config->supports_gl_compat_version = 20; else if (test_type->glsl_version_required <= 120) config->supports_gl_compat_version = 21; else if (test_type->glsl_version_required <= 130) config->supports_gl_compat_version = 30; else if (test_type->glsl_version_required <= 150) config->supports_gl_core_version = 32; else piglit_report_result(PIGLIT_FAIL); } void piglit_init(int argc, char **argv) { enum test_array_type array_type; GLint max_varying_floats; struct varying_desc *varyings; unsigned num_varyings; unsigned glsl_version; GLuint vs, fs; if (argc != 3) print_usage_and_exit(argv[0]); glsl_version = test_type->glsl_version_required; if (strcmp(argv[2], "array") == 0) array_type = ARRAY; else if (strcmp(argv[2], "separate") == 0) array_type = SEPARATE; else if (strcmp(argv[2], "arrays_of_arrays") == 0) { array_type = ARRAYS_OF_ARRAYS; piglit_require_extension("GL_ARB_arrays_of_arrays"); if (glsl_version < 120) glsl_version = 120; } else print_usage_and_exit(argv[0]); piglit_require_gl_version(20); piglit_require_GLSL_version(glsl_version); if (test_type->base == BASE_TYPE_DOUBLE) piglit_require_extension("GL_ARB_gpu_shader_fp64"); glGetIntegerv(GL_MAX_VARYING_FLOATS, &max_varying_floats); varyings = malloc(sizeof(*varyings) * max_varying_floats); num_varyings = choose_varyings(varyings, test_type, array_type, max_varying_floats); vs = get_shader(true, test_type->glsl_version_required, num_varyings, varyings, array_type); fs = get_shader(false, test_type->glsl_version_required, num_varyings, varyings, array_type); prog = piglit_link_simple_program(vs, fs); i_location = glGetUniformLocation(prog, "i"); free(varyings); } enum piglit_result piglit_display(void) { GLboolean pass = GL_TRUE; GLuint vao; float green[] = { 0.0, 1.0, 0.0, 1.0 }; glClear(GL_COLOR_BUFFER_BIT); glUseProgram(prog); glUniform1i(i_location, 0); if (piglit_is_core_profile) { glGenVertexArrays(1, &vao); glBindVertexArray(vao); } piglit_draw_rect(-1, -1, 2, 2); pass = piglit_probe_rect_rgba(0, 0, piglit_width, piglit_height, green) && pass; piglit_present_results(); return pass ? PIGLIT_PASS : PIGLIT_FAIL; }