/* * Copyright (c) 2014 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. */ #include "piglit-util-gl.h" /** * @file xfb-streams.c * * This test uses geometry shader multiple stream support from * GL_ARB_gpu_shader5 and GL_ARB_transform_feedback3 to capture * transform feedback from 4 streams into 4 buffers. (GL_ARB_gpu_shader5 * requires support for 4 GS streams.) */ 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 static const char vs_pass_thru_text[] = "#version 150\n" "void main() {\n" " gl_Position = vec4(0.0);\n" "}\n"; static const char gs_tmpl[] = "#version 150\n" "#extension GL_ARB_gpu_shader5 : enable\n" "layout(points, invocations = %d) in;\n" "layout(points, max_vertices = 4) out;\n" "out float stream0_0_out;\n" "layout(stream = 1) out vec2 stream1_0_out;\n" "layout(stream = 2) out float stream2_0_out;\n" "layout(stream = 3) out vec3 stream3_0_out;\n" "layout(stream = 1) out vec3 stream1_1_out;\n" "layout(stream = 2) out vec4 stream2_1_out;\n" "void main() {\n" " gl_Position = gl_in[0].gl_Position;\n" " stream0_0_out = 1.0 + gl_InvocationID;\n" " EmitVertex();\n" " EndPrimitive();\n" " stream3_0_out = vec3(12.0 + gl_InvocationID, 13.0 + gl_InvocationID,\n" " 14.0 + gl_InvocationID);\n" " EmitStreamVertex(3);\n" " EndStreamPrimitive(3);\n" " stream2_0_out = 7.0 + gl_InvocationID;\n" " stream2_1_out = vec4(8.0 + gl_InvocationID, 9.0 + gl_InvocationID,\n" " 10.0 + gl_InvocationID, 11.0 + gl_InvocationID);\n" " EmitStreamVertex(2);\n" " EndStreamPrimitive(2);\n" " stream1_0_out = vec2(2.0 + gl_InvocationID, 3.0 + gl_InvocationID);\n" " stream1_1_out = vec3(4.0 + gl_InvocationID, 5.0 + gl_InvocationID,\n" " 6.0 + gl_InvocationID);\n" " EmitStreamVertex(1);\n" " EndStreamPrimitive(1);\n" "}"; static const char gs_tmpl_blocks[] = "#version 150\n" "#extension GL_ARB_gpu_shader5 : enable\n" "layout(points, invocations = %d) in;\n" "layout(points, max_vertices = 4) out;\n" "out float stream0_0_out;\n" "layout(stream = 1) out Block1 {\n" " vec2 stream1_0_out;\n" " vec3 stream1_1_out;\n" "};\n" "layout(stream = 2) out Block2 {\n" " float stream2_0_out;\n" " vec4 stream2_1_out;\n" "} blockWithName;\n" "layout(stream = 3) out vec3 stream3_0_out;\n" "void main() {\n" " gl_Position = gl_in[0].gl_Position;\n" " stream0_0_out = 1.0 + gl_InvocationID;\n" " EmitVertex();\n" " EndPrimitive();\n" " stream3_0_out = vec3(12.0 + gl_InvocationID, 13.0 + gl_InvocationID,\n" " 14.0 + gl_InvocationID);\n" " EmitStreamVertex(3);\n" " EndStreamPrimitive(3);\n" " blockWithName.stream2_0_out = 7.0 + gl_InvocationID;\n" " blockWithName.stream2_1_out = vec4(8.0 + gl_InvocationID, 9.0 + gl_InvocationID,\n" " 10.0 + gl_InvocationID, 11.0 + gl_InvocationID);\n" " EmitStreamVertex(2);\n" " EndStreamPrimitive(2);\n" " stream1_0_out = vec2(2.0 + gl_InvocationID, 3.0 + gl_InvocationID);\n" " stream1_1_out = vec3(4.0 + gl_InvocationID, 5.0 + gl_InvocationID,\n" " 6.0 + gl_InvocationID);\n" " EmitStreamVertex(1);\n" " EndStreamPrimitive(1);\n" "}"; const char *stream_names[] = { "first", "second", "third", "forth" }; int stream_float_counts[] = { 1, 5, 5, 3 }; #define STREAMS ARRAY_SIZE(stream_names) static const char *varyings[] = { "stream0_0_out", "gl_NextBuffer", "stream1_0_out", "stream1_1_out", "gl_NextBuffer", "stream2_0_out", "stream2_1_out", "gl_NextBuffer", "stream3_0_out" }; static const char *varyings_blocks[] = { "stream0_0_out", "gl_NextBuffer", "stream1_0_out", "stream1_1_out", "gl_NextBuffer", "Block2.stream2_0_out", "Block2.stream2_1_out", "gl_NextBuffer", "stream3_0_out" }; static bool build_and_use_program(GLint gs_invocation_n, const char *gs_template, const char **gs_varyings, int array_size) { GLuint prog; char *gs_text; (void)!asprintf(&gs_text, gs_template, gs_invocation_n); prog = piglit_build_simple_program_multiple_shaders( GL_VERTEX_SHADER, vs_pass_thru_text, GL_GEOMETRY_SHADER, gs_text, 0); free(gs_text); glTransformFeedbackVaryings(prog, array_size, gs_varyings, GL_INTERLEAVED_ATTRIBS); glLinkProgram(prog); if (!piglit_link_check_status(prog)) return false; if (!piglit_check_gl_error(GL_NO_ERROR)) return false; glUseProgram(prog); return true; } static bool probe_buffers(const GLuint *xfb, const GLuint *queries, unsigned primitive_n) { bool pass; unsigned i; GLuint query_result; float *expected[STREAMS]; int expected_n[STREAMS]; for (i = 0; i < STREAMS; i++) { expected_n[i] = stream_float_counts[i] * primitive_n; } for (i = 0; i < STREAMS; i++) { glGetQueryObjectuiv(queries[i], GL_QUERY_RESULT, &query_result); if (query_result != primitive_n) { printf("Expected %u primitives generated, got %u\n", primitive_n, query_result); return false; } glGetQueryObjectuiv(queries[STREAMS+i], GL_QUERY_RESULT, &query_result); if (query_result != primitive_n) { printf("Expected %u TF primitives written, got %u\n", primitive_n, query_result); return false; } } for (i = 0; i < STREAMS; i++) { expected[i] = malloc(expected_n[i] * sizeof(float)); } for (i = 0; i < primitive_n; ++i) { expected[0][i * stream_float_counts[0] + 0] = i + 1.0; /* stream0_0 */ expected[1][i * stream_float_counts[1] + 0] = i + 2.0; /* stream1_0[0] */ expected[1][i * stream_float_counts[1] + 1] = i + 3.0; /* stream1_0[1] */ expected[1][i * stream_float_counts[1] + 2] = i + 4.0; /* stream1_1[0] */ expected[1][i * stream_float_counts[1] + 3] = i + 5.0; /* stream1_1[1] */ expected[1][i * stream_float_counts[1] + 4] = i + 6.0; /* stream1_1[2] */ expected[2][i * stream_float_counts[2] + 0] = i + 7.0; /* stream2_0 */ expected[2][i * stream_float_counts[2] + 1] = i + 8.0; /* stream2_1[0] */ expected[2][i * stream_float_counts[2] + 2] = i + 9.0; /* stream2_1[1] */ expected[2][i * stream_float_counts[2] + 3] = i + 10.0; /* stream2_1[2] */ expected[2][i * stream_float_counts[2] + 4] = i + 11.0; /* stream2_1[3] */ expected[3][i * stream_float_counts[3] + 0] = i + 12.0; /* stream3_0[0] */ expected[3][i * stream_float_counts[3] + 1] = i + 13.0; /* stream3_0[1] */ expected[3][i * stream_float_counts[3] + 2] = i + 14.0; /* stream3_0[2] */ } for (i = 0; i < STREAMS; ++i) { char *name; (void)!asprintf(&name, "stream%d", i); pass = piglit_probe_buffer(xfb[i], GL_TRANSFORM_FEEDBACK_BUFFER, name, 1, expected_n[i], expected[i]); free(name); } for (i = 0; i < STREAMS; i++) { free(expected[i]); } return pass; } static bool run_subtest(const char *gs_template, const char **gs_varyings, int array_size) { bool pass; unsigned primitive_n; GLint gs_invocation_n; GLuint queries[2*STREAMS]; GLuint xfb[STREAMS]; GLuint vao; unsigned i; piglit_require_extension("GL_ARB_gpu_shader5"); piglit_require_extension("GL_ARB_transform_feedback3"); glGetIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &gs_invocation_n); if (!piglit_check_gl_error(GL_NO_ERROR)) return false; if (gs_invocation_n <= 0) { printf("Maximum amount of geometry shader invocations " "needs to be positive (%u).\n", gs_invocation_n); return false; } primitive_n = gs_invocation_n; if (!build_and_use_program(gs_invocation_n, gs_template, gs_varyings, array_size)) { return false; } /* Set up the transform feedback buffers. */ glGenBuffers(ARRAY_SIZE(xfb), xfb); for (i = 0; i < ARRAY_SIZE(xfb); i++) { unsigned float_n = primitive_n * stream_float_counts[i]; glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, xfb[i]); glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, float_n * sizeof(float), NULL, GL_STREAM_READ); } /* Test only records using transform feedback. */ glEnable(GL_RASTERIZER_DISCARD); if (!piglit_check_gl_error(GL_NO_ERROR)) return false; glGenQueries(ARRAY_SIZE(queries), queries); for (i = 0; i < STREAMS; i++) { glBeginQueryIndexed(GL_PRIMITIVES_GENERATED, i, queries[i]); glBeginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, i, queries[STREAMS + i]); } glGenVertexArrays(1, &vao); glBindVertexArray(vao); /* Draw and record */ glBeginTransformFeedback(GL_POINTS); glDrawArrays(GL_POINTS, 0, 1); for (i = 0; i < STREAMS; i++) { glEndQueryIndexed(GL_PRIMITIVES_GENERATED, i); glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, i); } glEndTransformFeedback(); glDeleteVertexArrays(1, &vao); if (!piglit_check_gl_error(GL_NO_ERROR)) return false; pass = probe_buffers(xfb, queries, primitive_n); glDeleteBuffers(ARRAY_SIZE(xfb), xfb); glDeleteQueries(ARRAY_SIZE(queries), queries); return pass; } void piglit_init(int argc, char **argv) { bool test1; bool test2; test1 = run_subtest(gs_tmpl, varyings, ARRAY_SIZE(varyings)); piglit_report_subtest_result(test1 ? PIGLIT_PASS : PIGLIT_FAIL, "arb_gpu_shader5-xfb-streams"); test2 = run_subtest(gs_tmpl_blocks, varyings_blocks, ARRAY_SIZE(varyings_blocks)); piglit_report_subtest_result(test2 ? PIGLIT_PASS : PIGLIT_FAIL, "arb_gpu_shader5-xfb-streams-blocks"); piglit_report_result(test1 && test2 ? PIGLIT_PASS : PIGLIT_FAIL); } enum piglit_result piglit_display(void) { /* Should never be reached */ return PIGLIT_FAIL; }