/* * 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-restart-xfb.c * * Test interactions between primitive restart and transform feedback * primitive counting behaviour. * * This test makes a single glDrawElements(GL_TRIANGLE_STRIP, 9, ...) * draw call, where the index buffer specifies 4 normal vertices, the * primitive restart index, and then 4 more normal vertices. It * verifies that the implementation correctly counts this as drawing 4 * triangles (rather than 7, which would be the behaviour of if * primitive restart were not in use). * * The test can be run in three ways (selectable by a command line * argument): * * - "generated" verifies that the GL_PRIMITIVES_GENERATED query * counts the primitives correctly. * * - "written" verifies that the * GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query counts the * primitives correctly. * * - "flush" verifies that if these 4 triangles are followed by a * glFlush() and then more primitives further drawing, transform * feedback for the latter primitives is placed at the correct * location in the transform feedback buffer. */ #include "piglit-util-gl.h" PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_core_version = 31; config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END enum test_mode_enum { TEST_MODE_GENERATED, TEST_MODE_WRITTEN, TEST_MODE_FLUSH, }; static const char vs_text[] = "#version 130\n" "in int x_in;\n" "flat out int x_out;\n" "void main()\n" "{\n" " gl_Position = vec4(0.0);\n" " x_out = x_in;\n" "}\n"; static const GLchar *varyings[] = { "x_out" }; /** * Indices used for the test. */ static const GLubyte indices[] = { /* For the main draw call */ 0, 1, 2, 3, 0xff, 4, 5, 6, 7, /* After the glFlush() call (when in TEST_MODE_FLUSH) */ 8, 9, 10, 11 }; static const GLint vertex_attrs[] = { 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 }; /** * Expected transform feedback result when in TEST_MODE_FLUSH: * - 2 triangles with attribute 1 and 2 triangles with attribute value 2 (from * the first draw call) * - 2 triangles with attribute 3 (from the second draw call, after the flush) */ static const GLint expected_xfb_result[] = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3 }; static void print_usage_and_exit(const char *prog_name) { printf("Usage: %s \n" " where is one of the following:\n" " generated\n" " written\n" " flush\n", prog_name); piglit_report_result(PIGLIT_FAIL); } static bool check_query_result(GLuint query, GLuint expected) { GLuint result; glGetQueryObjectuiv(query, GL_QUERY_RESULT, &result); if (result != expected) { printf("Query result: %u, expected: %u\n", result, expected); return false; } return true; } static bool check_xfb_result() { bool pass = true; int i; const GLint *readback = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(expected_xfb_result), GL_MAP_READ_BIT); for (i = 0; i < ARRAY_SIZE(expected_xfb_result); i++) { if (readback[i] != expected_xfb_result[i]) { printf("XFB[%i] == %i, expected %i\n", i, readback[i], expected_xfb_result[i]); pass = false; } } glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); return pass; } void piglit_init(int argc, char **argv) { GLuint buf; void *initial_data; bool pass = true; enum test_mode_enum test_mode; GLuint prog; GLuint vao, vbo_attrs, vbo_indices; GLuint query; if (argc != 2) print_usage_and_exit(argv[0]); if (strcmp(argv[1], "generated") == 0) test_mode = TEST_MODE_GENERATED; else if (strcmp(argv[1], "written") == 0) test_mode = TEST_MODE_WRITTEN; else if (strcmp(argv[1], "flush") == 0) test_mode = TEST_MODE_FLUSH; else print_usage_and_exit(argv[0]); prog = piglit_build_simple_program_unlinked(vs_text, NULL); glTransformFeedbackVaryings(prog, 1, varyings, GL_INTERLEAVED_ATTRIBS); glBindAttribLocation(prog, 0, "x_in"); glLinkProgram(prog); if (!piglit_link_check_status(prog) || !piglit_check_gl_error(GL_NO_ERROR)) { piglit_report_result(PIGLIT_FAIL); } glUseProgram(prog); /* Create transform feedback buffer and pre-load it with * garbage. */ glGenBuffers(1, &buf); initial_data = malloc(sizeof(expected_xfb_result)); memset(initial_data, 0xcc, sizeof(expected_xfb_result)); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf); glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(expected_xfb_result), initial_data, GL_STREAM_READ); free(initial_data); /* Set up VAO/VBO */ glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &vbo_attrs); glBindBuffer(GL_ARRAY_BUFFER, vbo_attrs); glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_attrs), vertex_attrs, GL_STREAM_DRAW); glVertexAttribIPointer(0, 1, GL_INT, 0, NULL); glEnableVertexAttribArray(0); glGenBuffers(1, &vbo_indices); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_indices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STREAM_DRAW); /* Misc setup */ glEnable(GL_RASTERIZER_DISCARD); glEnable(GL_PRIMITIVE_RESTART); glPrimitiveRestartIndex(0xff); glGenQueries(1, &query); switch (test_mode) { case TEST_MODE_GENERATED: glBeginQuery(GL_PRIMITIVES_GENERATED, query); glBeginTransformFeedback(GL_TRIANGLES); glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_BYTE, NULL); glEndTransformFeedback(); glEndQuery(GL_PRIMITIVES_GENERATED); pass = check_query_result(query, 4); break; case TEST_MODE_WRITTEN: glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query); glBeginTransformFeedback(GL_TRIANGLES); glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_BYTE, NULL); glEndTransformFeedback(); glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); pass = check_query_result(query, 4); break; case TEST_MODE_FLUSH: glBeginTransformFeedback(GL_TRIANGLES); glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_BYTE, NULL); glFlush(); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, (void *) (9 * sizeof(GLubyte))); glEndTransformFeedback(); pass = check_xfb_result(); break; } 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; }