/* * Copyright © 2011 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 overflow-edge-cases.c * * Verify edge cases of transform feedback overflow checking. * * This test exercises all possible combinations of the following four * variables: * * - Size passed to glBindBufferRange (1-6 floats) * - Number of transform feedback varying components (1 or 2) * - Number of primitives drawn (1-3) * - Primitive mode (GL_POINTS, GL_LINES, or GL_TRIANGLES) * * In all cases, it verifies that: * * - The proper values were written to the transform feedback buffer. * - GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN is set correctly. * - GL_PRIMITIVES_GENERATED is set correctly. * * The optional argument "use_gs" causes the test to use a geometry * shader. When this argument is given, the number of vertices output * by the geometry shader is in general different from the number of * vertices sent down the pipeline by the glDrawArrays() command. * Thus, the test verifies that the implementation does overflow * checking based on the post-geometry-shader vertex count. */ #include "piglit-util-gl.h" static bool use_gs; PIGLIT_GL_TEST_CONFIG_BEGIN use_gs = PIGLIT_STRIP_ARG("use_gs"); if (use_gs) { config.supports_gl_compat_version = 32; config.supports_gl_core_version = 32; } else { config.supports_gl_compat_version = 10; config.supports_gl_core_version = 31; } config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGB; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END #define XFB_BUFFER_SIZE 12 #define MAX_VERTICES 9 /** * Vertex shader used when use_gs is false. */ static const char *vstext_nogs = "attribute float vertex_num;\n" "varying float varying1;\n" "varying float varying2;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(vertex_num);\n" " varying1 = 100.0 + vertex_num;\n" " varying2 = 200.0 + vertex_num;\n" "}\n"; /** * Vertex shader used when use_gs is true. */ static const char *vstext_gs = "#version 150\n" "\n" "void main()\n" "{\n" "}\n"; /** * Geometry shader used when use_gs is true. */ static const char *gstext_gs = "#version 150\n" "layout(points) in;\n" "layout(%s, max_vertices=9) out;\n" "uniform int num_primitives;\n" "uniform int vertices_per_prim;\n" "out float varying1;\n" "out float varying2;\n" "\n" "void main()\n" "{\n" " int vertex_num = 0;\n" " for (int i = 0; i < num_primitives; i++) {\n" " for (int j = 0; j < vertices_per_prim; j++) {\n" " varying1 = 100.0 + float(vertex_num);\n" " varying2 = 200.0 + float(vertex_num);\n" " vertex_num++;\n" " EmitVertex();\n" " }\n" " EndPrimitive();\n" " }\n" "}\n"; static const char *varyings[] = { "varying1", "varying2" }; static GLuint xfb_buf, vao, array_buf; static GLuint progs[3][2]; /* indexed by (mode, num_varyings - 1) */ static GLuint query_prims_generated; static GLuint query_prims_written; static GLenum modes[] = { GL_POINTS, GL_LINES, GL_TRIANGLES }; static const char *mode_gs_out_primtypes[] = { "points", "line_strip", "triangle_strip" }; void piglit_init(int argc, char **argv) { GLuint vs, gs; int num_varyings; int mode; piglit_require_GLSL(); piglit_require_transform_feedback(); for (mode = 0; mode < ARRAY_SIZE(modes); mode++) { if (use_gs) { char *gstext; vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vstext_gs); (void)!asprintf(&gstext, gstext_gs, mode_gs_out_primtypes[mode]); gs = piglit_compile_shader_text(GL_GEOMETRY_SHADER, gstext); } else { vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vstext_nogs); } for (num_varyings = 1; num_varyings <= 2; ++num_varyings) { GLuint prog = glCreateProgram(); glAttachShader(prog, vs); if (use_gs) glAttachShader(prog, gs); else glBindAttribLocation(prog, 0, "vertex_num"); glTransformFeedbackVaryings(prog, num_varyings, varyings, GL_INTERLEAVED_ATTRIBS); glLinkProgram(prog); if (!piglit_link_check_status(prog)) { glDeleteProgram(prog); piglit_report_result(PIGLIT_FAIL); } progs[mode][num_varyings - 1] = prog; } } glGenBuffers(1, &xfb_buf); glGenBuffers(1, &array_buf); glGenQueries(1, &query_prims_generated); glGenQueries(1, &query_prims_written); if (piglit_is_extension_supported("GL_ARB_vertex_array_object") || piglit_get_gl_version() >= 30) { glGenVertexArrays(1, &vao); glBindVertexArray(vao); } } static int mode_vertices_per_prim[] = { 1, 2, 3 }; static GLboolean test(int bind_size, int num_varyings, int num_primitives, int mode_index) { float initial_xfb_buf[XFB_BUFFER_SIZE]; float vertex_data[MAX_VERTICES]; int i, j; int vertices_per_prim = mode_vertices_per_prim[mode_index]; int expected_primitives_written = MIN2(num_primitives, bind_size / num_varyings / vertices_per_prim); int expected_vertices_written = expected_primitives_written * vertices_per_prim; GLuint query_result; GLboolean pass = GL_TRUE; float expected_xfb_results[XFB_BUFFER_SIZE]; float *readback; GLuint prog; printf("size=%d, num_varyings=%d, num_primitives=%d, mode=%s: ", bind_size, num_varyings, num_primitives, piglit_get_prim_name(modes[mode_index])); /* Setup program and initial buffer contents */ prog = progs[mode_index][num_varyings - 1]; glUseProgram(prog); if (use_gs) { glUniform1i(glGetUniformLocation(prog, "num_primitives"), num_primitives); glUniform1i(glGetUniformLocation(prog, "vertices_per_prim"), vertices_per_prim); } else { for (i = 0; i < MAX_VERTICES; ++i) vertex_data[i] = i; glBindBuffer(GL_ARRAY_BUFFER, array_buf); glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), &vertex_data, GL_STATIC_DRAW); glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, sizeof(float), NULL); glEnableVertexAttribArray(0); } for (i = 0; i < XFB_BUFFER_SIZE; ++i) initial_xfb_buf[i] = 0.0; glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfb_buf); glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(initial_xfb_buf), initial_xfb_buf, GL_STREAM_READ); glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfb_buf, 0, sizeof(float) * bind_size); /* Start queries and XFB */ glBeginQuery(GL_PRIMITIVES_GENERATED, query_prims_generated); glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query_prims_written); glBeginTransformFeedback(modes[mode_index]); /* Draw */ if (use_gs) { glDrawArrays(GL_POINTS, 0, 1); } else { glDrawArrays(modes[mode_index], 0, num_primitives * vertices_per_prim); } /* Stop XFB and check queries */ glEndTransformFeedback(); glEndQuery(GL_PRIMITIVES_GENERATED); glGetQueryObjectuiv(query_prims_generated, GL_QUERY_RESULT, &query_result); if (query_result != num_primitives) { printf("\n Expected %u primitives generated, got %u\n", (unsigned) num_primitives, query_result); pass = GL_FALSE; } glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); glGetQueryObjectuiv(query_prims_written, GL_QUERY_RESULT, &query_result); if (query_result != expected_primitives_written) { printf("\n Expected %u primitives written, got %u", (unsigned) expected_primitives_written, query_result); pass = GL_FALSE; } /* Check transform feedback buffer */ memcpy(expected_xfb_results, initial_xfb_buf, sizeof(initial_xfb_buf)); for (i = 0; i < expected_vertices_written; ++i) { for (j = 0; j < num_varyings; ++j) { expected_xfb_results[i * num_varyings + j] = 100.0 * (j + 1) + i; } } readback = glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); for (i = 0; i < XFB_BUFFER_SIZE; ++i) { if (expected_xfb_results[i] != readback[i]) { printf("\n Expected buf[%i] = %f, got %f", i, expected_xfb_results[i], readback[i]); pass = GL_FALSE; } } glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); /* unmap readback */ if (pass) printf("PASS\n"); else printf(" FAIL\n"); return pass; } enum piglit_result piglit_display(void) { GLboolean pass = GL_TRUE; int bind_size, num_varyings, num_primitives, mode_index; for (bind_size = 1; bind_size <= 6; ++bind_size) { for (num_varyings = 1; num_varyings <= 2; ++num_varyings) { for (num_primitives = 1; num_primitives <= 3; ++num_primitives) { for (mode_index = 0; mode_index < 3; ++mode_index) { pass = test(bind_size, num_varyings, num_primitives, mode_index) && pass; } } } } return pass ? PIGLIT_PASS : PIGLIT_FAIL; }