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
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.
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
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
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
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
71 * 5. Verify, using a GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query,
72 * that the expected number of primitives were written to the
75 * 6. Verify, using glMapBuffer, that the expected data was written to
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
91 * Maximum buffer size--used for declaraing static arrays. Measured
92 * in multiples of sizeof(GLfloat).
94 #define MAX_BUFFER_SIZE_FLOATS 10
97 static GLuint xfb_buf
;
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.
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).
129 * Size of the buffer that should be passed to
130 * glBindBufferData after the buffer is bound. Measured in
131 * multiples of sizeof(GLfloat).
136 * Number of triangles to draw.
138 unsigned num_draw_triangles
;
141 * Number of primitives that are expected to be written to the
144 unsigned num_feedback_triangles
;
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
=
165 "attribute float input_value;\n"
166 "varying float output_value;\n"
170 " gl_Position = vec4(0.0);\n"
171 " output_value = 100.0 + input_value;\n"
175 print_usage_and_exit(const char *prog_name
)
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
);
185 static const struct test_case
*
186 interpret_test_case_arg(const char *arg
)
189 for (i
= 0; test_cases
[i
].name
!= NULL
; i
++) {
190 if (strcmp(test_cases
[i
].name
, arg
) == 0)
191 return &test_cases
[i
];
197 piglit_init(int argc
, char **argv
)
199 const char *varying_name
= "output_value";
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
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
);
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
);
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
;
241 GLboolean pass
= GL_TRUE
;
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
,
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,
260 selected_test
->bind_offset
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
++)
269 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER
,
270 selected_test
->new_size
* sizeof(GLfloat
), canary_data
,
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
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
)
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
];
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
])
313 glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER
);
315 piglit_present_results();
317 return pass
? PIGLIT_PASS
: PIGLIT_FAIL
;