/* * 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 400-combinations.c * Combine 20 vertex shaders and 20 fragment shaders in various ways. * * Verify that the right shaders are used in the right combinations several * ways. * * * The vertex shader has information baked-in that determines the X position * of the block on the screen. * * * The fragment shader has information baked-in that determines how the * block is colored. This is combined with data passed from the vertex * shader. * * Since data is passed from the vertex shader to the fragment shader, the * test can use either rendezvous-by-name (default) or rendezvous-by-location * (with --by-location command line parameter). */ #include "piglit-util-gl.h" #include "sso-common.h" /** * Size of each square that will be drawn. */ static const unsigned tile_size = 5; /** * Size of the gap between the squares. */ static const unsigned border_size = 2; static GLuint vs_programs[20]; static GLuint fs_programs[20]; PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 10; config.supports_gl_core_version = 31; config.window_width = (tile_size + border_size) * ARRAY_SIZE(vs_programs); config.window_height = (tile_size + border_size) * ARRAY_SIZE(fs_programs); config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE; PIGLIT_GL_TEST_CONFIG_END static GLuint pipe; static GLuint vao = 0; static GLuint bo = 0; struct combination { unsigned char row; unsigned char col; }; static struct combination combinations[ARRAY_SIZE(vs_programs) * ARRAY_SIZE(fs_programs)]; static const char *vs_code = "#version %d\n" "#extension GL_ARB_separate_shader_objects: require\n" "#extension GL_ARB_explicit_attrib_location: require\n" "\n" "layout(location = 0) in vec4 piglit_vertex;\n" "layout(location = 1) in vec3 vertex_color;\n" "\n" "%s out vec3 %s;\n" "\n" "const vec4 offset = vec4(%d, 0, 0, 0);\n" "\n" "uniform mat4 transform;\n" "\n" "void main()\n" "{\n" " gl_Position = transform * (piglit_vertex + offset);\n" " %s = vertex_color;\n" "}\n" ; static const char *fs_code = "#version %d\n" "#extension GL_ARB_separate_shader_objects: require\n" "#extension GL_ARB_explicit_attrib_location: enable\n" "\n" "#if __VERSION__ >= 130\n" "layout(location = 0) out vec4 out_color;\n" "#else\n" "#define out_color gl_FragColor\n" "#endif\n" "\n" "%s in vec3 %s;\n" "\n" "const vec3 color_offset = vec3(%d, %d, %d);\n" "\n" "void main()\n" "{\n" " out_color = vec4(%s + color_offset, 1.);\n" "}\n" ; enum piglit_result piglit_display(void) { unsigned i; unsigned j; static const float expected[] = { 0.0f, 1.0f, 0.0f, 1.0f }; /* This is stored in row-major order. Note the GL_TRUE parameter to * the glProgramUniformMatrix4fv call below. */ const float transform[16] = { 2.f / piglit_width, 0.0f, 0.0f, -1.0f, 0.0f, 2.f / piglit_height, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; bool pass = true; glClearColor(.5f, .5f, .5f, 1.f); glClear(GL_COLOR_BUFFER_BIT); for (i = 0; i < ARRAY_SIZE(vs_programs); i++) { const GLint loc = glGetUniformLocation(vs_programs[i], "transform"); glProgramUniformMatrix4fv(vs_programs[i], loc, 1, GL_TRUE, transform); } glBindProgramPipeline(pipe); for (i = 0; i < ARRAY_SIZE(combinations); i++) { const unsigned row = combinations[i].row; const unsigned col = combinations[i].col; glUseProgramStages(pipe, GL_VERTEX_SHADER_BIT, vs_programs[col]); glUseProgramStages(pipe, GL_FRAGMENT_SHADER_BIT, fs_programs[row]); glDrawArrays(GL_TRIANGLE_FAN, row * 4, 4); } glBindProgramPipeline(0); for (i = 0; i < ARRAY_SIZE(vs_programs); i++) { for (j = 0; j < ARRAY_SIZE(fs_programs); j++) { const unsigned x = (i * tile_size) + ((i + 1) * border_size); const unsigned y = (j * tile_size) + ((j + 1) * border_size); pass = piglit_probe_rect_rgba(x, y, tile_size, tile_size, expected) && pass; } } piglit_present_results(); return pass ? PIGLIT_PASS : PIGLIT_FAIL; } #define RED(x) ((int) (x / 2)) #define GREEN(x) (-(int) x) #define BLUE(x) ((int) (x * 7)) void piglit_init(int argc, char **argv) { unsigned glsl_version; unsigned i; unsigned j; unsigned idx; const char *location; const char *vertex_name; const char *fragment_name; struct vertex { float x; float y; float r; float g; float b; } *vert; piglit_require_extension("GL_ARB_separate_shader_objects"); piglit_require_extension("GL_ARB_explicit_attrib_location"); if (argc > 1 && strcmp(argv[1], "--by-location") == 0) { location = "layout(location = 3)"; vertex_name = "a"; fragment_name = "b"; } else { location = ""; vertex_name = "in_color"; fragment_name = "in_color"; } glsl_version = pick_a_glsl_version(); /* Generate the vertex shader programs. Each vertex shader is * hardcoded to select a specific column on the display. */ printf("Generating vertex shaders...\n"); for (i = 0; i < ARRAY_SIZE(vs_programs); i++) { const unsigned base_x = (i * tile_size) + ((i + 1) * border_size); char *source = NULL; (void)!asprintf(&source, vs_code, glsl_version, location, vertex_name, base_x, vertex_name); vs_programs[i] = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, (const GLchar *const *) &source); piglit_link_check_status(vs_programs[i]); if (i == 0) puts(source); free(source); } printf("Generating fragment shaders...\n"); for (i = 0; i < ARRAY_SIZE(fs_programs); i++) { char *source = NULL; (void)!asprintf(&source, fs_code, glsl_version, location, fragment_name, RED(i), GREEN(i), BLUE(i), fragment_name); fs_programs[i] = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, (const GLchar *const *) &source); piglit_link_check_status(fs_programs[i]); if (i == 3) puts(source); free(source); } glGenProgramPipelines(1, &pipe); /* Generate vertex data for the tests. The row of each block is * determined by the vertex data. The color data for the block comes * from the vertex data and the data baked into the fragment shader. */ if (piglit_get_gl_version() >= 30 || piglit_is_extension_supported("GL_ARB_vertex_array_object")) { glGenVertexArrays(1, &vao); glBindVertexArray(vao); } glGenBuffers(1, &bo); glBindBuffer(GL_ARRAY_BUFFER, bo); glBufferData(GL_ARRAY_BUFFER, sizeof(vert[0]) * 4 * ARRAY_SIZE(fs_programs), NULL, GL_STATIC_DRAW); vert = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); for (i = 0; i < ARRAY_SIZE(fs_programs); i++) { const unsigned base_y = (i * tile_size) + ((i + 1) * border_size); vert[(i * 4) + 0].x = 0.f; vert[(i * 4) + 0].y = (float) base_y; vert[(i * 4) + 0].r = (float) -RED(i); vert[(i * 4) + 0].g = (float) 1 - GREEN(i); vert[(i * 4) + 0].b = (float) -BLUE(i); vert[(i * 4) + 1].x = (float) tile_size; vert[(i * 4) + 1].y = (float) base_y; vert[(i * 4) + 1].r = (float) -RED(i); vert[(i * 4) + 1].g = (float) 1 - GREEN(i); vert[(i * 4) + 1].b = (float) -BLUE(i); vert[(i * 4) + 2].x = (float) tile_size; vert[(i * 4) + 2].y = (float) (base_y + tile_size); vert[(i * 4) + 2].r = (float) -RED(i); vert[(i * 4) + 2].g = (float) 1 - GREEN(i); vert[(i * 4) + 2].b = (float) -BLUE(i); vert[(i * 4) + 3].x = 0.f; vert[(i * 4) + 3].y = (float) (base_y + tile_size); vert[(i * 4) + 3].r = (float) -RED(i); vert[(i * 4) + 3].g = (float) 1 - GREEN(i); vert[(i * 4) + 3].b = (float) -BLUE(i); } glUnmapBuffer(GL_ARRAY_BUFFER); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vert[0]), (void *)(intptr_t) offsetof(struct vertex, x)); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vert[0]), (void *)(intptr_t) offsetof(struct vertex, r)); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); /* Generate the set of combinations of vertex shader programs and * fragment shader programs that will be used together. This is all * the possible combinations. The next step is to shuffle list so * that there's (hopefully) no pattern to the access combination... to * uncover driver bugs. */ idx = 0; for (i = 0; i < ARRAY_SIZE(vs_programs); i++) { for (j = 0; j < ARRAY_SIZE(fs_programs); j++) { combinations[idx].row = j; combinations[idx].col = i; idx++; } } for (i = ARRAY_SIZE(combinations); i > 1; i--) { const unsigned j = rand() % i; if (j != i - 1) { const struct combination temp = combinations[j]; combinations[j] = combinations[i - 1]; combinations[i - 1] = temp; } } }