present-race.c revision fe8aea9e
1/*
2 * Copyright (c) 2014 Intel Corporation
3 *
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:
10 *
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.
14 *
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <X11/Xlib.h>
30#include <X11/Xlib-xcb.h>
31#include <X11/xshmfence.h>
32#include <X11/Xutil.h>
33#include <X11/Xlibint.h>
34#include <X11/extensions/dpms.h>
35#include <X11/extensions/randr.h>
36#include <X11/extensions/Xcomposite.h>
37#include <X11/extensions/Xrandr.h>
38#include <X11/extensions/Xrender.h>
39#include <X11/extensions/XShm.h>
40#if HAVE_X11_EXTENSIONS_SHMPROTO_H
41#include <X11/extensions/shmproto.h>
42#elif HAVE_X11_EXTENSIONS_SHMSTR_H
43#include <X11/extensions/shmstr.h>
44#else
45#error Failed to find the right header for X11 MIT-SHM protocol definitions
46#endif
47#include <xcb/xcb.h>
48#include <xcb/present.h>
49#include <xcb/xfixes.h>
50#include <xcb/dri3.h>
51#include <xf86drm.h>
52#include <i915_drm.h>
53
54#include <stdio.h>
55#include <string.h>
56#include <fcntl.h>
57#include <unistd.h>
58#include <assert.h>
59#include <errno.h>
60#include <setjmp.h>
61#include <signal.h>
62
63#include <sys/mman.h>
64#include <sys/ipc.h>
65#include <sys/shm.h>
66#include <pciaccess.h>
67
68#include "dri3.h"
69
70static int _x_error_occurred;
71static uint32_t stamp;
72
73static int
74_check_error_handler(Display     *display,
75		     XErrorEvent *event)
76{
77	printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
78	       DisplayString(display),
79	       event->serial,
80	       event->error_code,
81	       event->request_code,
82	       event->minor_code);
83	_x_error_occurred++;
84	return False; /* ignored */
85}
86
87static int has_composite(Display *dpy)
88{
89	int event, error;
90	int major, minor;
91
92	if (!XCompositeQueryExtension(dpy, &event, &error))
93		return 0;
94
95	XCompositeQueryVersion(dpy, &major, &minor);
96
97	return major > 0 || minor >= 4;
98}
99
100static void *setup_msc(Display *dpy, Window win)
101{
102	xcb_connection_t *c = XGetXCBConnection(dpy);
103	xcb_void_cookie_t cookie;
104	uint32_t id = xcb_generate_id(c);
105	xcb_generic_error_t *error;
106	void *q;
107
108	cookie = xcb_present_select_input_checked(c, id, win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
109	q = xcb_register_for_special_xge(c, &xcb_present_id, id, &stamp);
110
111	error = xcb_request_check(c, cookie);
112	assert(error == NULL);
113
114	return q;
115}
116
117static void teardown_msc(Display *dpy, void *q)
118{
119	xcb_unregister_for_special_event(XGetXCBConnection(dpy), q);
120}
121
122static uint64_t wait_vblank(Display *dpy, Window win)
123{
124	xcb_connection_t *c = XGetXCBConnection(dpy);
125	static uint32_t serial = 1;
126	uint64_t msc = 0;
127	int complete = 0;
128	void *q;
129
130	if (win == 0)
131		win = DefaultRootWindow(dpy);
132
133	q = setup_msc(dpy, win);
134
135	xcb_present_notify_msc(c, win, serial ^ 0xdeadbeef, 0, 1, 0);
136	xcb_flush(c);
137
138	do {
139		xcb_present_complete_notify_event_t *ce;
140		xcb_generic_event_t *ev;
141
142		ev = xcb_wait_for_special_event(c, q);
143		if (ev == NULL)
144			break;
145
146		ce = (xcb_present_complete_notify_event_t *)ev;
147		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
148		    ce->serial == (serial ^ 0xdeadbeef)) {
149			msc = ce->msc;
150			complete = 1;
151		}
152		free(ev);
153	} while (!complete);
154
155	if (++serial == 0)
156		serial = 1;
157
158	teardown_msc(dpy, q);
159
160	return msc;
161}
162
163static int test_basic(Display *dpy, int dummy)
164{
165	xcb_connection_t *c = XGetXCBConnection(dpy);
166	XSetWindowAttributes attr;
167	Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
168	Pixmap pixmap;
169	struct dri3_fence fence;
170	Window root, win;
171	unsigned int width, height;
172	unsigned border, depth;
173	int x, y, ret = 1;
174	const char *phase;
175	uint64_t msc;
176
177	root = DefaultRootWindow(dpy);
178	XGetGeometry(dpy, root,
179		     &win, &x, &y,
180		     &width, &height, &border, &depth);
181
182	_x_error_occurred = 0;
183	attr.override_redirect = 1;
184	switch (dummy) {
185	case 0:
186		win = root;
187		phase = "root";
188		break;
189	case 1:
190		win = XCreateWindow(dpy, root,
191				    0, 0, width, height, 0, depth,
192				    InputOutput, visual,
193				    CWOverrideRedirect, &attr);
194		phase = "fullscreen";
195		break;
196	case 2:
197		width /= 2;
198		height /= 2;
199		win = XCreateWindow(dpy, root,
200				    0, 0, width, height, 0, depth,
201				    InputOutput, visual,
202				    CWOverrideRedirect, &attr);
203		phase = "window";
204		break;
205	case 3:
206		if (!has_composite(dpy))
207			return 0;
208
209		win = XCreateWindow(dpy, root,
210				    0, 0, width, height, 0,
211				    DefaultDepth(dpy, DefaultScreen(dpy)),
212				    InputOutput,
213				    DefaultVisual(dpy, DefaultScreen(dpy)),
214				    CWOverrideRedirect, &attr);
215		XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
216		phase = "composite";
217		break;
218
219	default:
220		phase = "broken";
221		win = root;
222		abort();
223		break;
224	}
225
226	XMapWindow(dpy, win);
227	XSync(dpy, True);
228	if (_x_error_occurred)
229		return 1;
230
231	if (dri3_create_fence(dpy, win, &fence))
232		return 0;
233
234	printf("%s: Testing basic flip: %dx%d\n", phase, width, height);
235	fflush(stdout);
236	_x_error_occurred = 0;
237
238	xshmfence_reset(fence.addr);
239	msc = wait_vblank(dpy, win);
240
241	pixmap = XCreatePixmap(dpy, win, width, height, depth);
242	xcb_present_pixmap(c, win, pixmap, 0,
243			   0, /* valid */
244			   0, /* update */
245			   0, /* x_off */
246			   0, /* y_off */
247			   None,
248			   None, /* wait fence */
249			   fence.xid,
250			   XCB_PRESENT_OPTION_NONE,
251			   (msc + 64) & -64, /* target msc */
252			   64, /* divisor */
253			   32, /* remainder */
254			   0, NULL);
255	XFreePixmap(dpy, pixmap);
256
257	pixmap = XCreatePixmap(dpy, win, width, height, depth);
258	xcb_present_pixmap(c, win, pixmap, 0,
259			   0, /* valid */
260			   0, /* update */
261			   0, /* x_off */
262			   0, /* y_off */
263			   None,
264			   None, /* wait fence */
265			   None, /* sync fence */
266			   XCB_PRESENT_OPTION_NONE,
267			   (msc + 64) & -64, /* target msc */
268			   64, /* divisor */
269			   48, /* remainder */
270			   0, NULL);
271	XFreePixmap(dpy, pixmap);
272	XDestroyWindow(dpy, win);
273	XFlush(dpy);
274
275	ret = !!xshmfence_await(fence.addr);
276	dri3_fence_free(dpy, &fence);
277
278	XSync(dpy, True);
279	ret += !!_x_error_occurred;
280
281	return ret;
282}
283
284static int test_race(Display *dpy, int dummy)
285{
286	Display *mgr = XOpenDisplay(NULL);
287	xcb_connection_t *c = XGetXCBConnection(dpy);
288	XSetWindowAttributes attr;
289	Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
290	Pixmap pixmap;
291	struct dri3_fence fence;
292	Window root, win;
293	unsigned int width, height;
294	unsigned border, depth;
295	int x, y, ret = 1;
296	const char *phase;
297	uint64_t msc;
298
299	root = DefaultRootWindow(dpy);
300	XGetGeometry(dpy, root,
301		     &win, &x, &y,
302		     &width, &height, &border, &depth);
303
304	_x_error_occurred = 0;
305	attr.override_redirect = 1;
306	switch (dummy) {
307	case 0:
308		win = root;
309		phase = "root";
310		break;
311	case 1:
312		win = XCreateWindow(dpy, root,
313				    0, 0, width, height, 0, depth,
314				    InputOutput, visual,
315				    CWOverrideRedirect, &attr);
316		phase = "fullscreen";
317		break;
318	case 2:
319		width /= 2;
320		height /= 2;
321		win = XCreateWindow(dpy, root,
322				    0, 0, width, height, 0, depth,
323				    InputOutput, visual,
324				    CWOverrideRedirect, &attr);
325		phase = "window";
326		break;
327	case 3:
328		if (!has_composite(dpy))
329			return 0;
330
331		win = XCreateWindow(dpy, root,
332				    0, 0, width, height, 0,
333				    DefaultDepth(dpy, DefaultScreen(dpy)),
334				    InputOutput,
335				    DefaultVisual(dpy, DefaultScreen(dpy)),
336				    CWOverrideRedirect, &attr);
337		XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
338		phase = "composite";
339		break;
340
341	default:
342		phase = "broken";
343		win = root;
344		abort();
345		break;
346	}
347
348	XMapWindow(dpy, win);
349	XSync(dpy, True);
350	if (_x_error_occurred)
351		return 1;
352
353	if (dri3_create_fence(dpy, win, &fence))
354		return 0;
355
356	printf("%s: Testing race with manager: %dx%d\n", phase, width, height);
357	fflush(stdout);
358	_x_error_occurred = 0;
359
360	xshmfence_reset(fence.addr);
361	msc = wait_vblank(dpy, win);
362
363	pixmap = XCreatePixmap(dpy, win, width, height, depth);
364	xcb_present_pixmap(c, win, pixmap, 0,
365			   0, /* valid */
366			   0, /* update */
367			   0, /* x_off */
368			   0, /* y_off */
369			   None,
370			   None, /* wait fence */
371			   fence.xid,
372			   XCB_PRESENT_OPTION_NONE,
373			   (msc + 64) & -64, /* target msc */
374			   64, /* divisor */
375			   32, /* remainder */
376			   0, NULL);
377	XFreePixmap(dpy, pixmap);
378
379	XFlush(dpy);
380	XDestroyWindow(mgr, win);
381	XFlush(mgr);
382
383	pixmap = XCreatePixmap(dpy, win, width, height, depth);
384	xcb_present_pixmap(c, win, pixmap, 0,
385			   0, /* valid */
386			   0, /* update */
387			   0, /* x_off */
388			   0, /* y_off */
389			   None,
390			   None, /* wait fence */
391			   None, /* sync fence */
392			   XCB_PRESENT_OPTION_NONE,
393			   (msc + 64) & -64, /* target msc */
394			   64, /* divisor */
395			   48, /* remainder */
396			   0, NULL);
397	XFreePixmap(dpy, pixmap);
398	XFlush(dpy);
399
400	ret = !!xshmfence_await(fence.addr);
401	dri3_fence_free(dpy, &fence);
402
403	XSync(dpy, True);
404	ret += !!_x_error_occurred;
405
406	XCloseDisplay(mgr);
407
408	return ret;
409}
410
411static int has_present(Display *dpy)
412{
413	xcb_connection_t *c = XGetXCBConnection(dpy);
414	xcb_generic_error_t *error = NULL;
415	void *reply;
416
417	reply = xcb_xfixes_query_version_reply(c,
418					       xcb_xfixes_query_version(c,
419									XCB_XFIXES_MAJOR_VERSION,
420									XCB_XFIXES_MINOR_VERSION),
421					       &error);
422	free(reply);
423	free(error);
424	if (reply == NULL) {
425		fprintf(stderr, "XFixes not supported on %s\n", DisplayString(dpy));
426		return 0;
427	}
428
429	reply = xcb_dri3_query_version_reply(c,
430					     xcb_dri3_query_version(c,
431								    XCB_DRI3_MAJOR_VERSION,
432								    XCB_DRI3_MINOR_VERSION),
433					     &error);
434	free(reply);
435	free(error);
436	if (reply == NULL) {
437		fprintf(stderr, "DRI3 not supported on %s\n", DisplayString(dpy));
438		return 0;
439	}
440
441	reply = xcb_present_query_version_reply(c,
442						xcb_present_query_version(c,
443									  XCB_PRESENT_MAJOR_VERSION,
444									  XCB_PRESENT_MINOR_VERSION),
445						&error);
446
447	free(reply);
448	free(error);
449	if (reply == NULL) {
450		fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy));
451		return 0;
452	}
453
454	return 1;
455}
456
457int main(void)
458{
459	Display *dpy;
460	int dummy;
461	int error = 0;
462
463	dpy = XOpenDisplay(NULL);
464	if (dpy == NULL)
465		return 77;
466
467	if (!has_present(dpy))
468		return 77;
469
470	if (DPMSQueryExtension(dpy, &dummy, &dummy))
471		DPMSDisable(dpy);
472
473	signal(SIGALRM, SIG_IGN);
474	XSetErrorHandler(_check_error_handler);
475
476	for (dummy = 0; dummy <= 3; dummy++) {
477		error += test_basic(dpy, dummy);
478		error += test_race(dpy, dummy);
479	}
480
481	if (DPMSQueryExtension(dpy, &dummy, &dummy))
482		DPMSEnable(dpy);
483	return !!error;
484}
485