present-test.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
70#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
71#define PAGE_ALIGN(x) ALIGN(x, 4096)
72
73#define GTT I915_GEM_DOMAIN_GTT
74#define CPU I915_GEM_DOMAIN_CPU
75
76static int _x_error_occurred;
77static uint32_t stamp;
78
79static int
80_check_error_handler(Display     *display,
81		     XErrorEvent *event)
82{
83	printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
84	       DisplayString(display),
85	       event->serial,
86	       event->error_code,
87	       event->request_code,
88	       event->minor_code);
89	_x_error_occurred++;
90	return False; /* ignored */
91}
92
93static int is_i915_device(int fd)
94{
95	drm_version_t version;
96	char name[5] = "";
97
98	memset(&version, 0, sizeof(version));
99	version.name_len = 4;
100	version.name = name;
101
102	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
103		return 0;
104
105	return strcmp("i915", name) == 0;
106}
107
108static int is_intel(int fd)
109{
110	struct drm_i915_getparam gp;
111	int ret;
112
113	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
114	ret = is_i915_device(fd);
115	if (ret) {
116		gp.param = I915_PARAM_HAS_GEM;
117		gp.value = &ret;
118		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
119			ret = 0;
120	}
121	return ret;
122}
123
124static void *setup_msc(Display *dpy,  Window win)
125{
126	xcb_connection_t *c = XGetXCBConnection(dpy);
127	xcb_void_cookie_t cookie;
128	uint32_t id = xcb_generate_id(c);
129	xcb_generic_error_t *error;
130	void *q;
131
132	cookie = xcb_present_select_input_checked(c, id, win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
133	q = xcb_register_for_special_xge(c, &xcb_present_id, id, &stamp);
134
135	error = xcb_request_check(c, cookie);
136	assert(error == NULL);
137
138	return q;
139}
140
141static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc, uint64_t *ust)
142{
143	xcb_connection_t *c = XGetXCBConnection(dpy);
144	static uint32_t serial = 1;
145	uint64_t msc = 0;
146	int complete = 0;
147
148	xcb_present_notify_msc(c, win, serial ^ 0xcc00ffee, 0, 0, 0);
149	xcb_flush(c);
150
151	do {
152		xcb_present_complete_notify_event_t *ce;
153		xcb_generic_event_t *ev;
154
155		ev = xcb_wait_for_special_event(c, q);
156		if (ev == NULL)
157			break;
158
159		ce = (xcb_present_complete_notify_event_t *)ev;
160		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
161		    ce->serial == (serial ^ 0xcc00ffee)) {
162			msc = ce->msc;
163			if (ust)
164				*ust = ce->ust;
165			complete = 1;
166		}
167		free(ev);
168	} while (!complete);
169
170	if ((int64_t)(msc - last_msc) < 0) {
171		printf("Invalid MSC: was %llu, now %llu\n",
172		       (long long)last_msc, (long long)msc);
173	}
174
175	if (++serial == 0)
176		serial = 1;
177
178	return msc;
179}
180
181static uint64_t wait_vblank(Display *dpy, Window win, void *q)
182{
183	xcb_connection_t *c = XGetXCBConnection(dpy);
184	static uint32_t serial = 1;
185	uint64_t msc = 0;
186	int complete = 0;
187
188	xcb_present_notify_msc(c, win, serial ^ 0xdeadbeef, 0, 1, 0);
189	xcb_flush(c);
190
191	do {
192		xcb_present_complete_notify_event_t *ce;
193		xcb_generic_event_t *ev;
194
195		ev = xcb_wait_for_special_event(c, q);
196		if (ev == NULL)
197			break;
198
199		ce = (xcb_present_complete_notify_event_t *)ev;
200		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
201		    ce->serial == (serial ^ 0xdeadbeef)) {
202			msc = ce->msc;
203			complete = 1;
204		}
205		free(ev);
206	} while (!complete);
207
208	if (++serial == 0)
209		serial = 1;
210
211	return msc;
212}
213
214static uint64_t msc_interval(Display *dpy, Window win, void *q)
215{
216	xcb_connection_t *c = XGetXCBConnection(dpy);
217	uint64_t msc, ust;
218	int complete = 0;
219
220	msc = check_msc(dpy, win, q, 0, NULL);
221
222	xcb_present_notify_msc(c, win, 0xc0ffee00, msc, 0, 0);
223	xcb_present_notify_msc(c, win, 0xc0ffee01, msc + 10, 0, 0);
224	xcb_flush(c);
225
226	ust = msc = 0;
227	do {
228		xcb_present_complete_notify_event_t *ce;
229		xcb_generic_event_t *ev;
230
231		ev = xcb_wait_for_special_event(c, q);
232		if (ev == NULL)
233			break;
234
235		ce = (xcb_present_complete_notify_event_t *)ev;
236		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
237		    ce->serial == 0xc0ffee00) {
238			msc -= ce->msc;
239			ust -= ce->ust;
240			complete++;
241		}
242		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
243		    ce->serial == 0xc0ffee01) {
244			msc += ce->msc;
245			ust += ce->ust;
246			complete++;
247		}
248		free(ev);
249	} while (complete != 2);
250
251	printf("10 frame interval: msc=%lld, ust=%lld\n",
252	       (long long)msc, (long long)ust);
253	XSync(dpy, True);
254	if (msc == 0)
255		return 0;
256
257	return (ust + msc/2) / msc;
258}
259
260static void teardown_msc(Display *dpy, void *q)
261{
262	xcb_unregister_for_special_event(XGetXCBConnection(dpy), q);
263}
264
265static int test_whole(Display *dpy, Window win, const char *phase)
266{
267	xcb_connection_t *c = XGetXCBConnection(dpy);
268	Pixmap pixmap;
269	struct dri3_fence fence;
270	Window root;
271	unsigned int width, height;
272	unsigned border, depth;
273	int x, y, ret = 1;
274
275	XGetGeometry(dpy, win,
276		     &root, &x, &y, &width, &height, &border, &depth);
277
278	if (dri3_create_fence(dpy, win, &fence))
279		return 0;
280
281	printf("%s: Testing simple flip: %dx%d\n", phase, width, height);
282	_x_error_occurred = 0;
283
284	xshmfence_reset(fence.addr);
285
286	pixmap = XCreatePixmap(dpy, win, width, height, depth);
287	xcb_present_pixmap(c, win, pixmap, 0,
288			   0, /* valid */
289			   0, /* update */
290			   0, /* x_off */
291			   0, /* y_off */
292			   None,
293			   None, /* wait fence */
294			   fence.xid,
295			   XCB_PRESENT_OPTION_NONE,
296			   0, /* target msc */
297			   0, /* divisor */
298			   0, /* remainder */
299			   0, NULL);
300	XFreePixmap(dpy, pixmap);
301
302	pixmap = XCreatePixmap(dpy, win, width, height, depth);
303	xcb_present_pixmap(c, win, pixmap, 0,
304			   0, /* valid */
305			   0, /* update */
306			   0, /* x_off */
307			   0, /* y_off */
308			   None,
309			   None, /* wait fence */
310			   None, /* sync fence */
311			   XCB_PRESENT_OPTION_NONE,
312			   0, /* target msc */
313			   0, /* divisor */
314			   0, /* remainder */
315			   0, NULL);
316	XFreePixmap(dpy, pixmap);
317	XFlush(dpy);
318
319	ret = !!xshmfence_await(fence.addr);
320	dri3_fence_free(dpy, &fence);
321
322	XSync(dpy, True);
323	ret += !!_x_error_occurred;
324
325	return ret;
326}
327
328static uint64_t flush_flips(Display *dpy, Window win, Pixmap pixmap, void *Q, uint64_t *ust)
329{
330	xcb_connection_t *c = XGetXCBConnection(dpy);
331	uint64_t msc;
332	int complete;
333
334	msc = check_msc(dpy, win, Q, 0, NULL);
335	xcb_present_pixmap(c, win, pixmap,
336			   0xdeadbeef, /* serial */
337			   0, /* valid */
338			   0, /* update */
339			   0, /* x_off */
340			   0, /* y_off */
341			   None,
342			   None, /* wait fence */
343			   None,
344			   XCB_PRESENT_OPTION_NONE,
345			   msc + 60, /* target msc */
346			   0, /* divisor */
347			   0, /* remainder */
348			   0, NULL);
349	xcb_flush(c);
350	complete = 0;
351	do {
352		xcb_present_complete_notify_event_t *ce;
353		xcb_generic_event_t *ev;
354
355		ev = xcb_wait_for_special_event(c, Q);
356		if (ev == NULL)
357			break;
358
359		ce = (xcb_present_complete_notify_event_t *)ev;
360		complete = (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP &&
361			    ce->serial == 0xdeadbeef);
362		free(ev);
363	} while (!complete);
364	XSync(dpy, True);
365
366	return check_msc(dpy, win, Q, msc, ust);
367}
368
369static int test_double(Display *dpy, Window win, const char *phase, void *Q)
370{
371#define COUNT (15*60)
372	xcb_connection_t *c = XGetXCBConnection(dpy);
373	Pixmap pixmap;
374	Window root;
375	unsigned int width, height;
376	unsigned border, depth;
377	int x, y, n, ret;
378	struct {
379		uint64_t msc, ust;
380	} frame[COUNT+1];
381	int offset = 0;
382
383	XGetGeometry(dpy, win,
384		     &root, &x, &y, &width, &height, &border, &depth);
385
386	printf("%s: Testing flip double buffering: %dx%d\n", phase, width, height);
387	_x_error_occurred = 0;
388
389	pixmap = XCreatePixmap(dpy, win, width, height, depth);
390	flush_flips(dpy, win, pixmap, Q, NULL);
391	for (n = 0; n <= COUNT; n++) {
392		int complete;
393
394		xcb_present_pixmap(c, win, pixmap, n,
395				   0, /* valid */
396				   0, /* update */
397				   0, /* x_off */
398				   0, /* y_off */
399				   None,
400				   None, /* wait fence */
401				   None,
402				   XCB_PRESENT_OPTION_NONE,
403				   0, /* target msc */
404				   0, /* divisor */
405				   0, /* remainder */
406				   0, NULL);
407		xcb_flush(c);
408
409		complete = 0;
410		do {
411			xcb_present_complete_notify_event_t *ce;
412			xcb_generic_event_t *ev;
413
414			ev = xcb_wait_for_special_event(c, Q);
415			if (ev == NULL)
416				break;
417
418			ce = (xcb_present_complete_notify_event_t *)ev;
419			if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP &&
420			    ce->serial == n) {
421				frame[n].msc = ce->msc;
422				frame[n].ust = ce->ust;
423				complete = 1;
424			}
425			free(ev);
426		} while (!complete);
427	}
428	XFreePixmap(dpy, pixmap);
429
430	XSync(dpy, True);
431	ret = !!_x_error_occurred;
432
433	if (frame[COUNT].msc - frame[0].msc != COUNT) {
434		printf("Expected %d frames interval, %d elapsed instead\n",
435		       COUNT, (int)(frame[COUNT].msc - frame[0].msc));
436		for (n = 0; n <= COUNT; n++) {
437			if (frame[n].msc - frame[0].msc != n + offset) {
438				printf("frame[%d]: msc=%03lld, ust=%lld\n", n,
439				       (long long)(frame[n].msc - frame[0].msc),
440				       (long long)(frame[n].ust - frame[0].ust));
441				offset = frame[n].msc - frame[0].msc - n;
442				ret++;
443			}
444		}
445	}
446
447	return ret;
448}
449
450static int test_future(Display *dpy, Window win, const char *phase, void *Q)
451{
452	xcb_connection_t *c = XGetXCBConnection(dpy);
453	Pixmap pixmap;
454	struct dri3_fence fence;
455	Window root;
456	unsigned int width, height;
457	unsigned border, depth;
458	int x, y, ret = 0, n;
459	uint64_t msc, ust;
460	int complete, count;
461	int early = 0, late = 0;
462	int earliest = 0, latest = 0;
463	uint64_t interval;
464
465	XGetGeometry(dpy, win,
466		     &root, &x, &y, &width, &height, &border, &depth);
467
468	if (dri3_create_fence(dpy, win, &fence))
469		return 0;
470
471	printf("%s: Testing flips into the future: %dx%d\n", phase, width, height);
472	_x_error_occurred = 0;
473
474	interval = msc_interval(dpy, win, Q);
475	if (interval == 0) {
476		printf("Zero delay between frames\n");
477		return 1;
478	}
479
480	pixmap = XCreatePixmap(dpy, win, width, height, depth);
481	msc = flush_flips(dpy, win, pixmap, Q, &ust);
482	for (n = 1; n <= 10; n++)
483		xcb_present_pixmap(c, win, pixmap,
484				   n, /* serial */
485				   0, /* valid */
486				   0, /* update */
487				   0, /* x_off */
488				   0, /* y_off */
489				   None,
490				   None, /* wait fence */
491				   None,
492				   XCB_PRESENT_OPTION_NONE,
493				   msc + 60 + n*15*60, /* target msc */
494				   0, /* divisor */
495				   0, /* remainder */
496				   0, NULL);
497	xcb_present_pixmap(c, win, pixmap,
498			   0xdeadbeef, /* serial */
499			   0, /* valid */
500			   0, /* update */
501			   0, /* x_off */
502			   0, /* y_off */
503			   None,
504			   None, /* wait fence */
505			   None,
506			   XCB_PRESENT_OPTION_NONE,
507			   msc + 60 + n*15*60, /* target msc */
508			   0, /* divisor */
509			   0, /* remainder */
510			   0, NULL);
511	xcb_flush(c);
512
513	complete = 0;
514	count = 0;
515	do {
516		xcb_present_complete_notify_event_t *ce;
517		xcb_generic_event_t *ev;
518
519		ev = xcb_wait_for_special_event(c, Q);
520		if (ev == NULL)
521			break;
522
523		ce = (xcb_present_complete_notify_event_t *)ev;
524		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
525
526		if (ce->serial == 0xdeadbeef) {
527			int64_t time;
528
529			time = ce->ust - (ust + (60 + 15*60*n) * interval);
530			if (time < -(int64_t)interval) {
531				fprintf(stderr,
532					"\tflips completed too early by %lldms\n",
533					(long long)(-time / 1000));
534			} else if (time > (int64_t)interval) {
535				fprintf(stderr,
536					"\tflips completed too late by %lldms\n",
537					(long long)(time / 1000));
538			}
539			complete = 1;
540		} else {
541			int diff = (int64_t)(ce->msc - (15*60*ce->serial + msc + 60));
542			if (diff < 0) {
543				if (-diff > earliest) {
544					fprintf(stderr, "\tframe %d displayed early by %d frames\n", ce->serial, -diff);
545					earliest = -diff;
546				}
547				early++;
548				ret++;
549			} else if (diff > 0) {
550				if (diff > latest) {
551					fprintf(stderr, "\tframe %d displayed late by %d frames\n", ce->serial, diff);
552					latest = diff;
553				}
554				late++;
555				ret++;
556			}
557			count++;
558		}
559		free(ev);
560	} while (!complete);
561
562	if (early)
563		printf("\t%d frames shown too early (worst %d)!\n", early, earliest);
564	if (late)
565		printf("\t%d frames shown too late (worst %d)!\n", late, latest);
566
567	if (count != 10) {
568		fprintf(stderr, "Sentinel frame received too early! %d frames outstanding\n", 10 - count);
569		ret++;
570
571		do {
572			xcb_present_complete_notify_event_t *ce;
573			xcb_generic_event_t *ev;
574
575			ev = xcb_wait_for_special_event(c, Q);
576			if (ev == NULL)
577				break;
578
579			ce = (xcb_present_complete_notify_event_t *)ev;
580			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
581			free(ev);
582		} while (++count != 10);
583	}
584
585	ret += !!_x_error_occurred;
586
587	return ret;
588}
589
590static int test_exhaustion(Display *dpy, Window win, const char *phase, void *Q)
591{
592#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */
593	xcb_connection_t *c = XGetXCBConnection(dpy);
594	Pixmap pixmap;
595	struct dri3_fence fence[2];
596	Window root;
597	xcb_xfixes_region_t region;
598	unsigned int width, height;
599	unsigned border, depth;
600	int x, y, ret = 0, n;
601	uint64_t target, final;
602
603	XGetGeometry(dpy, win,
604		     &root, &x, &y, &width, &height, &border, &depth);
605
606	if (dri3_create_fence(dpy, win, &fence[0]) ||
607	    dri3_create_fence(dpy, win, &fence[1]))
608		return 0;
609
610	printf("%s: Testing flips with long vblank queues: %dx%d\n", phase, width, height);
611	_x_error_occurred = 0;
612
613	region = xcb_generate_id(c);
614	xcb_xfixes_create_region(c, region, 0, NULL);
615
616	pixmap = XCreatePixmap(dpy, win, width, height, depth);
617	xshmfence_reset(fence[0].addr);
618	xshmfence_reset(fence[1].addr);
619	target = check_msc(dpy, win, Q, 0, NULL);
620	for (n = N_VBLANKS; n--; )
621		xcb_present_pixmap(c, win, pixmap, 0,
622				   0, /* valid */
623				   region, /* update */
624				   0, /* x_off */
625				   0, /* y_off */
626				   None,
627				   None, /* wait fence */
628				   None,
629				   XCB_PRESENT_OPTION_NONE,
630				   target + N_VBLANKS, /* target msc */
631				   1, /* divisor */
632				   0, /* remainder */
633				   0, NULL);
634	xcb_present_pixmap(c, win, pixmap, 0,
635			   region, /* valid */
636			   region, /* update */
637			   0, /* x_off */
638			   0, /* y_off */
639			   None,
640			   None, /* wait fence */
641			   fence[0].xid,
642			   XCB_PRESENT_OPTION_NONE,
643			   target, /* target msc */
644			   0, /* divisor */
645			   0, /* remainder */
646			   0, NULL);
647	for (n = 1; n < N_VBLANKS; n++)
648		xcb_present_pixmap(c, win, pixmap, 0,
649				   region, /* valid */
650				   region, /* update */
651				   0, /* x_off */
652				   0, /* y_off */
653				   None,
654				   None, /* wait fence */
655				   None,
656				   XCB_PRESENT_OPTION_NONE,
657				   target + n, /* target msc */
658				   0, /* divisor */
659				   0, /* remainder */
660				   0, NULL);
661	xcb_present_pixmap(c, win, pixmap, 0,
662			   region, /* valid */
663			   region, /* update */
664			   0, /* x_off */
665			   0, /* y_off */
666			   None,
667			   None, /* wait fence */
668			   fence[1].xid,
669			   XCB_PRESENT_OPTION_NONE,
670			   target + N_VBLANKS, /* target msc */
671			   0, /* divisor */
672			   0, /* remainder */
673			   0, NULL);
674	xcb_flush(c);
675
676	ret += !!xshmfence_await(fence[0].addr);
677	final = check_msc(dpy, win, Q, 0, NULL);
678	if (final < target) {
679		printf("\tFirst flip too early, MSC was %llu, expected %llu\n",
680		       (long long)final, (long long)target);
681		ret++;
682	} else if (final > target + 1) {
683		printf("\tFirst flip too late, MSC was %llu, expected %llu\n",
684		       (long long)final, (long long)target);
685		ret++;
686	}
687
688	ret += !!xshmfence_await(fence[1].addr);
689	final = check_msc(dpy, win, Q, 0, NULL);
690	if (final < target + N_VBLANKS) {
691		printf("\tLast flip too early, MSC was %llu, expected %llu\n",
692		       (long long)final, (long long)(target + N_VBLANKS));
693		ret++;
694	} else if (final > target + N_VBLANKS + 1) {
695		printf("\tLast flip too late, MSC was %llu, expected %llu\n",
696		       (long long)final, (long long)(target + N_VBLANKS));
697		ret++;
698	}
699
700	flush_flips(dpy, win, pixmap, Q, NULL);
701
702	XFreePixmap(dpy, pixmap);
703	xcb_xfixes_destroy_region(c, region);
704	dri3_fence_free(dpy, &fence[1]);
705	dri3_fence_free(dpy, &fence[0]);
706
707	XSync(dpy, True);
708	ret += !!_x_error_occurred;
709
710	return ret;
711#undef N_VBLANKS
712}
713
714static int test_accuracy(Display *dpy, Window win, const char *phase, void *Q)
715{
716#define N_VBLANKS (60 * 120) /* ~2 minutes */
717	xcb_connection_t *c = XGetXCBConnection(dpy);
718	Pixmap pixmap;
719	Window root;
720	unsigned int width, height;
721	unsigned border, depth;
722	int x, y, ret = 0, n;
723	uint64_t target;
724	int early = 0, late = 0;
725	int earliest = 0, latest = 0;
726	int complete, count;
727
728	XGetGeometry(dpy, win,
729		     &root, &x, &y, &width, &height, &border, &depth);
730
731	printf("%s: Testing flip accuracy: %dx%d\n", phase, width, height);
732	_x_error_occurred = 0;
733
734	pixmap = XCreatePixmap(dpy, win, width, height, depth);
735	target = flush_flips(dpy, win, pixmap, Q, NULL);
736	for (n = 0; n <= N_VBLANKS; n++)
737		xcb_present_pixmap(c, win, pixmap,
738				   n, /* serial */
739				   0, /* valid */
740				   0, /* update */
741				   0, /* x_off */
742				   0, /* y_off */
743				   None,
744				   None, /* wait fence */
745				   None,
746				   XCB_PRESENT_OPTION_NONE,
747				   target + 60 + n, /* target msc */
748				   0, /* divisor */
749				   0, /* remainder */
750				   0, NULL);
751	xcb_present_pixmap(c, win, pixmap,
752			   0xdeadbeef, /* serial */
753			   0, /* valid */
754			   0, /* update */
755			   0, /* x_off */
756			   0, /* y_off */
757			   None,
758			   None, /* wait fence */
759			   None,
760			   XCB_PRESENT_OPTION_NONE,
761			   target + 60 + n, /* target msc */
762			   0, /* divisor */
763			   0, /* remainder */
764			   0, NULL);
765	xcb_flush(c);
766
767	complete = 0;
768	count = 0;
769	do {
770		xcb_present_complete_notify_event_t *ce;
771		xcb_generic_event_t *ev;
772
773		ev = xcb_wait_for_special_event(c, Q);
774		if (ev == NULL)
775			break;
776
777		ce = (xcb_present_complete_notify_event_t *)ev;
778		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
779
780		if (ce->serial != 0xdeadbeef) {
781			int diff = (int64_t)(ce->msc - (target + ce->serial + 60));
782			if (diff < 0) {
783				if (-diff > earliest) {
784					fprintf(stderr, "\tframe %d displayed early by %d frames\n", ce->serial, -diff);
785					earliest = -diff;
786				}
787				early++;
788				ret++;
789			} else if (diff > 0) {
790				if (diff > latest) {
791					fprintf(stderr, "\tframe %d displayed late by %d frames\n", ce->serial, diff);
792					latest = diff;
793				}
794				late++;
795				ret++;
796			}
797			count++;
798		} else
799			complete = 1;
800		free(ev);
801	} while (!complete);
802
803	if (early)
804		printf("\t%d frames shown too early (worst %d)!\n", early, earliest);
805	if (late)
806		printf("\t%d frames shown too late (worst %d)!\n", late, latest);
807
808	if (count != N_VBLANKS+1) {
809		fprintf(stderr, "Sentinel frame received too early! %d frames outstanding\n", N_VBLANKS+1 - count);
810		ret++;
811		do {
812			xcb_present_complete_notify_event_t *ce;
813			xcb_generic_event_t *ev;
814
815			ev = xcb_wait_for_special_event(c, Q);
816			if (ev == NULL)
817				break;
818
819			ce = (xcb_present_complete_notify_event_t *)ev;
820			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
821			free(ev);
822		} while (++count != N_VBLANKS+1);
823	}
824
825	XFreePixmap(dpy, pixmap);
826
827	XSync(dpy, True);
828	ret += !!_x_error_occurred;
829
830	return ret;
831#undef N_VBLANKS
832}
833
834static int test_modulus(Display *dpy, Window win, const char *phase, void *Q)
835{
836	xcb_connection_t *c = XGetXCBConnection(dpy);
837	Pixmap pixmap;
838	Window root;
839	unsigned int width, height;
840	unsigned border, depth;
841	xcb_xfixes_region_t region;
842	int x, y, ret = 0;
843	uint64_t target;
844	int early = 0, late = 0;
845	int earliest = 0, latest = 0;
846	int complete, expect, count;
847
848	XGetGeometry(dpy, win,
849		     &root, &x, &y, &width, &height, &border, &depth);
850
851	printf("%s: Testing flip modulus: %dx%d\n", phase, width, height);
852	_x_error_occurred = 0;
853
854	region = xcb_generate_id(c);
855	xcb_xfixes_create_region(c, region, 0, NULL);
856
857	pixmap = XCreatePixmap(dpy, win, width, height, depth);
858	target = flush_flips(dpy, win, pixmap, Q, NULL);
859	expect = 0;
860	for (x = 1; x <= 7; x++) {
861		for (y = 0; y < x; y++) {
862			xcb_present_pixmap(c, win, pixmap,
863					   y << 16 | x, /* serial */
864					   region, /* valid */
865					   region, /* update */
866					   0, /* x_off */
867					   0, /* y_off */
868					   None,
869					   None, /* wait fence */
870					   None,
871					   XCB_PRESENT_OPTION_NONE,
872					   0, /* target msc */
873					   x, /* divisor */
874					   y, /* remainder */
875					   0, NULL);
876			expect++;
877		}
878	}
879	xcb_present_pixmap(c, win, pixmap,
880			   0xdeadbeef, /* serial */
881			   0, /* valid */
882			   0, /* update */
883			   0, /* x_off */
884			   0, /* y_off */
885			   None,
886			   None, /* wait fence */
887			   None,
888			   XCB_PRESENT_OPTION_NONE,
889			   target + 2*x, /* target msc */
890			   0, /* divisor */
891			   0, /* remainder */
892			   0, NULL);
893	xcb_flush(c);
894
895	complete = 0;
896	count = 0;
897	do {
898		xcb_present_complete_notify_event_t *ce;
899		xcb_generic_event_t *ev;
900
901		ev = xcb_wait_for_special_event(c, Q);
902		if (ev == NULL)
903			break;
904
905		ce = (xcb_present_complete_notify_event_t *)ev;
906		if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP)
907			break;
908
909		assert(ce->serial);
910		if (ce->serial != 0xdeadbeef) {
911			uint64_t msc;
912			int diff;
913
914			x = ce->serial & 0xffff;
915			y = ce->serial >> 16;
916
917			msc = target;
918			msc -= target % x;
919			msc += y;
920			if (msc <= target)
921				msc += x;
922
923			diff = (int64_t)(ce->msc - msc);
924			if (diff < 0) {
925				if (-diff > earliest) {
926					fprintf(stderr, "\tframe (%d, %d) displayed early by %d frames\n", y, x, -diff);
927					earliest = -diff;
928				}
929				early++;
930				ret++;
931			} else if (diff > 0) {
932				if (diff > latest) {
933					fprintf(stderr, "\tframe (%d, %d) displayed late by %d frames\n", y, x, diff);
934					latest = diff;
935				}
936				late++;
937				ret++;
938			}
939			count++;
940		} else
941			complete = 1;
942		free(ev);
943	} while (!complete);
944
945	if (early)
946		printf("\t%d frames shown too early (worst %d)!\n", early, earliest);
947	if (late)
948		printf("\t%d frames shown too late (worst %d)!\n", late, latest);
949
950	if (count != expect) {
951		fprintf(stderr, "Sentinel frame received too early! %d frames outstanding\n", expect - count);
952		ret++;
953		do {
954			xcb_present_complete_notify_event_t *ce;
955			xcb_generic_event_t *ev;
956
957			ev = xcb_wait_for_special_event(c, Q);
958			if (ev == NULL)
959				break;
960
961			ce = (xcb_present_complete_notify_event_t *)ev;
962			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
963			free(ev);
964		} while (++count != expect);
965	}
966
967	XFreePixmap(dpy, pixmap);
968	xcb_xfixes_destroy_region(c, region);
969
970	XSync(dpy, True);
971	ret += !!_x_error_occurred;
972
973	return ret;
974}
975
976static int test_future_msc(Display *dpy, void *Q)
977{
978	xcb_connection_t *c = XGetXCBConnection(dpy);
979	Window root = DefaultRootWindow(dpy);
980	int ret = 0, n;
981	uint64_t msc, ust;
982	int complete, count;
983	int early = 0, late = 0;
984	int earliest = 0, latest = 0;
985	uint64_t interval;
986
987	printf("Testing notifies into the future\n");
988	_x_error_occurred = 0;
989
990	interval = msc_interval(dpy, root, Q);
991	if (interval == 0) {
992		printf("Zero delay between frames\n");
993		return 1;
994	}
995	msc = check_msc(dpy, root, Q, 0, &ust);
996	printf("Initial msc=%llx, interval between frames %lldus\n",
997	       (long long)msc, (long long)interval);
998
999	for (n = 1; n <= 10; n++)
1000		xcb_present_notify_msc(c, root, n, msc + 60 + n*15*60, 0, 0);
1001	xcb_present_notify_msc(c, root, 0xdeadbeef, msc + 60 + n*15*60, 0, 0);
1002	xcb_flush(c);
1003
1004	complete = 0;
1005	count = 0;
1006	do {
1007		xcb_present_complete_notify_event_t *ce;
1008		xcb_generic_event_t *ev;
1009
1010		ev = xcb_wait_for_special_event(c, Q);
1011		if (ev == NULL)
1012			break;
1013
1014		ce = (xcb_present_complete_notify_event_t *)ev;
1015		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1016
1017		if (ce->serial == 0xdeadbeef) {
1018			int64_t time, tolerance;
1019
1020			tolerance = 60 + 15*60*n/10;
1021			if (tolerance < interval)
1022				tolerance = interval;
1023
1024			time = ce->ust - (ust + (60 + 15*60*n) * interval);
1025			if (time < -(int64_t)tolerance) {
1026				fprintf(stderr,
1027					"\tnotifies completed too early by %lldms, tolerance %lldus\n",
1028					(long long)(-time / 1000), (long long)tolerance);
1029			} else if (time > (int64_t)tolerance) {
1030				fprintf(stderr,
1031					"\tnotifies completed too late by %lldms, tolerance %lldus\n",
1032					(long long)(time / 1000), (long long)tolerance);
1033			}
1034			complete = 1;
1035		} else {
1036			int diff = (int64_t)(ce->msc - (15*60*ce->serial + msc + 60));
1037
1038			if (ce->serial != count + 1) {
1039				fprintf(stderr, "vblank received out of order! expected %d, received %d\n",
1040					count + 1, (int)ce->serial);
1041				ret++;
1042			}
1043			count++;
1044
1045			if (diff < 0) {
1046				if (-diff > earliest) {
1047					fprintf(stderr, "\tnotify %d early by %d msc\n", ce->serial, -diff);
1048					earliest = -diff;
1049				}
1050				early++;
1051				ret++;
1052			} else if (diff > 0) {
1053				if (diff > latest) {
1054					fprintf(stderr, "\tnotify %d late by %d msc\n", ce->serial, diff);
1055					latest = diff;
1056				}
1057				late++;
1058				ret++;
1059			}
1060		}
1061		free(ev);
1062	} while (!complete);
1063
1064	if (early)
1065		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1066	if (late)
1067		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1068
1069	if (count != 10) {
1070		fprintf(stderr, "Sentinel vblank received too early! %d waits outstanding\n", 10 - count);
1071		ret++;
1072		do {
1073			xcb_present_complete_notify_event_t *ce;
1074			xcb_generic_event_t *ev;
1075
1076			ev = xcb_wait_for_special_event(c, Q);
1077			if (ev == NULL)
1078				break;
1079
1080			ce = (xcb_present_complete_notify_event_t *)ev;
1081			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1082			free(ev);
1083		} while (++count != 10);
1084	}
1085
1086	XSync(dpy, True);
1087	ret += !!_x_error_occurred;
1088
1089	return ret;
1090}
1091
1092static int test_wrap_msc(Display *dpy)
1093{
1094	xcb_connection_t *c = XGetXCBConnection(dpy);
1095	Window root, win;
1096	int x, y;
1097	unsigned int width, height;
1098	unsigned border, depth;
1099	XSetWindowAttributes attr;
1100	int ret = 0, n;
1101	uint64_t msc, ust;
1102	int complete;
1103	uint64_t interval;
1104	void *Q;
1105
1106	XGetGeometry(dpy, DefaultRootWindow(dpy),
1107		     &root, &x, &y, &width, &height, &border, &depth);
1108
1109	attr.override_redirect = 1;
1110	win = XCreateWindow(dpy, root,
1111			    0, 0, width, height, 0, depth,
1112			    InputOutput, DefaultVisual(dpy, DefaultScreen(dpy)),
1113			    CWOverrideRedirect, &attr);
1114	XMapWindow(dpy, win);
1115	XSync(dpy, True);
1116	if (_x_error_occurred)
1117		return 1;
1118
1119	printf("Testing wraparound notifies\n");
1120	_x_error_occurred = 0;
1121
1122	Q = setup_msc(dpy, win);
1123	interval = msc_interval(dpy, win, Q);
1124	if (interval == 0) {
1125		printf("Zero delay between frames\n");
1126		return 1;
1127	}
1128	msc = check_msc(dpy, win, Q, 0, &ust);
1129	printf("Initial msc=%llx, interval between frames %lldus\n",
1130	       (long long)msc, (long long)interval);
1131
1132	for (n = 1; n <= 10; n++)
1133		xcb_present_notify_msc(c, win, n,
1134				       msc + ((long long)n<<32) + n,
1135				       0, 0);
1136	for (n = 1; n <= 10; n++)
1137		xcb_present_notify_msc(c, win, -n,
1138				       0, (long long)n << 32, 0);
1139	xcb_present_notify_msc(c, win, 0xdeadbeef, msc + 60*10, 0, 0);
1140	xcb_flush(c);
1141
1142	complete = 0;
1143	do {
1144		xcb_present_complete_notify_event_t *ce;
1145		xcb_generic_event_t *ev;
1146
1147		ev = xcb_wait_for_special_event(c, Q);
1148		if (ev == NULL)
1149			break;
1150
1151		ce = (xcb_present_complete_notify_event_t *)ev;
1152		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1153
1154		if (ce->serial == 0xdeadbeef) {
1155			complete = 1;
1156		} else {
1157			fprintf(stderr,
1158				"\tnotify %d recieved at +%llu\n",
1159				ce->serial, ce->msc - msc);
1160			ret++;
1161		}
1162		free(ev);
1163	} while (!complete);
1164
1165	teardown_msc(dpy, Q);
1166	XDestroyWindow(dpy, win);
1167	XSync(dpy, True);
1168
1169	return ret;
1170}
1171
1172static int test_exhaustion_msc(Display *dpy, void *Q)
1173{
1174#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */
1175	xcb_connection_t *c = XGetXCBConnection(dpy);
1176	Window root = DefaultRootWindow(dpy);
1177	int ret = 0, n, complete;
1178	int earliest = 0, early = 0;
1179	int latest = 0, late = 0;
1180	uint64_t msc;
1181
1182	printf("Testing notifies with long queues\n");
1183	_x_error_occurred = 0;
1184
1185	msc = check_msc(dpy, root, Q, 0, NULL);
1186	for (n = N_VBLANKS; n--; )
1187		xcb_present_notify_msc(c, root, N_VBLANKS, msc + N_VBLANKS, 0, 0);
1188	for (n = 1; n <= N_VBLANKS ; n++)
1189		xcb_present_notify_msc(c, root, n, msc + n, 0, 0);
1190	xcb_flush(c);
1191
1192	complete = 2*N_VBLANKS;
1193	do {
1194		xcb_present_complete_notify_event_t *ce;
1195		xcb_generic_event_t *ev;
1196		int diff;
1197
1198		ev = xcb_wait_for_special_event(c, Q);
1199		if (ev == NULL)
1200			break;
1201
1202		ce = (xcb_present_complete_notify_event_t *)ev;
1203		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1204
1205		diff = (int64_t)(ce->msc - msc - ce->serial);
1206		if (diff < 0) {
1207			if (-diff > earliest) {
1208				fprintf(stderr, "\tnotify %d early by %d msc\n",(int)ce->serial, -diff);
1209				earliest = -diff;
1210			}
1211			early++;
1212			ret++;
1213		} else if (diff > 0) {
1214			if (diff > latest) {
1215				fprintf(stderr, "\tnotify %d late by %d msc\n", (int)ce->serial, diff);
1216				latest = diff;
1217			}
1218			late++;
1219			ret++;
1220		}
1221		free(ev);
1222	} while (--complete);
1223
1224	if (early)
1225		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1226	if (late)
1227		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1228
1229	XSync(dpy, True);
1230	ret += !!_x_error_occurred;
1231
1232	return ret;
1233#undef N_VBLANKS
1234}
1235
1236static int test_accuracy_msc(Display *dpy, void *Q)
1237{
1238#define N_VBLANKS (60 * 120) /* ~2 minutes */
1239	xcb_connection_t *c = XGetXCBConnection(dpy);
1240	Window root = DefaultRootWindow(dpy);
1241	int ret = 0, n;
1242	uint64_t msc;
1243	int early = 0, late = 0;
1244	int earliest = 0, latest = 0;
1245	int complete, count;
1246
1247	printf("Testing notify accuracy\n");
1248	_x_error_occurred = 0;
1249
1250	msc = check_msc(dpy, root, Q, 0, NULL);
1251	for (n = 0; n <= N_VBLANKS; n++)
1252		xcb_present_notify_msc(c, root, n, msc + 60 + n, 0, 0);
1253	xcb_present_notify_msc(c, root, 0xdeadbeef, msc + 60 + n, 0, 0);
1254	xcb_flush(c);
1255
1256	complete = 0;
1257	count = 0;
1258	do {
1259		xcb_present_complete_notify_event_t *ce;
1260		xcb_generic_event_t *ev;
1261
1262		ev = xcb_wait_for_special_event(c, Q);
1263		if (ev == NULL)
1264			break;
1265
1266		ce = (xcb_present_complete_notify_event_t *)ev;
1267		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1268
1269		if (ce->serial != 0xdeadbeef) {
1270			int diff = (int64_t)(ce->msc - (msc + ce->serial + 60));
1271			if (diff < 0) {
1272				if (-diff > earliest) {
1273					fprintf(stderr, "\tnotify %d early by %d msc\n", ce->serial, -diff);
1274					earliest = -diff;
1275				}
1276				early++;
1277				ret++;
1278			} else if (diff > 0) {
1279				if (diff > latest) {
1280					fprintf(stderr, "\tnotify %d late by %d msc\n", ce->serial, diff);
1281					latest = diff;
1282				}
1283				late++;
1284				ret++;
1285			}
1286			count++;
1287		} else
1288			complete = 1;
1289		free(ev);
1290	} while (!complete);
1291
1292	if (early)
1293		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1294	if (late)
1295		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1296
1297	if (count != N_VBLANKS+1) {
1298		fprintf(stderr, "Sentinel vblank received too early! %d waits outstanding\n", N_VBLANKS+1 - count);
1299		ret++;
1300		do {
1301			xcb_present_complete_notify_event_t *ce;
1302			xcb_generic_event_t *ev;
1303
1304			ev = xcb_wait_for_special_event(c, Q);
1305			if (ev == NULL)
1306				break;
1307
1308			ce = (xcb_present_complete_notify_event_t *)ev;
1309			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1310			free(ev);
1311		} while (++count != N_VBLANKS+1);
1312	}
1313
1314	XSync(dpy, True);
1315	ret += !!_x_error_occurred;
1316
1317	return ret;
1318#undef N_VBLANKS
1319}
1320
1321static int test_modulus_msc(Display *dpy, void *Q)
1322{
1323	xcb_connection_t *c = XGetXCBConnection(dpy);
1324	Window root = DefaultRootWindow(dpy);
1325	xcb_present_complete_notify_event_t *ce;
1326	xcb_generic_event_t *ev;
1327	int x, y, ret = 0;
1328	uint64_t target;
1329	int early = 0, late = 0;
1330	int earliest = 0, latest = 0;
1331	int complete, count, expect;
1332
1333	printf("Testing notify modulus\n");
1334	_x_error_occurred = 0;
1335
1336	target = wait_vblank(dpy, root, Q);
1337
1338	expect = 0;
1339	xcb_present_notify_msc(c, root, 0, 0, 0, 0);
1340	for (x = 1; x <= 19; x++) {
1341		for (y = 0; y < x; y++) {
1342			xcb_present_notify_msc(c, root, y << 16 | x, 0, x, y);
1343			expect++;
1344		}
1345	}
1346	xcb_present_notify_msc(c, root, 0xdeadbeef, target + 2*x, 0, 0);
1347	xcb_flush(c);
1348
1349	ev = xcb_wait_for_special_event(c, Q);
1350	if (ev) {
1351		ce = (xcb_present_complete_notify_event_t *)ev;
1352		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1353		assert(ce->serial == 0);
1354		assert(target == ce->msc);
1355		target = ce->msc;
1356	}
1357
1358	complete = 0;
1359	count = 0;
1360	do {
1361		ev = xcb_wait_for_special_event(c, Q);
1362		if (ev == NULL)
1363			break;
1364
1365		ce = (xcb_present_complete_notify_event_t *)ev;
1366		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1367
1368		assert(ce->serial);
1369		if (ce->serial != 0xdeadbeef) {
1370			uint64_t msc;
1371			int diff;
1372
1373			x = ce->serial & 0xffff;
1374			y = ce->serial >> 16;
1375
1376			msc = target;
1377			msc -= target % x;
1378			msc += y;
1379			if (msc <= target)
1380				msc += x;
1381
1382			diff = (int64_t)(ce->msc - msc);
1383			if (diff < 0) {
1384				if (-diff > earliest) {
1385					fprintf(stderr, "\tnotify (%d, %d) early by %d msc (target %lld, reported %lld)\n", y, x, -diff, (long long)msc, (long long)ce->msc);
1386					earliest = -diff;
1387				}
1388				early++;
1389				ret++;
1390			} else if (diff > 0) {
1391				if (diff > latest) {
1392					fprintf(stderr, "\tnotify (%d, %d) late by %d msc (target %lld, reported %lld)\n", y, x, diff, (long long)msc, (long long)ce->msc);
1393					latest = diff;
1394				}
1395				late++;
1396				ret++;
1397			}
1398			count++;
1399		} else
1400			complete = 1;
1401		free(ev);
1402	} while (!complete);
1403
1404	if (early)
1405		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1406	if (late)
1407		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1408
1409	if (count != expect) {
1410		fprintf(stderr, "Sentinel vblank received too early! %d waits outstanding\n", expect - count);
1411		ret++;
1412		do {
1413			ev = xcb_wait_for_special_event(c, Q);
1414			if (ev == NULL)
1415				break;
1416
1417			ce = (xcb_present_complete_notify_event_t *)ev;
1418			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1419			free(ev);
1420		} while (++count != expect);
1421	}
1422
1423	XSync(dpy, True);
1424	ret += !!_x_error_occurred;
1425
1426	return ret;
1427}
1428
1429static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
1430{
1431	XRRScreenResources *res;
1432
1433	res = XRRGetScreenResourcesCurrent(dpy, window);
1434	if (res == NULL)
1435		res = XRRGetScreenResources(dpy, window);
1436
1437	return res;
1438}
1439
1440static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
1441{
1442	int i;
1443
1444	for (i = 0; i < res->nmode; i++) {
1445		if (res->modes[i].id == id)
1446			return &res->modes[i];
1447	}
1448
1449	return NULL;
1450}
1451
1452static int for_each_crtc(Display *dpy,
1453			  int (*func)(Display *dpy,
1454				      RRCrtc crtc,
1455				      int width, int height,
1456				      void *closure),
1457			  void *closure)
1458{
1459	XRRScreenResources *res;
1460	XRRCrtcInfo **original_crtc;
1461	int i, j, err = 0;
1462
1463	if (!XRRQueryVersion(dpy, &i, &j))
1464		return -1;
1465
1466	res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy));
1467	if (res == NULL)
1468		return -1;
1469
1470	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
1471	for (i = 0; i < res->ncrtc; i++)
1472		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
1473
1474	for (i = 0; i < res->noutput; i++) {
1475		XRROutputInfo *output;
1476		XRRModeInfo *mode;
1477
1478		output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
1479		if (output == NULL)
1480			continue;
1481
1482		mode = NULL;
1483		if (res->nmode)
1484			mode = lookup_mode(res, output->modes[0]);
1485
1486		for (j = 0; mode && j < output->ncrtc; j++) {
1487			printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n",
1488			       i, j, (long)res->outputs[i], (long)output->crtcs[j]);
1489			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
1490					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
1491			XSync(dpy, True);
1492
1493			err += func(dpy, output->crtcs[j], mode->width, mode->height, closure);
1494
1495			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
1496					 0, 0, None, RR_Rotate_0, NULL, 0);
1497			XSync(dpy, True);
1498		}
1499
1500		XRRFreeOutputInfo(output);
1501	}
1502
1503	for (i = 0; i < res->ncrtc; i++)
1504		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
1505				 original_crtc[i]->x,
1506				 original_crtc[i]->y,
1507				 original_crtc[i]->mode,
1508				 original_crtc[i]->rotation,
1509				 original_crtc[i]->outputs,
1510				 original_crtc[i]->noutput);
1511
1512	free(original_crtc);
1513	XRRFreeScreenResources(res);
1514
1515	return err;
1516}
1517
1518struct test_crtc {
1519	Window win;
1520	int depth;
1521	unsigned flags;
1522
1523	struct dri3_fence fence;
1524	void *queue;
1525	uint64_t msc;
1526};
1527#define SYNC 0x1
1528#define FUTURE 0x2
1529
1530static int __test_crtc(Display *dpy, RRCrtc crtc,
1531		       int width, int height,
1532		       void *closure)
1533{
1534	struct test_crtc *test = closure;
1535	Pixmap pixmap;
1536	int err = 0;
1537
1538	test->msc = check_msc(dpy, test->win, test->queue, test->msc, NULL);
1539
1540	if (test->flags & SYNC)
1541		xshmfence_reset(test->fence.addr);
1542
1543	pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth);
1544	xcb_present_pixmap(XGetXCBConnection(dpy),
1545			   test->win, pixmap,
1546			   0, /* sbc */
1547			   0, /* valid */
1548			   0, /* update */
1549			   0, /* x_off */
1550			   0, /* y_off */
1551			   crtc,
1552			   None, /* wait fence */
1553			   test->flags & SYNC ? test->fence.xid : None,
1554			   XCB_PRESENT_OPTION_NONE,
1555			   test->msc, /* target msc */
1556			   1, /* divisor */
1557			   0, /* remainder */
1558			   0, NULL);
1559	if (test->flags & SYNC) {
1560		Pixmap tmp = XCreatePixmap(dpy, test->win, width, height, test->depth);
1561		xcb_present_pixmap(XGetXCBConnection(dpy),
1562				   test->win, tmp,
1563				   1, /* sbc */
1564				   0, /* valid */
1565				   0, /* update */
1566				   0, /* x_off */
1567				   0, /* y_off */
1568				   crtc,
1569				   None, /* wait fence */
1570				   None, /* sync fence */
1571				   XCB_PRESENT_OPTION_NONE,
1572				   test->msc + (test->flags & FUTURE ? 5 * 16 : 1), /* target msc */
1573				   1, /* divisor */
1574				   0, /* remainder */
1575				   0, NULL);
1576		XFreePixmap(dpy, tmp);
1577		XFlush(dpy);
1578		err += !!xshmfence_await(test->fence.addr);
1579	}
1580	XFreePixmap(dpy, pixmap);
1581
1582	test->msc = check_msc(dpy, test->win, test->queue, test->msc, NULL);
1583	return err;
1584}
1585
1586static int test_crtc(Display *dpy, void *queue, uint64_t last_msc)
1587{
1588	struct test_crtc test;
1589	int err = 0;
1590
1591	XSync(dpy, True);
1592	_x_error_occurred = 0;
1593
1594	test.win = DefaultRootWindow(dpy);
1595	test.depth = DefaultDepth(dpy, DefaultScreen(dpy));
1596	if (dri3_create_fence(dpy, test.win, &test.fence))
1597		return -1;
1598	test.queue = queue;
1599	test.msc = last_msc;
1600
1601	printf("Testing each crtc, without waiting for each flip\n");
1602	test.flags = 0;
1603	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
1604	err += for_each_crtc(dpy, __test_crtc, &test);
1605	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
1606
1607	printf("Testing each crtc, waiting for flips to complete\n");
1608	test.flags = SYNC;
1609	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
1610	err += for_each_crtc(dpy, __test_crtc, &test);
1611	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
1612
1613	printf("Testing each crtc, with future flips\n");
1614	test.flags = FUTURE | SYNC;
1615	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
1616	err += for_each_crtc(dpy, __test_crtc, &test);
1617	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
1618
1619	dri3_fence_free(dpy, &test.fence);
1620	XSync(dpy, True);
1621	err += !!_x_error_occurred;
1622
1623	if (err)
1624		printf("%s: failures=%d\n", __func__, err);
1625
1626	return err;
1627}
1628
1629static int
1630can_use_shm(Display *dpy)
1631{
1632	int major, minor, has_pixmap;
1633
1634	if (!XShmQueryExtension(dpy))
1635		return 0;
1636
1637	XShmQueryVersion(dpy, &major, &minor, &has_pixmap);
1638	return has_pixmap;
1639}
1640
1641static int test_shm(Display *dpy)
1642{
1643	Window win = DefaultRootWindow(dpy);
1644	XShmSegmentInfo shm;
1645	Pixmap pixmap;
1646	Window root;
1647	unsigned int width, height;
1648	unsigned border, depth;
1649	int x, y, ret = 1;
1650
1651	if (!can_use_shm(dpy))
1652		return 0;
1653
1654	_x_error_occurred = 0;
1655
1656	XGetGeometry(dpy, win, &root, &x, &y,
1657		     &width, &height, &border, &depth);
1658
1659	printf("Using %dx%d SHM\n", width, height);
1660
1661	shm.shmid = shmget(IPC_PRIVATE, height * 4*width, IPC_CREAT | 0666);
1662	if (shm.shmid == -1)
1663		return 0;
1664
1665	shm.shmaddr = shmat(shm.shmid, 0, 0);
1666	if (shm.shmaddr == (char *) -1)
1667		goto rmid;
1668
1669	shm.readOnly = False;
1670	XShmAttach(dpy, &shm);
1671
1672	pixmap = XShmCreatePixmap(dpy, DefaultRootWindow(dpy),
1673				  shm.shmaddr, &shm, width, height, 24);
1674	if (_x_error_occurred)
1675		goto detach;
1676
1677	xcb_present_pixmap(XGetXCBConnection(dpy),
1678			   win, pixmap,
1679			   0, /* sbc */
1680			   0, /* valid */
1681			   0, /* update */
1682			   0, /* x_off */
1683			   0, /* y_off */
1684			   None,
1685			   None, /* wait fence */
1686			   None,
1687			   XCB_PRESENT_OPTION_NONE,
1688			   0, /* target msc */
1689			   0, /* divisor */
1690			   0, /* remainder */
1691			   0, NULL);
1692	XFreePixmap(dpy, pixmap);
1693
1694	XSync(dpy, True);
1695	if (_x_error_occurred)
1696		goto detach;
1697
1698	ret = 0;
1699detach:
1700	XShmDetach(dpy, &shm);
1701	shmdt(shm.shmaddr);
1702	XSync(dpy, False);
1703rmid:
1704	shmctl(shm.shmid, IPC_RMID, NULL);
1705	return ret;
1706}
1707
1708static uint32_t gem_create(int fd, int size)
1709{
1710	struct drm_i915_gem_create create;
1711
1712	create.handle = 0;
1713	create.size = size;
1714	(void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
1715
1716	return create.handle;
1717}
1718
1719struct local_i915_gem_caching {
1720	uint32_t handle;
1721	uint32_t caching;
1722};
1723
1724#define LOCAL_I915_GEM_SET_CACHING	0x2f
1725#define LOCAL_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_SET_CACHING, struct local_i915_gem_caching)
1726
1727static int gem_set_caching(int fd, uint32_t handle, int caching)
1728{
1729	struct local_i915_gem_caching arg;
1730
1731	arg.handle = handle;
1732	arg.caching = caching;
1733
1734	return drmIoctl(fd, LOCAL_IOCTL_I915_GEM_SET_CACHING, &arg) == 0;
1735}
1736
1737static int gem_set_tiling(int fd, uint32_t handle, int tiling, int stride)
1738{
1739	struct drm_i915_gem_set_tiling set_tiling;
1740	int err;
1741
1742restart:
1743	set_tiling.handle = handle;
1744	set_tiling.tiling_mode = tiling;
1745	set_tiling.stride = stride;
1746
1747	if (drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0)
1748		return 1;
1749
1750	err = errno;
1751	if (err == EINTR)
1752		goto restart;
1753
1754	if (err == EAGAIN) {
1755		sched_yield();
1756		goto restart;
1757	}
1758
1759	return 0;
1760}
1761
1762static int gem_export(int fd, uint32_t handle)
1763{
1764	struct drm_prime_handle args;
1765
1766	args.handle = handle;
1767	args.flags = O_CLOEXEC;
1768
1769	if (drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args))
1770		return -1;
1771
1772	return args.fd;
1773}
1774
1775static void gem_close(int fd, uint32_t handle)
1776{
1777	struct drm_gem_close close;
1778
1779	close.handle = handle;
1780	(void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
1781}
1782
1783static int test_dri3_tiling(Display *dpy)
1784{
1785	Window win = DefaultRootWindow(dpy);
1786	const int tiling[] = { I915_TILING_NONE, I915_TILING_X, I915_TILING_Y };
1787	Window root;
1788	unsigned int width, height;
1789	unsigned border, depth, bpp;
1790	unsigned stride, size;
1791	void *Q;
1792	int x, y;
1793	int device;
1794	int line = -1;
1795	int t;
1796
1797	device = dri3_open(dpy);
1798	if (device < 0)
1799		return 0;
1800
1801	if (!is_intel(device))
1802		return 0;
1803
1804	printf("Opened Intel DRI3 device\n");
1805
1806	XGetGeometry(dpy, win, &root, &x, &y,
1807		     &width, &height, &border, &depth);
1808
1809	switch (depth) {
1810	case 8: bpp = 8; break;
1811	case 15: case 16: bpp = 16; break;
1812	case 24: case 32: bpp = 32; break;
1813	default: return 0;
1814	}
1815
1816	stride = ALIGN(width * bpp/8, 512);
1817	size = PAGE_ALIGN(stride * ALIGN(height, 32));
1818	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n",
1819	       width, height, stride, size);
1820
1821	_x_error_occurred = 0;
1822	Q = setup_msc(dpy, root);
1823
1824	for (t = 0; t < sizeof(tiling)/sizeof(tiling[0]); t++) {
1825		uint64_t msc;
1826		uint32_t src;
1827		int src_fd;
1828		Pixmap src_pix;
1829
1830		src = gem_create(device, size);
1831		if (!src) {
1832			line = __LINE__;
1833			goto fail;
1834		}
1835
1836		gem_set_tiling(device, src, tiling[t], stride);
1837
1838		src_fd = gem_export(device, src);
1839		if (src_fd < 0) {
1840			line = __LINE__;
1841			goto fail;
1842		}
1843
1844		src_pix = dri3_create_pixmap(dpy, root,
1845					     width, height, depth,
1846					     src_fd, bpp, stride, size);
1847
1848		msc = wait_vblank(dpy, root, Q);
1849
1850		xcb_present_pixmap(XGetXCBConnection(dpy),
1851				   win, src_pix,
1852				   0, /* sbc */
1853				   0, /* valid */
1854				   0, /* update */
1855				   0, /* x_off */
1856				   0, /* y_off */
1857				   None,
1858				   None, /* wait fence */
1859				   None,
1860				   XCB_PRESENT_OPTION_NONE,
1861				   msc + 2, /* target msc */
1862				   1, /* divisor */
1863				   0, /* remainder */
1864				   0, NULL);
1865
1866		xcb_present_pixmap(XGetXCBConnection(dpy),
1867				   win, src_pix,
1868				   0, /* sbc */
1869				   0, /* valid */
1870				   0, /* update */
1871				   0, /* x_off */
1872				   0, /* y_off */
1873				   None,
1874				   None, /* wait fence */
1875				   None,
1876				   XCB_PRESENT_OPTION_NONE,
1877				   msc + 3, /* target msc */
1878				   1, /* divisor */
1879				   0, /* remainder */
1880				   0, NULL);
1881
1882		XSync(dpy, True);
1883		if (_x_error_occurred) {
1884			line = __LINE__;
1885			goto fail;
1886		}
1887		XFreePixmap(dpy, src_pix);
1888		_x_error_occurred = 0;
1889
1890		close(src_fd);
1891		gem_close(device, src);
1892	}
1893
1894	teardown_msc(dpy, Q);
1895	return 0;
1896
1897fail:
1898	printf("%s failed with tiling %d, line %d\n", __func__, tiling[t], line);
1899	teardown_msc(dpy, Q);
1900	return 1;
1901}
1902
1903static int test_dri3(Display *dpy)
1904{
1905	Window win = DefaultRootWindow(dpy);
1906	Pixmap pixmap;
1907	Window root;
1908	unsigned int width, height;
1909	unsigned border, depth;
1910	unsigned stride, size;
1911	int x, y, ret = 1;
1912	int device, handle;
1913	int bpp;
1914
1915	device = dri3_open(dpy);
1916	if (device < 0)
1917		return 0;
1918
1919	if (!is_intel(device))
1920		return 0;
1921
1922	printf("Opened Intel DRI3 device\n");
1923
1924	XGetGeometry(dpy, win, &root, &x, &y,
1925		     &width, &height, &border, &depth);
1926
1927	switch (depth) {
1928	case 8: bpp = 8; break;
1929	case 15: case 16: bpp = 16; break;
1930	case 24: case 32: bpp = 32; break;
1931	default: return 0;
1932	}
1933
1934	stride = width * bpp/8;
1935	size = PAGE_ALIGN(stride * height);
1936	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n",
1937	       width, height, stride, size);
1938
1939	pixmap = 0;
1940	handle = gem_create(device, size);
1941	if (handle) {
1942		pixmap = dri3_create_pixmap(dpy, root,
1943					     width, height, depth,
1944					     gem_export(device, handle), bpp, stride, size);
1945		gem_close(device, handle);
1946	}
1947	if (pixmap == 0)
1948		goto fail;
1949
1950	xcb_present_pixmap(XGetXCBConnection(dpy),
1951			   win, pixmap,
1952			   0, /* sbc */
1953			   0, /* valid */
1954			   0, /* update */
1955			   0, /* x_off */
1956			   0, /* y_off */
1957			   None,
1958			   None, /* wait fence */
1959			   None,
1960			   XCB_PRESENT_OPTION_NONE,
1961			   0, /* target msc */
1962			   0, /* divisor */
1963			   0, /* remainder */
1964			   0, NULL);
1965	XFreePixmap(dpy, pixmap);
1966
1967	XSync(dpy, True);
1968	if (_x_error_occurred)
1969		goto fail;
1970
1971	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for CPU\n",
1972	       width, height, stride, size);
1973
1974	pixmap = 0;
1975	handle = gem_create(device, size);
1976	if (handle) {
1977		gem_set_caching(device, handle, CPU);
1978		handle = dri3_create_pixmap(dpy, root,
1979					     width, height, depth,
1980					     gem_export(device, handle), bpp, stride, size);
1981		gem_close(device, handle);
1982	}
1983	if (pixmap == 0)
1984		goto fail;
1985
1986	xcb_present_pixmap(XGetXCBConnection(dpy),
1987			   win, pixmap,
1988			   0, /* sbc */
1989			   0, /* valid */
1990			   0, /* update */
1991			   0, /* x_off */
1992			   0, /* y_off */
1993			   None,
1994			   None, /* wait fence */
1995			   None,
1996			   XCB_PRESENT_OPTION_NONE,
1997			   0, /* target msc */
1998			   0, /* divisor */
1999			   0, /* remainder */
2000			   0, NULL);
2001	XFreePixmap(dpy, pixmap);
2002
2003	XSync(dpy, True);
2004	if (_x_error_occurred)
2005		goto fail;
2006
2007	ret = 0;
2008fail:
2009	close(device);
2010	return ret;
2011}
2012
2013static int has_present(Display *dpy)
2014{
2015	xcb_connection_t *c = XGetXCBConnection(dpy);
2016	xcb_generic_error_t *error = NULL;
2017	void *reply;
2018
2019	reply = xcb_xfixes_query_version_reply(c,
2020					       xcb_xfixes_query_version(c,
2021									XCB_XFIXES_MAJOR_VERSION,
2022									XCB_XFIXES_MINOR_VERSION),
2023					       &error);
2024	free(reply);
2025	free(error);
2026	if (reply == NULL) {
2027		fprintf(stderr, "XFixes not supported on %s\n", DisplayString(dpy));
2028		return 0;
2029	}
2030
2031	reply = xcb_dri3_query_version_reply(c,
2032					     xcb_dri3_query_version(c,
2033								    XCB_DRI3_MAJOR_VERSION,
2034								    XCB_DRI3_MINOR_VERSION),
2035					     &error);
2036	free(reply);
2037	free(error);
2038	if (reply == NULL) {
2039		fprintf(stderr, "DRI3 not supported on %s\n", DisplayString(dpy));
2040		return 0;
2041	}
2042
2043	reply = xcb_present_query_version_reply(c,
2044						xcb_present_query_version(c,
2045									  XCB_PRESENT_MAJOR_VERSION,
2046									  XCB_PRESENT_MINOR_VERSION),
2047						&error);
2048
2049	free(reply);
2050	free(error);
2051	if (reply == NULL) {
2052		fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy));
2053		return 0;
2054	}
2055
2056	return 1;
2057}
2058
2059static int has_composite(Display *dpy)
2060{
2061	int event, error;
2062	int major, minor;
2063
2064	if (!XCompositeQueryExtension(dpy, &event, &error))
2065		return 0;
2066
2067	XCompositeQueryVersion(dpy, &major, &minor);
2068
2069	return major > 0 || minor >= 4;
2070}
2071
2072int main(void)
2073{
2074	Display *dpy;
2075	Window root;
2076	int dummy;
2077	int error = 0;
2078	uint64_t last_msc;
2079	void *queue;
2080
2081	dpy = XOpenDisplay(NULL);
2082	if (dpy == NULL)
2083		return 77;
2084
2085	if (!has_present(dpy))
2086		return 77;
2087
2088	if (DPMSQueryExtension(dpy, &dummy, &dummy))
2089		DPMSDisable(dpy);
2090
2091	root = DefaultRootWindow(dpy);
2092
2093	signal(SIGALRM, SIG_IGN);
2094	XSetErrorHandler(_check_error_handler);
2095
2096	queue = setup_msc(dpy, root);
2097	last_msc = check_msc(dpy, root, queue, 0, NULL);
2098
2099	error += test_future_msc(dpy, queue);
2100	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2101
2102	error += test_wrap_msc(dpy);
2103	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2104
2105	error += test_accuracy_msc(dpy, queue);
2106	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2107
2108	error += test_modulus_msc(dpy, queue);
2109	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2110
2111	error += test_exhaustion_msc(dpy, queue);
2112	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2113
2114	for (dummy = 0; dummy <= 3; dummy++) {
2115		Window win;
2116		uint64_t msc = 0;
2117		XSetWindowAttributes attr;
2118		Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
2119		unsigned int width, height;
2120		unsigned border, depth;
2121		const char *phase;
2122		int x, y;
2123		void *Q;
2124
2125		attr.override_redirect = 1;
2126
2127		XGetGeometry(dpy, root, &win, &x, &y,
2128			     &width, &height, &border, &depth);
2129
2130		_x_error_occurred = 0;
2131		switch (dummy) {
2132		case 0:
2133			win = root;
2134			phase = "root";
2135			break;
2136		case 1:
2137			win = XCreateWindow(dpy, root,
2138					    0, 0, width, height, 0, depth,
2139					    InputOutput, visual,
2140					    CWOverrideRedirect, &attr);
2141			phase = "fullscreen";
2142			break;
2143		case 2:
2144			win = XCreateWindow(dpy, root,
2145					    0, 0, width/2, height/2, 0, depth,
2146					    InputOutput, visual,
2147					    CWOverrideRedirect, &attr);
2148			phase = "window";
2149			break;
2150		case 3:
2151			if (!has_composite(dpy))
2152				continue;
2153
2154			win = XCreateWindow(dpy, root,
2155					    0, 0, width, height, 0,
2156					    DefaultDepth(dpy, DefaultScreen(dpy)),
2157					    InputOutput,
2158					    DefaultVisual(dpy, DefaultScreen(dpy)),
2159					    CWOverrideRedirect, &attr);
2160			XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
2161			phase = "composite";
2162			break;
2163
2164		default:
2165			phase = "broken";
2166			win = root;
2167			abort();
2168			break;
2169		}
2170
2171		XMapWindow(dpy, win);
2172		XSync(dpy, True);
2173		if (_x_error_occurred)
2174			continue;
2175
2176		Q = setup_msc(dpy, win);
2177		msc = check_msc(dpy, win, Q, msc, NULL);
2178
2179		error += test_whole(dpy, win, phase);
2180		msc = check_msc(dpy, win, Q, msc, NULL);
2181
2182		error += test_double(dpy, win, phase, Q);
2183		msc = check_msc(dpy, win, Q, msc, NULL);
2184
2185		error += test_future(dpy, win, phase, Q);
2186		msc = check_msc(dpy, win, Q, msc, NULL);
2187
2188		error += test_accuracy(dpy, win, phase, Q);
2189		msc = check_msc(dpy, win, Q, msc, NULL);
2190
2191		error += test_modulus(dpy, win, phase, Q);
2192		msc = check_msc(dpy, win, Q, msc, NULL);
2193
2194		error += test_exhaustion(dpy, win, phase, Q);
2195		msc = check_msc(dpy, win, Q, msc, NULL);
2196
2197		teardown_msc(dpy, Q);
2198		if (win != root)
2199			XDestroyWindow(dpy, win);
2200	}
2201
2202	error += test_crtc(dpy, queue, last_msc);
2203	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2204
2205	error += test_shm(dpy);
2206	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2207
2208	error += test_dri3(dpy);
2209	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2210
2211	error += test_dri3_tiling(dpy);
2212	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2213
2214	teardown_msc(dpy, queue);
2215
2216	if (DPMSQueryExtension(dpy, &dummy, &dummy))
2217		DPMSEnable(dpy);
2218	return !!error;
2219}
2220