2 * Copyright © 2014 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 DEALINGS
24 * Neil Roberts <neil@linux.intel.com>
28 /** @file glx-context-flush-control.c
30 * Tests the GLX_ARB_context_flush_control extension. It takes the
31 * following steps using two threads. The threads are only used so it
32 * can operate on another context without having to rebind it. The
33 * threads are run lock-step so that each step is run sequentially.
35 * Thread 1: Make a flushless context A
36 * Thread 1: Make a flushy context B, shared with A
37 * Thread 1: Make a flushy context C, shared with A
38 * Thread 1: Bind context A
39 * Thread 2: Bind context C
40 * Thread 1: Make a renderbuffer.
41 * Thread 1: glClear() it to green.
42 * Thread 1: glFinish()
43 * Thread 1: glClear() it to red.
44 * Thread 2: Do a glReadPixels()
46 * (At this point the GL implementation is allowed to have finished
47 * the clear to red but it probably won't have. If the read pixels
48 * returns green here then it's not a failure but the test won't work
49 * so it will report PIGLIT_SKIP)
51 * Thread 1: Bind context C
53 * Thread 2: Make sure glReadPixels() is still green, otherwise fail.
55 * All of the steps are then run again but this time context A is made
56 * flushy and the last step ensures that the pixel becomes red instead
57 * of green. If it did become red then the GL successfully made a
58 * flush when context A was released.
60 * The test also verifies that calling glGetIntegerv with
61 * GL_CONTEXT_RELEASE_BEHAVIOR returns the expected value when setting
62 * the attribute to none and flush and also when the attribute is left
68 #include "piglit-util-gl.h"
69 #include "piglit-glx-util.h"
72 #ifndef GLX_ARB_context_flush_control
73 #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
74 #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
75 #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
78 #ifndef GL_CONTEXT_RELEASE_BEHAVIOR
79 #define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB
80 #define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC
83 static PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribs
= NULL
;
85 enum release_behavior
{
100 struct window
*window
;
103 pthread_mutex_t mutex
;
107 void (* running_func
)(struct thread_data
*data
);
111 enum piglit_result result
;
114 static float red
[] = { 1.0f
, 0.0f
, 0.0f
};
115 static float green
[] = { 0.0f
, 1.0f
, 0.0f
};
118 create_context(Display
*display
,
119 struct window
*window
,
120 GLXContext share_ctx
,
121 enum release_behavior release_behavior
)
123 GLint actual_release_behavior
;
125 int ctx_attribs
[7] = {
126 GLX_CONTEXT_MAJOR_VERSION_ARB
, 1,
127 GLX_CONTEXT_MINOR_VERSION_ARB
, 5,
128 GLX_CONTEXT_RELEASE_BEHAVIOR_ARB
,
133 switch (release_behavior
) {
135 ctx_attribs
[5] = GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB
;
138 ctx_attribs
[5] = GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB
;
140 case RB_NOT_SPECIFIED
:
145 ctx
= CreateContextAttribs(display
, window
->config
,
151 glXMakeCurrent(display
, window
->glx_window
, ctx
);
153 piglit_dispatch_default_init(PIGLIT_DISPATCH_GL
);
155 piglit_require_extension("GL_KHR_context_flush_control");
157 glGetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR
, &actual_release_behavior
);
159 switch (release_behavior
) {
161 case RB_NOT_SPECIFIED
:
162 assert(actual_release_behavior
==
163 GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH
);
166 assert(actual_release_behavior
== GL_NONE
);
176 create_window(Display
*display
,
177 struct window
*window
)
179 window
->xvi
= piglit_get_glx_visual(display
);
181 piglit_glx_get_fbconfig_for_visinfo(display
, window
->xvi
);
183 window
->window
= piglit_get_glx_window(display
, window
->xvi
);
184 window
->glx_window
= glXCreateWindow(display
, window
->config
,
185 window
->window
, NULL
);
189 thread_func(void *user_data
)
191 struct thread_data
*data
= user_data
;
193 void (* running_func
)(struct thread_data
*data
);
196 /* Wait for something to do */
197 pthread_mutex_lock(&data
->mutex
);
198 while (!data
->quit
&& data
->running_func
== NULL
)
199 pthread_cond_wait(&data
->cond
, &data
->mutex
);
201 running_func
= data
->running_func
;
202 pthread_mutex_unlock(&data
->mutex
);
209 pthread_mutex_lock(&data
->mutex
);
210 data
->running_func
= NULL
;
211 pthread_cond_signal(&data
->cond
);
212 pthread_mutex_unlock(&data
->mutex
);
219 run_in_thread(struct thread_data
*data
,
220 void (* func
)(struct thread_data
*data
))
222 pthread_mutex_lock(&data
->mutex
);
224 /* Tell the thread about the function */
225 data
->running_func
= func
;
226 pthread_cond_signal(&data
->cond
);
228 /* Wait for it to complete */
230 pthread_cond_wait(&data
->cond
, &data
->mutex
);
231 while (data
->running_func
);
233 pthread_mutex_unlock(&data
->mutex
);
235 if (data
->result
!= PIGLIT_PASS
)
236 piglit_report_result(data
->result
);
240 bind_context(struct thread_data
*data
)
242 glXMakeCurrent(data
->display
, data
->window
->glx_window
, data
->context
);
246 unbind_context(struct thread_data
*data
)
248 glXMakeCurrent(data
->display
, None
, NULL
);
252 check_green(struct thread_data
*data
)
254 glBindFramebuffer(GL_FRAMEBUFFER
, data
->fbo
);
256 /* At this point the main thread has flushed a clear to green
257 * and queued a clear to red without flushing. It would be
258 * valid for the framebuffer to be red here but in that case
259 * the test won't work so we will skip the test */
260 if (!piglit_probe_pixel_rgb_silent(0, 0, green
, NULL
)) {
261 printf("Either the clear to green command was not completed "
262 "or the clear to red command was flushed too early so "
263 "the test will be skipped\n");
264 data
->result
= PIGLIT_SKIP
;
267 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
271 check_still_green(struct thread_data
*data
)
275 glBindFramebuffer(GL_FRAMEBUFFER
, data
->fbo
);
277 /* The pixel should still be green even though the main thread
278 * has released the original context because it shouldn't
280 if (!piglit_probe_pixel_rgb_silent(0, 0, green
, NULL
)) {
281 if (piglit_probe_pixel_rgb_silent(0, 0, red
, probe
)) {
282 printf("The renderbuffer contains a red pixel which "
283 "means that releasing the first context has "
284 "caused a flush.\n");
286 printf("Expected green\n"
287 "Observed: %f %f %f\n",
288 probe
[0], probe
[1], probe
[2]);
291 data
->result
= PIGLIT_FAIL
;
294 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
298 check_changed_to_red(struct thread_data
*data
)
302 glBindFramebuffer(GL_FRAMEBUFFER
, data
->fbo
);
304 /* Releasing the original context should have caused a flush
305 * so the framebuffer should have become red */
306 if (!piglit_probe_pixel_rgb_silent(0, 0, red
, NULL
)) {
307 if (piglit_probe_pixel_rgb_silent(0, 0, green
, probe
)) {
308 printf("The renderbuffer contains a green pixel which "
309 "means that releasing the first context has not "
310 "caused a flush.\n");
312 printf("Expected red\n"
313 "Observed: %f %f %f\n",
314 probe
[0], probe
[1], probe
[2]);
317 data
->result
= PIGLIT_FAIL
;
320 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
324 do_test_flush(Display
*display
,
325 struct window
*window
,
326 GLXContext context_b
,
327 struct thread_data
*thread_data
,
328 enum release_behavior release_behavior
)
333 glGenRenderbuffers(1, &rb
);
334 glBindRenderbuffer(GL_RENDERBUFFER
, rb
);
335 glRenderbufferStorage(GL_RENDERBUFFER
, GL_RGB
, 1, 1);
337 glGenFramebuffers(1, &fbo
);
338 glBindFramebuffer(GL_FRAMEBUFFER
, fbo
);
339 glFramebufferRenderbuffer(GL_FRAMEBUFFER
,
340 GL_COLOR_ATTACHMENT0
,
344 status
= glCheckFramebufferStatus(GL_FRAMEBUFFER
);
345 if (status
!= GL_FRAMEBUFFER_COMPLETE
) {
346 printf("failed to create a 1x1 GL_RGB renderbuffer\n");
347 piglit_report_result(PIGLIT_SKIP
);
350 thread_data
->fbo
= fbo
;
352 /* Clear the framebuffer to green */
353 glClearColor(0.0f
, 1.0f
, 0.0f
, 1.0f
);
354 glClear(GL_COLOR_BUFFER_BIT
);
355 /* Make sure the color actually hits the framebuffer */
358 /* Post a command to clear it to red without flushing */
359 glClearColor(1.0f
, 0.0f
, 0.0f
, 1.0f
);
360 glClear(GL_COLOR_BUFFER_BIT
);
362 run_in_thread(thread_data
, check_green
);
364 /* Switch to the other context. This shouldn't cause a flush
365 * if the release behavior is RB_NONE */
366 glXMakeCurrent(display
, window
->glx_window
, context_b
);
368 /* Give the GPU some time to finish rendering */
371 if (release_behavior
== RB_NONE
) {
372 /* Verify that it didn't cause a flush */
373 run_in_thread(thread_data
, check_still_green
);
375 /* Make sure it did cause a flush */
376 run_in_thread(thread_data
, check_changed_to_red
);
379 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
380 glBindFramebuffer(GL_FRAMEBUFFER
, 0);
381 glDeleteRenderbuffers(1, &rb
);
382 glDeleteFramebuffers(1, &fbo
);
386 test_flush(Display
*display
,
387 struct window
*window
,
388 enum release_behavior release_behavior
)
390 GLXContext context_a
, context_b
, context_c
;
391 struct thread_data thread_data
;
394 /* Create three contexts with each of the three possible
395 * values for the release behavior. This also verifies that
396 * the GL extension returns the right value for each attribute
397 * value from glGetIntegerv. We only need one context without
398 * flushing and the value for the others doesn't really
400 context_a
= create_context(display
, window
, NULL
, release_behavior
);
401 piglit_require_extension("GL_EXT_framebuffer_object");
402 context_b
= create_context(display
, window
, context_a
, RB_FLUSH
);
403 piglit_require_extension("GL_EXT_framebuffer_object");
404 context_c
= create_context(display
, window
,
405 context_a
, RB_NOT_SPECIFIED
);
406 piglit_require_extension("GL_EXT_framebuffer_object");
408 thread_data
.display
= display
;
409 thread_data
.window
= window
;
410 thread_data
.context
= context_c
;
412 pthread_mutex_init(&thread_data
.mutex
, NULL
);
413 pthread_cond_init(&thread_data
.cond
, NULL
);
415 thread_data
.quit
= false;
416 thread_data
.running_func
= NULL
;
417 thread_data
.result
= PIGLIT_PASS
;
419 pthread_create(&thread
, NULL
, thread_func
, &thread_data
);
421 glXMakeCurrent(display
, window
->glx_window
, context_a
);
423 run_in_thread(&thread_data
, bind_context
);
425 do_test_flush(display
, window
, context_b
,
426 &thread_data
, release_behavior
);
428 run_in_thread(&thread_data
, unbind_context
);
430 pthread_mutex_lock(&thread_data
.mutex
);
431 thread_data
.quit
= true;
432 pthread_cond_signal(&thread_data
.cond
);
433 pthread_mutex_unlock(&thread_data
.mutex
);
435 pthread_join(thread
, NULL
);
437 pthread_cond_destroy(&thread_data
.cond
);
438 pthread_mutex_destroy(&thread_data
.mutex
);
440 glXDestroyContext(display
, context_c
);
441 glXDestroyContext(display
, context_b
);
442 glXDestroyContext(display
, context_a
);
446 destroy_window(Display
*display
,
447 struct window
*window
)
449 glXDestroyWindow(display
, window
->glx_window
);
450 XDestroyWindow(display
, window
->window
);
454 main(int argc
, char **argv
)
457 struct window window
;
460 for (int i
= 1; i
< argc
; i
++) {
461 if (!strcmp(argv
[i
], "-auto"))
462 piglit_automatic
= 1;
464 fprintf(stderr
, "Unknown option: %s\n", argv
[i
]);
467 display
= piglit_get_glx_display();
469 piglit_require_glx_extension(display
, "GLX_ARB_get_proc_address");
470 piglit_require_glx_extension(display
, "GLX_ARB_create_context");
471 piglit_require_glx_extension(display
, "GLX_ARB_context_flush_control");
473 CreateContextAttribs
= (PFNGLXCREATECONTEXTATTRIBSARBPROC
)
474 glXGetProcAddressARB((GLubyte
*) "glXCreateContextAttribsARB");
476 create_window(display
, &window
);
478 test_flush(display
, &window
, RB_NONE
);
479 test_flush(display
, &window
, RB_FLUSH
);
481 destroy_window(display
, &window
);
483 piglit_report_result(pass
? PIGLIT_PASS
: PIGLIT_FAIL
);