cl: Don't use device_infos if num_device_infos == 0
[piglit.git] / tests / glx / glx-context-flush-control.c
blob9b171c1526b9e39fbc3e4df67b259c64553b553c
1 /*
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
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 DEALINGS
21 * IN THE SOFTWARE.
23 * Authors:
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
52 * Thread 1: sleep(.5)
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
63 * out entirely.
66 #include <unistd.h>
68 #include "piglit-util-gl.h"
69 #include "piglit-glx-util.h"
70 #include "pthread.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
76 #endif
78 #ifndef GL_CONTEXT_RELEASE_BEHAVIOR
79 #define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB
80 #define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC
81 #endif
83 static PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribs = NULL;
85 enum release_behavior {
86 RB_NONE,
87 RB_FLUSH,
88 RB_NOT_SPECIFIED
91 struct window {
92 GLXFBConfig config;
93 XVisualInfo *xvi;
94 Window window;
95 GLXWindow glx_window;
98 struct thread_data {
99 Display *display;
100 struct window *window;
101 GLXContext context;
103 pthread_mutex_t mutex;
104 pthread_cond_t cond;
106 bool quit;
107 void (* running_func)(struct thread_data *data);
109 GLuint fbo;
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 };
117 GLXContext
118 create_context(Display *display,
119 struct window *window,
120 GLXContext share_ctx,
121 enum release_behavior release_behavior)
123 GLint actual_release_behavior;
124 GLXContext ctx;
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,
129 release_behavior,
133 switch (release_behavior) {
134 case RB_NONE:
135 ctx_attribs[5] = GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB;
136 break;
137 case RB_FLUSH:
138 ctx_attribs[5] = GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB;
139 break;
140 case RB_NOT_SPECIFIED:
141 ctx_attribs[4] = 0;
142 break;
145 ctx = CreateContextAttribs(display, window->config,
146 share_ctx, True,
147 ctx_attribs);
149 assert(ctx);
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) {
160 case RB_FLUSH:
161 case RB_NOT_SPECIFIED:
162 assert(actual_release_behavior ==
163 GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH);
164 break;
165 case RB_NONE:
166 assert(actual_release_behavior == GL_NONE);
167 break;
168 default:
169 assert(0);
172 return ctx;
175 static void
176 create_window(Display *display,
177 struct window *window)
179 window->xvi = piglit_get_glx_visual(display);
180 window->config =
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);
188 static void *
189 thread_func(void *user_data)
191 struct thread_data *data = user_data;
192 bool quit;
193 void (* running_func)(struct thread_data *data);
195 while (true) {
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);
200 quit = data->quit;
201 running_func = data->running_func;
202 pthread_mutex_unlock(&data->mutex);
204 if (quit)
205 break;
207 running_func(data);
209 pthread_mutex_lock(&data->mutex);
210 data->running_func = NULL;
211 pthread_cond_signal(&data->cond);
212 pthread_mutex_unlock(&data->mutex);
215 return NULL;
218 static void
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);
239 static void
240 bind_context(struct thread_data *data)
242 glXMakeCurrent(data->display, data->window->glx_window, data->context);
245 static void
246 unbind_context(struct thread_data *data)
248 glXMakeCurrent(data->display, None, NULL);
251 static void
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);
270 static void
271 check_still_green(struct thread_data *data)
273 float probe[4];
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
279 * cause a flush */
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");
285 } else {
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);
297 static void
298 check_changed_to_red(struct thread_data *data)
300 float probe[4];
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");
311 } else {
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);
323 static void
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)
330 GLenum status;
331 GLuint fbo, rb;
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,
341 GL_RENDERBUFFER,
342 rb);
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 */
356 glFinish();
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 */
369 usleep(500000);
371 if (release_behavior == RB_NONE) {
372 /* Verify that it didn't cause a flush */
373 run_in_thread(thread_data, check_still_green);
374 } else {
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);
385 static void
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;
392 pthread_t thread;
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
399 * matter */
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);
445 static void
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)
456 Display *display;
457 struct window window;
458 bool pass = true;
460 for (int i = 1; i < argc; i++) {
461 if (!strcmp(argv[i], "-auto"))
462 piglit_automatic = 1;
463 else
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);
485 return 0;