perf/pixel-rate: new pixel throughput microbenchmark
[piglit.git] / tests / spec / ext_transform_feedback / change-size.c
blob05682c7a3c486660c9c83d6a494dfcbddfde97ef
1 /*
2 * Copyright © 2012 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 /**
25 * \file change-size.c
27 * Confirm that transform feedback properly handles a change in the
28 * size of a transform feedback buffer after it is bound but before it
29 * is used.
31 * In particular, this test verifies the following behaviours, from
32 * the GL 4.3 spec, section 6.1.1 ("Binding Buffer Objects to Indexed
33 * Targets"):
35 * BindBufferBase binds the entire buffer, even when the size of the buffer
36 * is changed after the binding is established. It is equivalent to calling
37 * BindBufferRange with offset zero, while size is determined by the size of
38 * the bound buffer at the time the binding is used.
40 * Regardless of the size specified with BindBufferRange, or indirectly with
41 * BindBufferBase, the GL will never read or write beyond the end of a bound
42 * buffer. In some cases this constraint may result in visibly different
43 * behavior when a buffer overflow would otherwise result, such as described
44 * for transform feedback operations in section 13.2.2.
46 * This test verifies that the expected number of primitives are
47 * written after a change to the size of the transform feedback
48 * buffer, using both a GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query
49 * and by looking at the contents of the buffer itself. We run
50 * transform feedback in GL_TRIANGLES mode and use a buffer size that
51 * is not a multiple of 3, so that we can look at the last element in
52 * the transform feedback buffer and verify that transform feedback
53 * didn't overwrite it.
55 * The test performs the following operations:
57 * 1. Create a transform feedback buffer using glBufferData().
59 * 2. Bind the buffer for transform feedback using either
60 * glBindBufferBase, glBindBufferRange, or glBindBufferOffsetEXT
61 * (if supported).
63 * 3. Change the size of the bound buffer using glBufferData(). A
64 * non-null data pointer is passed to glBufferData() to store a
65 * known pattern in the buffer, so that in step 6 we'll be able to
66 * determine which parts of the buffer were overwritten.
68 * 4. Draw some triangles, feeding back a single float from each
69 * vertex.
71 * 5. Verify, using a GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query,
72 * that the expected number of primitives were written to the
73 * buffer.
75 * 6. Verify, using glMapBuffer, that the expected data was written to
76 * the buffer.
79 #include "piglit-util-gl.h"
81 PIGLIT_GL_TEST_CONFIG_BEGIN
83 config.supports_gl_compat_version = 10;
85 config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA;
86 config.khr_no_error_support = PIGLIT_NO_ERRORS;
88 PIGLIT_GL_TEST_CONFIG_END
90 /**
91 * Maximum buffer size--used for declaraing static arrays. Measured
92 * in multiples of sizeof(GLfloat).
94 #define MAX_BUFFER_SIZE_FLOATS 10
96 static GLuint prog;
97 static GLuint xfb_buf;
98 static GLuint query;
100 const struct test_case
103 * Name of the test case. NULL is used as a sentinel to mark
104 * the end of the list of test cases.
106 const char *name;
109 * Size that the buffer should have before binding. Measured
110 * in multiples of sizeof(GLfloat).
112 unsigned initial_size;
115 * Offset to pass to glBindBufferRange/glBindBufferOffsetEXT,
116 * or zero if glBindBufferBase should be used. Measured in
117 * multiples of sizeof(GLfloat).
119 unsigned bind_offset;
122 * Size to pass to glBindBufferRange, or zero if
123 * glBindBufferOffsetEXT/glBindBufferBase should be used.
124 * Measured in multiples of sizeof(GLfloat).
126 unsigned bind_size;
129 * Size of the buffer that should be passed to
130 * glBindBufferData after the buffer is bound. Measured in
131 * multiples of sizeof(GLfloat).
133 unsigned new_size;
136 * Number of triangles to draw.
138 unsigned num_draw_triangles;
141 * Number of primitives that are expected to be written to the
142 * buffer.
144 unsigned num_feedback_triangles;
145 } test_cases[] = {
146 /* name initial bind bind new num tris:
147 * size offset size size draw feedback */
148 { "base-shrink", 7, 0, 0, 4, 2, 1 },
149 { "base-grow", 4, 0, 0, 7, 2, 2 },
150 { "offset-shrink", 10, 3, 0, 7, 2, 1 },
151 { "offset-grow", 7, 3, 0, 10, 2, 2 },
152 { "range-shrink", 10, 3, 7, 7, 2, 1 },
153 { "range-grow", 7, 3, 4, 10, 2, 1 },
154 { NULL, 0, 0, 0, 0, 0, 0 }
157 const struct test_case *selected_test;
160 * Vertex shader, which simply copies its input attribute to its
161 * output varying, adding 100 in the process.
163 static const char *vstext =
164 "#version 120\n"
165 "attribute float input_value;\n"
166 "varying float output_value;\n"
167 "\n"
168 "void main()\n"
169 "{\n"
170 " gl_Position = vec4(0.0);\n"
171 " output_value = 100.0 + input_value;\n"
172 "}\n";
174 static void
175 print_usage_and_exit(const char *prog_name)
177 unsigned i;
178 printf("Usage: %s <test_case>\n"
179 " where <test_case> is one of the following:\n", prog_name);
180 for (i = 0; test_cases[i].name != NULL; i++)
181 printf(" %s\n", test_cases[i].name);
182 exit(1);
185 static const struct test_case *
186 interpret_test_case_arg(const char *arg)
188 unsigned i;
189 for (i = 0; test_cases[i].name != NULL; i++) {
190 if (strcmp(test_cases[i].name, arg) == 0)
191 return &test_cases[i];
193 return NULL;
196 void
197 piglit_init(int argc, char **argv)
199 const char *varying_name = "output_value";
201 /* Parse args */
202 if (argc != 2)
203 print_usage_and_exit(argv[0]);
204 selected_test = interpret_test_case_arg(argv[1]);
205 if (selected_test == NULL)
206 print_usage_and_exit(argv[0]);
208 /* Make sure required GL features are present */
209 piglit_require_GLSL_version(120);
210 piglit_require_transform_feedback();
211 if (selected_test->bind_offset != 0 && selected_test->bind_size == 0) {
212 /* Test requires glBindBufferOffsetEXT, which is in
213 * EXT_transform_feedback, but was never adopted into
214 * OpenGL.
216 piglit_require_extension("GL_EXT_transform_feedback");
219 /* Create program and buffer */
220 prog = piglit_build_simple_program_unlinked(vstext, NULL);
221 glTransformFeedbackVaryings(prog, 1, &varying_name,
222 GL_INTERLEAVED_ATTRIBS);
223 glLinkProgram(prog);
224 if (!piglit_link_check_status(prog))
225 piglit_report_result(PIGLIT_FAIL);
226 glGenBuffers(1, &xfb_buf);
227 glGenQueries(1, &query);
228 if (!piglit_check_gl_error(GL_NO_ERROR))
229 piglit_report_result(PIGLIT_FAIL);
232 enum piglit_result
233 piglit_display(void)
235 GLint input_index = glGetAttribLocation(prog, "input_value");
236 GLfloat canary_data[MAX_BUFFER_SIZE_FLOATS];
237 GLfloat input_data[MAX_BUFFER_SIZE_FLOATS];
238 GLfloat expected_data[MAX_BUFFER_SIZE_FLOATS];
239 GLfloat *output_data;
240 GLuint query_result;
241 GLboolean pass = GL_TRUE;
242 unsigned i;
244 glUseProgram(prog);
246 /* Create a transform feedback buffer. */
247 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfb_buf);
248 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
249 selected_test->initial_size * sizeof(GLfloat), NULL,
250 GL_STREAM_READ);
252 /* Bind the buffer for transform feedback. */
253 if (selected_test->bind_size != 0) {
254 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfb_buf,
255 selected_test->bind_offset * sizeof(GLfloat),
256 selected_test->bind_size * sizeof(GLfloat));
257 } else if (selected_test->bind_offset != 0) {
258 glBindBufferOffsetEXT(GL_TRANSFORM_FEEDBACK_BUFFER, 0,
259 xfb_buf,
260 selected_test->bind_offset
261 * sizeof(GLfloat));
262 } else {
263 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfb_buf);
266 /* Change the size of the bound buffer. */
267 for (i = 0; i < MAX_BUFFER_SIZE_FLOATS; i++)
268 canary_data[i] = -1;
269 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
270 selected_test->new_size * sizeof(GLfloat), canary_data,
271 GL_STREAM_READ);
273 /* Draw some triangles. */
274 for (i = 0; i < MAX_BUFFER_SIZE_FLOATS; i++)
275 input_data[i] = i + 1;
276 glBindBuffer(GL_ARRAY_BUFFER, 0);
277 glVertexAttribPointer(input_index, 1, GL_FLOAT, GL_FALSE,
278 sizeof(GLfloat), input_data);
279 glEnableVertexAttribArray(input_index);
280 glBeginTransformFeedback(GL_TRIANGLES);
281 glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query);
282 glDrawArrays(GL_TRIANGLES, 0, selected_test->num_draw_triangles * 3);
283 glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
284 glEndTransformFeedback();
286 /* Verify that the expected number of primitives were
287 * written.
289 glGetQueryObjectuiv(query, GL_QUERY_RESULT, &query_result);
290 printf("PRIMITIVES_WRITTEN: expected=%u, actual=%u\n",
291 selected_test->num_feedback_triangles, query_result);
292 if (query_result != selected_test->num_feedback_triangles)
293 pass = GL_FALSE;
295 /* Verify that the expected data was written. */
296 for (i = 0; i < selected_test->new_size; i++) {
297 if (i >= selected_test->bind_offset &&
298 i < (3 * selected_test->num_feedback_triangles
299 + selected_test->bind_offset)) {
300 expected_data[i] = 100.0
301 + input_data[i - selected_test->bind_offset];
302 } else {
303 expected_data[i] = canary_data[i];
306 output_data = glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
307 for (i = 0; i < selected_test->new_size; ++i) {
308 printf("data[%u]: expected=%f, actual=%f\n", i,
309 expected_data[i], output_data[i]);
310 if (expected_data[i] != output_data[i])
311 pass = GL_FALSE;
313 glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
315 piglit_present_results();
317 return pass ? PIGLIT_PASS : PIGLIT_FAIL;