/* * 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 primitive-id-restart.c * * Verify that the use of primitive restart does not reset the * geometry shader's gl_PrimitiveID counter. * * From the GL 3.2 core spec, section 2.12.4 (Geometry Shader * Execution Environment): * * Restarting a primitive topology using the primitive restart * index has no effect on the primitive ID counter. * * This test uses a simple geometry shader that copies * gl_PrimitiveIDIn to a single output, which is captured using * transform feedback. * * The test operates by specifying a sequence of: * * - One vertex followed by primitive restart * - Two vertices followed by primitive restart * - Three vertices followed by primitive restart * * And so on up to twelve. The resulting transform feedback output is * checked to verify that the primitive ID's received by the geometry * shaders count upwards from 0, without restarting anywhere. * * Note: some generations of Intel hardware require primitive restart * to be emulated in software when either: * * - certain primitive types are used, or * * - the primitive restart index is not all 0xff's. * * To make sure that both the hardware and software primitive restart * codepaths are tested, this test accepts command line options to * control (a) which primitive type to use, and (b) whether the * primitive restart index should be all 0xff's. */ #include "piglit-util-gl.h" PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 32; config.supports_gl_core_version = 32; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END /** * Test up to 12 vertices both before and after primitive restart, * since that ensures that there are at least two primitives before * primitive restart in all drawing modes. */ #define LONGEST_INPUT_SEQUENCE 12 /* Sum of 2, 3, ..., LONGEST_INPUT_SEQUENCE + 1. */ #define NUM_ELEMENTS \ (LONGEST_INPUT_SEQUENCE * (LONGEST_INPUT_SEQUENCE + 3) / 2) /* Sum of 1, 2, ..., LONGEST_INPUT_SEQUENCE. */ #define MAX_TOTAL_PRIMS \ (LONGEST_INPUT_SEQUENCE * (LONGEST_INPUT_SEQUENCE + 1) / 2) static const char *vs_text = "#version 150\n" "\n" "void main()\n" "{\n" "}\n"; static const char *gs_template = "#version 150\n" "layout(%s) in;\n" "layout(points, max_vertices = 1) out;\n" "\n" "out int primitive_id;\n" "\n" "void main()\n" "{\n" " primitive_id = gl_PrimitiveIDIn;\n" " EmitVertex();\n" "}\n"; static const char *varyings[] = { "primitive_id" }; struct prim_type_info { GLenum prim_type; const char *input_layout; } prim_types[] = { { GL_POINTS, "points" }, { GL_LINE_LOOP, "lines" }, { GL_LINE_STRIP, "lines" }, { GL_LINES, "lines" }, { GL_TRIANGLES, "triangles" }, { GL_TRIANGLE_STRIP, "triangles" }, { GL_TRIANGLE_FAN, "triangles" }, { GL_LINES_ADJACENCY, "lines_adjacency" }, { GL_LINE_STRIP_ADJACENCY, "lines_adjacency" }, { GL_TRIANGLES_ADJACENCY, "triangles_adjacency" }, { GL_TRIANGLE_STRIP_ADJACENCY, "triangles_adjacency" }, }; NORETURN static void print_usage_and_exit(const char *prog_name) { int i; printf("Usage: %s \n" " where is one of the following:\n", prog_name); for(i = 0; i < ARRAY_SIZE(prim_types); i++) printf(" %s\n", piglit_get_prim_name(prim_types[i].prim_type)); printf(" and is one of the following:\n" " ffs - use a primitive restart index that is all 0xffs\n" " other - use a different primitive restart index\n"); piglit_report_result(PIGLIT_FAIL); } void piglit_init(int argc, char **argv) { GLenum prim_type = 0; const char *input_layout = NULL; GLubyte prim_restart_index; char *gs_text; GLuint prog, vs, gs, vao, xfb_buf, element_buf, generated_query, primitives_generated; GLubyte *elements; int i, j; GLsizei num_elements; bool pass = true; GLuint *readback; /* Parse params */ if (argc != 3) print_usage_and_exit(argv[0]); for (i = 0; i < ARRAY_SIZE(prim_types); i++) { if (strcmp(piglit_get_prim_name(prim_types[i].prim_type), argv[1]) == 0) { prim_type = prim_types[i].prim_type; input_layout = prim_types[i].input_layout; break; } } if (input_layout == NULL) print_usage_and_exit(argv[0]); if (strcmp(argv[2], "ffs") == 0) prim_restart_index = 0xff; else if (strcmp(argv[2], "other") == 0) prim_restart_index = 1; else print_usage_and_exit(argv[0]); /* Compile shaders */ prog = glCreateProgram(); vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vs_text); glAttachShader(prog, vs); (void)!asprintf(&gs_text, gs_template, input_layout); gs = piglit_compile_shader_text(GL_GEOMETRY_SHADER, gs_text); free(gs_text); glAttachShader(prog, gs); glTransformFeedbackVaryings(prog, 1, varyings, GL_INTERLEAVED_ATTRIBS); glLinkProgram(prog); if (!piglit_link_check_status(prog)) piglit_report_result(PIGLIT_FAIL); glUseProgram(prog); /* Set up other GL state */ glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &xfb_buf); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfb_buf); glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, MAX_TOTAL_PRIMS * sizeof(GLint), NULL, GL_STREAM_READ); glGenQueries(1, &generated_query); glEnable(GL_RASTERIZER_DISCARD); glEnable(GL_PRIMITIVE_RESTART); glPrimitiveRestartIndex(prim_restart_index); /* Set up element buffer */ glGenBuffers(1, &element_buf); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buf); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLubyte) * NUM_ELEMENTS, NULL, GL_STATIC_DRAW); elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_WRITE); num_elements = 0; for (i = 1; i <= LONGEST_INPUT_SEQUENCE; i++) { for (j = 0; j < i; j++) { /* Every element that isn't the primitive restart index * can have any value as far as it is not the primitive * restart index since we don't care about the actual * vertex data. * * NOTE: repeating the indices for all elements but the * primitive restart index causes a GPU hang in Intel's * Sandy Bridge platform, likely due to a hardware bug, * so make sure that we do not repeat the indices. * * More information: * * http://lists.freedesktop.org/archives/mesa-dev/2014-July/064221.html */ elements[num_elements++] = j != prim_restart_index ? j : j + 1; } elements[num_elements++] = prim_restart_index; } glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); /* Run vertices through the pipeline */ glBeginQuery(GL_PRIMITIVES_GENERATED, generated_query); glBeginTransformFeedback(GL_POINTS); glDrawElements(prim_type, num_elements, GL_UNSIGNED_BYTE, NULL); glEndTransformFeedback(); glEndQuery(GL_PRIMITIVES_GENERATED); /* Find out how many times the GS got invoked so we'll know * how many transform feedback outputs to examine. */ glGetQueryObjectuiv(generated_query, GL_QUERY_RESULT, &primitives_generated); if (primitives_generated > MAX_TOTAL_PRIMS) { printf("Expected no more than %d primitives, got %d\n", MAX_TOTAL_PRIMS, primitives_generated); pass = false; /* Set primitives_generated to MAX_TOTAL_PRIMS so that * we don't overflow the buffer in the loop below. */ primitives_generated = MAX_TOTAL_PRIMS; } /* Check transform feedback outputs. */ readback = glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); for (i = 0; i < primitives_generated; i++) { if (readback[i] != i) { printf("Expected primitive %d to have gl_PrimitiveIDIn" " = %d, got %d instead\n", i, i, readback[i]); pass = false; } } glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); pass = piglit_check_gl_error(GL_NO_ERROR) && pass; piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL); } enum piglit_result piglit_display(void) { /* Should never be reached */ return PIGLIT_FAIL; }