142542f5fSchristos/*
242542f5fSchristos * Copyright (c) 2014 Intel Corporation
342542f5fSchristos *
442542f5fSchristos * Permission is hereby granted, free of charge, to any person obtaining a
542542f5fSchristos * copy of this software and associated documentation files (the "Software"),
642542f5fSchristos * to deal in the Software without restriction, including without limitation
742542f5fSchristos * the rights to use, copy, modify, merge, publish, distribute, sublicense,
842542f5fSchristos * and/or sell copies of the Software, and to permit persons to whom the
942542f5fSchristos * Software is furnished to do so, subject to the following conditions:
1042542f5fSchristos *
1142542f5fSchristos * The above copyright notice and this permission notice (including the next
1242542f5fSchristos * paragraph) shall be included in all copies or substantial portions of the
1342542f5fSchristos * Software.
1442542f5fSchristos *
1542542f5fSchristos * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1642542f5fSchristos * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1742542f5fSchristos * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1842542f5fSchristos * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1942542f5fSchristos * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2042542f5fSchristos * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2142542f5fSchristos * SOFTWARE.
2242542f5fSchristos *
2342542f5fSchristos */
2442542f5fSchristos
2542542f5fSchristos#ifdef HAVE_CONFIG_H
2642542f5fSchristos#include "config.h"
2742542f5fSchristos#endif
2842542f5fSchristos
2942542f5fSchristos#include <X11/Xlib.h>
3042542f5fSchristos#include <X11/Xlib-xcb.h>
3142542f5fSchristos#include <X11/xshmfence.h>
3242542f5fSchristos#include <X11/Xutil.h>
3342542f5fSchristos#include <X11/Xlibint.h>
34fe8aea9eSmrg#include <X11/extensions/dpms.h>
3542542f5fSchristos#include <X11/extensions/randr.h>
36fe8aea9eSmrg#include <X11/extensions/Xcomposite.h>
3742542f5fSchristos#include <X11/extensions/Xrandr.h>
3842542f5fSchristos#include <X11/extensions/Xrender.h>
3942542f5fSchristos#include <X11/extensions/XShm.h>
4042542f5fSchristos#if HAVE_X11_EXTENSIONS_SHMPROTO_H
4142542f5fSchristos#include <X11/extensions/shmproto.h>
4242542f5fSchristos#elif HAVE_X11_EXTENSIONS_SHMSTR_H
4342542f5fSchristos#include <X11/extensions/shmstr.h>
4442542f5fSchristos#else
4542542f5fSchristos#error Failed to find the right header for X11 MIT-SHM protocol definitions
4642542f5fSchristos#endif
4742542f5fSchristos#include <xcb/xcb.h>
4842542f5fSchristos#include <xcb/present.h>
49fe8aea9eSmrg#include <xcb/xfixes.h>
50fe8aea9eSmrg#include <xcb/dri3.h>
5142542f5fSchristos#include <xf86drm.h>
5242542f5fSchristos#include <i915_drm.h>
5342542f5fSchristos
5442542f5fSchristos#include <stdio.h>
5542542f5fSchristos#include <string.h>
5642542f5fSchristos#include <fcntl.h>
5742542f5fSchristos#include <unistd.h>
5842542f5fSchristos#include <assert.h>
5942542f5fSchristos#include <errno.h>
6042542f5fSchristos#include <setjmp.h>
6142542f5fSchristos#include <signal.h>
6242542f5fSchristos
6342542f5fSchristos#include <sys/mman.h>
6442542f5fSchristos#include <sys/ipc.h>
6542542f5fSchristos#include <sys/shm.h>
6642542f5fSchristos#include <pciaccess.h>
6742542f5fSchristos
6842542f5fSchristos#include "dri3.h"
6942542f5fSchristos
7042542f5fSchristos#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
7142542f5fSchristos#define PAGE_ALIGN(x) ALIGN(x, 4096)
7242542f5fSchristos
7342542f5fSchristos#define GTT I915_GEM_DOMAIN_GTT
7442542f5fSchristos#define CPU I915_GEM_DOMAIN_CPU
7542542f5fSchristos
7642542f5fSchristosstatic int _x_error_occurred;
7742542f5fSchristosstatic uint32_t stamp;
7842542f5fSchristos
7942542f5fSchristosstatic int
8042542f5fSchristos_check_error_handler(Display     *display,
8142542f5fSchristos		     XErrorEvent *event)
8242542f5fSchristos{
8342542f5fSchristos	printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
8442542f5fSchristos	       DisplayString(display),
8542542f5fSchristos	       event->serial,
8642542f5fSchristos	       event->error_code,
8742542f5fSchristos	       event->request_code,
8842542f5fSchristos	       event->minor_code);
8942542f5fSchristos	_x_error_occurred++;
9042542f5fSchristos	return False; /* ignored */
9142542f5fSchristos}
9242542f5fSchristos
9342542f5fSchristosstatic int is_i915_device(int fd)
9442542f5fSchristos{
9542542f5fSchristos	drm_version_t version;
9642542f5fSchristos	char name[5] = "";
9742542f5fSchristos
9842542f5fSchristos	memset(&version, 0, sizeof(version));
9942542f5fSchristos	version.name_len = 4;
10042542f5fSchristos	version.name = name;
10142542f5fSchristos
10242542f5fSchristos	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
10342542f5fSchristos		return 0;
10442542f5fSchristos
10542542f5fSchristos	return strcmp("i915", name) == 0;
10642542f5fSchristos}
10742542f5fSchristos
10842542f5fSchristosstatic int is_intel(int fd)
10942542f5fSchristos{
11042542f5fSchristos	struct drm_i915_getparam gp;
11142542f5fSchristos	int ret;
11242542f5fSchristos
11342542f5fSchristos	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
11442542f5fSchristos	ret = is_i915_device(fd);
11542542f5fSchristos	if (ret) {
11642542f5fSchristos		gp.param = I915_PARAM_HAS_GEM;
11742542f5fSchristos		gp.value = &ret;
11842542f5fSchristos		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
11942542f5fSchristos			ret = 0;
12042542f5fSchristos	}
12142542f5fSchristos	return ret;
12242542f5fSchristos}
12342542f5fSchristos
12442542f5fSchristosstatic void *setup_msc(Display *dpy,  Window win)
12542542f5fSchristos{
12642542f5fSchristos	xcb_connection_t *c = XGetXCBConnection(dpy);
12742542f5fSchristos	xcb_void_cookie_t cookie;
12842542f5fSchristos	uint32_t id = xcb_generate_id(c);
12942542f5fSchristos	xcb_generic_error_t *error;
13042542f5fSchristos	void *q;
13142542f5fSchristos
13242542f5fSchristos	cookie = xcb_present_select_input_checked(c, id, win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
13342542f5fSchristos	q = xcb_register_for_special_xge(c, &xcb_present_id, id, &stamp);
13442542f5fSchristos
13542542f5fSchristos	error = xcb_request_check(c, cookie);
13642542f5fSchristos	assert(error == NULL);
13742542f5fSchristos
13842542f5fSchristos	return q;
13942542f5fSchristos}
14042542f5fSchristos
141fe8aea9eSmrgstatic uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc, uint64_t *ust)
14242542f5fSchristos{
14342542f5fSchristos	xcb_connection_t *c = XGetXCBConnection(dpy);
144fe8aea9eSmrg	static uint32_t serial = 1;
14542542f5fSchristos	uint64_t msc = 0;
146fe8aea9eSmrg	int complete = 0;
14742542f5fSchristos
148fe8aea9eSmrg	xcb_present_notify_msc(c, win, serial ^ 0xcc00ffee, 0, 0, 0);
14942542f5fSchristos	xcb_flush(c);
15042542f5fSchristos
15142542f5fSchristos	do {
15242542f5fSchristos		xcb_present_complete_notify_event_t *ce;
15342542f5fSchristos		xcb_generic_event_t *ev;
15442542f5fSchristos
15542542f5fSchristos		ev = xcb_wait_for_special_event(c, q);
15642542f5fSchristos		if (ev == NULL)
15742542f5fSchristos			break;
15842542f5fSchristos
15942542f5fSchristos		ce = (xcb_present_complete_notify_event_t *)ev;
160fe8aea9eSmrg		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
161fe8aea9eSmrg		    ce->serial == (serial ^ 0xcc00ffee)) {
162fe8aea9eSmrg			msc = ce->msc;
163fe8aea9eSmrg			if (ust)
164fe8aea9eSmrg				*ust = ce->ust;
165fe8aea9eSmrg			complete = 1;
166fe8aea9eSmrg		}
167fe8aea9eSmrg		free(ev);
168fe8aea9eSmrg	} while (!complete);
169fe8aea9eSmrg
170fe8aea9eSmrg	if ((int64_t)(msc - last_msc) < 0) {
171fe8aea9eSmrg		printf("Invalid MSC: was %llu, now %llu\n",
172fe8aea9eSmrg		       (long long)last_msc, (long long)msc);
173fe8aea9eSmrg	}
174fe8aea9eSmrg
175fe8aea9eSmrg	if (++serial == 0)
176fe8aea9eSmrg		serial = 1;
177fe8aea9eSmrg
178fe8aea9eSmrg	return msc;
179fe8aea9eSmrg}
180fe8aea9eSmrg
181fe8aea9eSmrgstatic uint64_t wait_vblank(Display *dpy, Window win, void *q)
182fe8aea9eSmrg{
183fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
184fe8aea9eSmrg	static uint32_t serial = 1;
185fe8aea9eSmrg	uint64_t msc = 0;
186fe8aea9eSmrg	int complete = 0;
187fe8aea9eSmrg
188fe8aea9eSmrg	xcb_present_notify_msc(c, win, serial ^ 0xdeadbeef, 0, 1, 0);
189fe8aea9eSmrg	xcb_flush(c);
190fe8aea9eSmrg
191fe8aea9eSmrg	do {
192fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
193fe8aea9eSmrg		xcb_generic_event_t *ev;
194fe8aea9eSmrg
195fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, q);
196fe8aea9eSmrg		if (ev == NULL)
197fe8aea9eSmrg			break;
198fe8aea9eSmrg
199fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
200fe8aea9eSmrg		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
201fe8aea9eSmrg		    ce->serial == (serial ^ 0xdeadbeef)) {
20242542f5fSchristos			msc = ce->msc;
203fe8aea9eSmrg			complete = 1;
204fe8aea9eSmrg		}
205fe8aea9eSmrg		free(ev);
206fe8aea9eSmrg	} while (!complete);
207fe8aea9eSmrg
208fe8aea9eSmrg	if (++serial == 0)
209fe8aea9eSmrg		serial = 1;
210fe8aea9eSmrg
211fe8aea9eSmrg	return msc;
212fe8aea9eSmrg}
213fe8aea9eSmrg
214fe8aea9eSmrgstatic uint64_t msc_interval(Display *dpy, Window win, void *q)
215fe8aea9eSmrg{
216fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
217fe8aea9eSmrg	uint64_t msc, ust;
218fe8aea9eSmrg	int complete = 0;
219fe8aea9eSmrg
220fe8aea9eSmrg	msc = check_msc(dpy, win, q, 0, NULL);
221fe8aea9eSmrg
222fe8aea9eSmrg	xcb_present_notify_msc(c, win, 0xc0ffee00, msc, 0, 0);
223fe8aea9eSmrg	xcb_present_notify_msc(c, win, 0xc0ffee01, msc + 10, 0, 0);
224fe8aea9eSmrg	xcb_flush(c);
225fe8aea9eSmrg
226fe8aea9eSmrg	ust = msc = 0;
227fe8aea9eSmrg	do {
228fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
229fe8aea9eSmrg		xcb_generic_event_t *ev;
230fe8aea9eSmrg
231fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, q);
232fe8aea9eSmrg		if (ev == NULL)
233fe8aea9eSmrg			break;
234fe8aea9eSmrg
235fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
236fe8aea9eSmrg		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
237fe8aea9eSmrg		    ce->serial == 0xc0ffee00) {
238fe8aea9eSmrg			msc -= ce->msc;
239fe8aea9eSmrg			ust -= ce->ust;
240fe8aea9eSmrg			complete++;
241fe8aea9eSmrg		}
242fe8aea9eSmrg		if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC &&
243fe8aea9eSmrg		    ce->serial == 0xc0ffee01) {
244fe8aea9eSmrg			msc += ce->msc;
245fe8aea9eSmrg			ust += ce->ust;
246fe8aea9eSmrg			complete++;
247fe8aea9eSmrg		}
248fe8aea9eSmrg		free(ev);
249fe8aea9eSmrg	} while (complete != 2);
250fe8aea9eSmrg
251fe8aea9eSmrg	printf("10 frame interval: msc=%lld, ust=%lld\n",
252fe8aea9eSmrg	       (long long)msc, (long long)ust);
253fe8aea9eSmrg	XSync(dpy, True);
254fe8aea9eSmrg	if (msc == 0)
255fe8aea9eSmrg		return 0;
256fe8aea9eSmrg
257fe8aea9eSmrg	return (ust + msc/2) / msc;
258fe8aea9eSmrg}
259fe8aea9eSmrg
260fe8aea9eSmrgstatic void teardown_msc(Display *dpy, void *q)
261fe8aea9eSmrg{
262fe8aea9eSmrg	xcb_unregister_for_special_event(XGetXCBConnection(dpy), q);
263fe8aea9eSmrg}
264fe8aea9eSmrg
265fe8aea9eSmrgstatic int test_whole(Display *dpy, Window win, const char *phase)
266fe8aea9eSmrg{
267fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
268fe8aea9eSmrg	Pixmap pixmap;
269fe8aea9eSmrg	struct dri3_fence fence;
270fe8aea9eSmrg	Window root;
271fe8aea9eSmrg	unsigned int width, height;
272fe8aea9eSmrg	unsigned border, depth;
273fe8aea9eSmrg	int x, y, ret = 1;
274fe8aea9eSmrg
275fe8aea9eSmrg	XGetGeometry(dpy, win,
276fe8aea9eSmrg		     &root, &x, &y, &width, &height, &border, &depth);
277fe8aea9eSmrg
278fe8aea9eSmrg	if (dri3_create_fence(dpy, win, &fence))
279fe8aea9eSmrg		return 0;
280fe8aea9eSmrg
281fe8aea9eSmrg	printf("%s: Testing simple flip: %dx%d\n", phase, width, height);
282fe8aea9eSmrg	_x_error_occurred = 0;
283fe8aea9eSmrg
284fe8aea9eSmrg	xshmfence_reset(fence.addr);
285fe8aea9eSmrg
286fe8aea9eSmrg	pixmap = XCreatePixmap(dpy, win, width, height, depth);
287fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap, 0,
288fe8aea9eSmrg			   0, /* valid */
289fe8aea9eSmrg			   0, /* update */
290fe8aea9eSmrg			   0, /* x_off */
291fe8aea9eSmrg			   0, /* y_off */
292fe8aea9eSmrg			   None,
293fe8aea9eSmrg			   None, /* wait fence */
294fe8aea9eSmrg			   fence.xid,
295fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
296fe8aea9eSmrg			   0, /* target msc */
297fe8aea9eSmrg			   0, /* divisor */
298fe8aea9eSmrg			   0, /* remainder */
299fe8aea9eSmrg			   0, NULL);
300fe8aea9eSmrg	XFreePixmap(dpy, pixmap);
301fe8aea9eSmrg
302fe8aea9eSmrg	pixmap = XCreatePixmap(dpy, win, width, height, depth);
303fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap, 0,
304fe8aea9eSmrg			   0, /* valid */
305fe8aea9eSmrg			   0, /* update */
306fe8aea9eSmrg			   0, /* x_off */
307fe8aea9eSmrg			   0, /* y_off */
308fe8aea9eSmrg			   None,
309fe8aea9eSmrg			   None, /* wait fence */
310fe8aea9eSmrg			   None, /* sync fence */
311fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
312fe8aea9eSmrg			   0, /* target msc */
313fe8aea9eSmrg			   0, /* divisor */
314fe8aea9eSmrg			   0, /* remainder */
315fe8aea9eSmrg			   0, NULL);
316fe8aea9eSmrg	XFreePixmap(dpy, pixmap);
317fe8aea9eSmrg	XFlush(dpy);
318fe8aea9eSmrg
319fe8aea9eSmrg	ret = !!xshmfence_await(fence.addr);
320fe8aea9eSmrg	dri3_fence_free(dpy, &fence);
321fe8aea9eSmrg
322fe8aea9eSmrg	XSync(dpy, True);
323fe8aea9eSmrg	ret += !!_x_error_occurred;
324fe8aea9eSmrg
325fe8aea9eSmrg	return ret;
326fe8aea9eSmrg}
327fe8aea9eSmrg
328fe8aea9eSmrgstatic uint64_t flush_flips(Display *dpy, Window win, Pixmap pixmap, void *Q, uint64_t *ust)
329fe8aea9eSmrg{
330fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
331fe8aea9eSmrg	uint64_t msc;
332fe8aea9eSmrg	int complete;
333fe8aea9eSmrg
334fe8aea9eSmrg	msc = check_msc(dpy, win, Q, 0, NULL);
335fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap,
336fe8aea9eSmrg			   0xdeadbeef, /* serial */
337fe8aea9eSmrg			   0, /* valid */
338fe8aea9eSmrg			   0, /* update */
339fe8aea9eSmrg			   0, /* x_off */
340fe8aea9eSmrg			   0, /* y_off */
341fe8aea9eSmrg			   None,
342fe8aea9eSmrg			   None, /* wait fence */
343fe8aea9eSmrg			   None,
344fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
345fe8aea9eSmrg			   msc + 60, /* target msc */
346fe8aea9eSmrg			   0, /* divisor */
347fe8aea9eSmrg			   0, /* remainder */
348fe8aea9eSmrg			   0, NULL);
349fe8aea9eSmrg	xcb_flush(c);
350fe8aea9eSmrg	complete = 0;
351fe8aea9eSmrg	do {
352fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
353fe8aea9eSmrg		xcb_generic_event_t *ev;
354fe8aea9eSmrg
355fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
356fe8aea9eSmrg		if (ev == NULL)
357fe8aea9eSmrg			break;
358fe8aea9eSmrg
359fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
360fe8aea9eSmrg		complete = (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP &&
361fe8aea9eSmrg			    ce->serial == 0xdeadbeef);
362fe8aea9eSmrg		free(ev);
363fe8aea9eSmrg	} while (!complete);
364fe8aea9eSmrg	XSync(dpy, True);
365fe8aea9eSmrg
366fe8aea9eSmrg	return check_msc(dpy, win, Q, msc, ust);
367fe8aea9eSmrg}
368fe8aea9eSmrg
369fe8aea9eSmrgstatic int test_double(Display *dpy, Window win, const char *phase, void *Q)
370fe8aea9eSmrg{
371fe8aea9eSmrg#define COUNT (15*60)
372fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
373fe8aea9eSmrg	Pixmap pixmap;
374fe8aea9eSmrg	Window root;
375fe8aea9eSmrg	unsigned int width, height;
376fe8aea9eSmrg	unsigned border, depth;
377fe8aea9eSmrg	int x, y, n, ret;
378fe8aea9eSmrg	struct {
379fe8aea9eSmrg		uint64_t msc, ust;
380fe8aea9eSmrg	} frame[COUNT+1];
381fe8aea9eSmrg	int offset = 0;
382fe8aea9eSmrg
383fe8aea9eSmrg	XGetGeometry(dpy, win,
384fe8aea9eSmrg		     &root, &x, &y, &width, &height, &border, &depth);
385fe8aea9eSmrg
386fe8aea9eSmrg	printf("%s: Testing flip double buffering: %dx%d\n", phase, width, height);
387fe8aea9eSmrg	_x_error_occurred = 0;
388fe8aea9eSmrg
389fe8aea9eSmrg	pixmap = XCreatePixmap(dpy, win, width, height, depth);
390fe8aea9eSmrg	flush_flips(dpy, win, pixmap, Q, NULL);
391fe8aea9eSmrg	for (n = 0; n <= COUNT; n++) {
392fe8aea9eSmrg		int complete;
393fe8aea9eSmrg
394fe8aea9eSmrg		xcb_present_pixmap(c, win, pixmap, n,
395fe8aea9eSmrg				   0, /* valid */
396fe8aea9eSmrg				   0, /* update */
397fe8aea9eSmrg				   0, /* x_off */
398fe8aea9eSmrg				   0, /* y_off */
399fe8aea9eSmrg				   None,
400fe8aea9eSmrg				   None, /* wait fence */
401fe8aea9eSmrg				   None,
402fe8aea9eSmrg				   XCB_PRESENT_OPTION_NONE,
403fe8aea9eSmrg				   0, /* target msc */
404fe8aea9eSmrg				   0, /* divisor */
405fe8aea9eSmrg				   0, /* remainder */
406fe8aea9eSmrg				   0, NULL);
407fe8aea9eSmrg		xcb_flush(c);
408fe8aea9eSmrg
409fe8aea9eSmrg		complete = 0;
410fe8aea9eSmrg		do {
411fe8aea9eSmrg			xcb_present_complete_notify_event_t *ce;
412fe8aea9eSmrg			xcb_generic_event_t *ev;
413fe8aea9eSmrg
414fe8aea9eSmrg			ev = xcb_wait_for_special_event(c, Q);
415fe8aea9eSmrg			if (ev == NULL)
416fe8aea9eSmrg				break;
417fe8aea9eSmrg
418fe8aea9eSmrg			ce = (xcb_present_complete_notify_event_t *)ev;
419fe8aea9eSmrg			if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP &&
420fe8aea9eSmrg			    ce->serial == n) {
421fe8aea9eSmrg				frame[n].msc = ce->msc;
422fe8aea9eSmrg				frame[n].ust = ce->ust;
423fe8aea9eSmrg				complete = 1;
424fe8aea9eSmrg			}
425fe8aea9eSmrg			free(ev);
426fe8aea9eSmrg		} while (!complete);
427fe8aea9eSmrg	}
428fe8aea9eSmrg	XFreePixmap(dpy, pixmap);
429fe8aea9eSmrg
430fe8aea9eSmrg	XSync(dpy, True);
431fe8aea9eSmrg	ret = !!_x_error_occurred;
432fe8aea9eSmrg
433fe8aea9eSmrg	if (frame[COUNT].msc - frame[0].msc != COUNT) {
434fe8aea9eSmrg		printf("Expected %d frames interval, %d elapsed instead\n",
435fe8aea9eSmrg		       COUNT, (int)(frame[COUNT].msc - frame[0].msc));
436fe8aea9eSmrg		for (n = 0; n <= COUNT; n++) {
437fe8aea9eSmrg			if (frame[n].msc - frame[0].msc != n + offset) {
438fe8aea9eSmrg				printf("frame[%d]: msc=%03lld, ust=%lld\n", n,
439fe8aea9eSmrg				       (long long)(frame[n].msc - frame[0].msc),
440fe8aea9eSmrg				       (long long)(frame[n].ust - frame[0].ust));
441fe8aea9eSmrg				offset = frame[n].msc - frame[0].msc - n;
442fe8aea9eSmrg				ret++;
443fe8aea9eSmrg			}
444fe8aea9eSmrg		}
445fe8aea9eSmrg	}
446fe8aea9eSmrg
447fe8aea9eSmrg	return ret;
448fe8aea9eSmrg}
449fe8aea9eSmrg
450fe8aea9eSmrgstatic int test_future(Display *dpy, Window win, const char *phase, void *Q)
451fe8aea9eSmrg{
452fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
453fe8aea9eSmrg	Pixmap pixmap;
454fe8aea9eSmrg	struct dri3_fence fence;
455fe8aea9eSmrg	Window root;
456fe8aea9eSmrg	unsigned int width, height;
457fe8aea9eSmrg	unsigned border, depth;
458fe8aea9eSmrg	int x, y, ret = 0, n;
459fe8aea9eSmrg	uint64_t msc, ust;
460fe8aea9eSmrg	int complete, count;
461fe8aea9eSmrg	int early = 0, late = 0;
462fe8aea9eSmrg	int earliest = 0, latest = 0;
463fe8aea9eSmrg	uint64_t interval;
464fe8aea9eSmrg
465fe8aea9eSmrg	XGetGeometry(dpy, win,
466fe8aea9eSmrg		     &root, &x, &y, &width, &height, &border, &depth);
467fe8aea9eSmrg
468fe8aea9eSmrg	if (dri3_create_fence(dpy, win, &fence))
469fe8aea9eSmrg		return 0;
470fe8aea9eSmrg
471fe8aea9eSmrg	printf("%s: Testing flips into the future: %dx%d\n", phase, width, height);
472fe8aea9eSmrg	_x_error_occurred = 0;
473fe8aea9eSmrg
474fe8aea9eSmrg	interval = msc_interval(dpy, win, Q);
475fe8aea9eSmrg	if (interval == 0) {
476fe8aea9eSmrg		printf("Zero delay between frames\n");
477fe8aea9eSmrg		return 1;
478fe8aea9eSmrg	}
479fe8aea9eSmrg
480fe8aea9eSmrg	pixmap = XCreatePixmap(dpy, win, width, height, depth);
481fe8aea9eSmrg	msc = flush_flips(dpy, win, pixmap, Q, &ust);
482fe8aea9eSmrg	for (n = 1; n <= 10; n++)
483fe8aea9eSmrg		xcb_present_pixmap(c, win, pixmap,
484fe8aea9eSmrg				   n, /* serial */
485fe8aea9eSmrg				   0, /* valid */
486fe8aea9eSmrg				   0, /* update */
487fe8aea9eSmrg				   0, /* x_off */
488fe8aea9eSmrg				   0, /* y_off */
489fe8aea9eSmrg				   None,
490fe8aea9eSmrg				   None, /* wait fence */
491fe8aea9eSmrg				   None,
492fe8aea9eSmrg				   XCB_PRESENT_OPTION_NONE,
493fe8aea9eSmrg				   msc + 60 + n*15*60, /* target msc */
494fe8aea9eSmrg				   0, /* divisor */
495fe8aea9eSmrg				   0, /* remainder */
496fe8aea9eSmrg				   0, NULL);
497fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap,
498fe8aea9eSmrg			   0xdeadbeef, /* serial */
499fe8aea9eSmrg			   0, /* valid */
500fe8aea9eSmrg			   0, /* update */
501fe8aea9eSmrg			   0, /* x_off */
502fe8aea9eSmrg			   0, /* y_off */
503fe8aea9eSmrg			   None,
504fe8aea9eSmrg			   None, /* wait fence */
505fe8aea9eSmrg			   None,
506fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
507fe8aea9eSmrg			   msc + 60 + n*15*60, /* target msc */
508fe8aea9eSmrg			   0, /* divisor */
509fe8aea9eSmrg			   0, /* remainder */
510fe8aea9eSmrg			   0, NULL);
511fe8aea9eSmrg	xcb_flush(c);
512fe8aea9eSmrg
513fe8aea9eSmrg	complete = 0;
514fe8aea9eSmrg	count = 0;
515fe8aea9eSmrg	do {
516fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
517fe8aea9eSmrg		xcb_generic_event_t *ev;
518fe8aea9eSmrg
519fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
520fe8aea9eSmrg		if (ev == NULL)
521fe8aea9eSmrg			break;
522fe8aea9eSmrg
523fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
524fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
525fe8aea9eSmrg
526fe8aea9eSmrg		if (ce->serial == 0xdeadbeef) {
527fe8aea9eSmrg			int64_t time;
528fe8aea9eSmrg
529fe8aea9eSmrg			time = ce->ust - (ust + (60 + 15*60*n) * interval);
530fe8aea9eSmrg			if (time < -(int64_t)interval) {
531fe8aea9eSmrg				fprintf(stderr,
532fe8aea9eSmrg					"\tflips completed too early by %lldms\n",
533fe8aea9eSmrg					(long long)(-time / 1000));
534fe8aea9eSmrg			} else if (time > (int64_t)interval) {
535fe8aea9eSmrg				fprintf(stderr,
536fe8aea9eSmrg					"\tflips completed too late by %lldms\n",
537fe8aea9eSmrg					(long long)(time / 1000));
538fe8aea9eSmrg			}
539fe8aea9eSmrg			complete = 1;
540fe8aea9eSmrg		} else {
541fe8aea9eSmrg			int diff = (int64_t)(ce->msc - (15*60*ce->serial + msc + 60));
542fe8aea9eSmrg			if (diff < 0) {
543fe8aea9eSmrg				if (-diff > earliest) {
544fe8aea9eSmrg					fprintf(stderr, "\tframe %d displayed early by %d frames\n", ce->serial, -diff);
545fe8aea9eSmrg					earliest = -diff;
546fe8aea9eSmrg				}
547fe8aea9eSmrg				early++;
548fe8aea9eSmrg				ret++;
549fe8aea9eSmrg			} else if (diff > 0) {
550fe8aea9eSmrg				if (diff > latest) {
551fe8aea9eSmrg					fprintf(stderr, "\tframe %d displayed late by %d frames\n", ce->serial, diff);
552fe8aea9eSmrg					latest = diff;
553fe8aea9eSmrg				}
554fe8aea9eSmrg				late++;
555fe8aea9eSmrg				ret++;
556fe8aea9eSmrg			}
557fe8aea9eSmrg			count++;
558fe8aea9eSmrg		}
559fe8aea9eSmrg		free(ev);
560fe8aea9eSmrg	} while (!complete);
561fe8aea9eSmrg
562fe8aea9eSmrg	if (early)
563fe8aea9eSmrg		printf("\t%d frames shown too early (worst %d)!\n", early, earliest);
564fe8aea9eSmrg	if (late)
565fe8aea9eSmrg		printf("\t%d frames shown too late (worst %d)!\n", late, latest);
566fe8aea9eSmrg
567fe8aea9eSmrg	if (count != 10) {
568fe8aea9eSmrg		fprintf(stderr, "Sentinel frame received too early! %d frames outstanding\n", 10 - count);
569fe8aea9eSmrg		ret++;
570fe8aea9eSmrg
571fe8aea9eSmrg		do {
572fe8aea9eSmrg			xcb_present_complete_notify_event_t *ce;
573fe8aea9eSmrg			xcb_generic_event_t *ev;
574fe8aea9eSmrg
575fe8aea9eSmrg			ev = xcb_wait_for_special_event(c, Q);
576fe8aea9eSmrg			if (ev == NULL)
577fe8aea9eSmrg				break;
578fe8aea9eSmrg
579fe8aea9eSmrg			ce = (xcb_present_complete_notify_event_t *)ev;
580fe8aea9eSmrg			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
581fe8aea9eSmrg			free(ev);
582fe8aea9eSmrg		} while (++count != 10);
583fe8aea9eSmrg	}
584fe8aea9eSmrg
585fe8aea9eSmrg	ret += !!_x_error_occurred;
586fe8aea9eSmrg
587fe8aea9eSmrg	return ret;
588fe8aea9eSmrg}
589fe8aea9eSmrg
590fe8aea9eSmrgstatic int test_exhaustion(Display *dpy, Window win, const char *phase, void *Q)
591fe8aea9eSmrg{
592fe8aea9eSmrg#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */
593fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
594fe8aea9eSmrg	Pixmap pixmap;
595fe8aea9eSmrg	struct dri3_fence fence[2];
596fe8aea9eSmrg	Window root;
597fe8aea9eSmrg	xcb_xfixes_region_t region;
598fe8aea9eSmrg	unsigned int width, height;
599fe8aea9eSmrg	unsigned border, depth;
600fe8aea9eSmrg	int x, y, ret = 0, n;
601fe8aea9eSmrg	uint64_t target, final;
602fe8aea9eSmrg
603fe8aea9eSmrg	XGetGeometry(dpy, win,
604fe8aea9eSmrg		     &root, &x, &y, &width, &height, &border, &depth);
605fe8aea9eSmrg
606fe8aea9eSmrg	if (dri3_create_fence(dpy, win, &fence[0]) ||
607fe8aea9eSmrg	    dri3_create_fence(dpy, win, &fence[1]))
608fe8aea9eSmrg		return 0;
609fe8aea9eSmrg
610fe8aea9eSmrg	printf("%s: Testing flips with long vblank queues: %dx%d\n", phase, width, height);
611fe8aea9eSmrg	_x_error_occurred = 0;
612fe8aea9eSmrg
613fe8aea9eSmrg	region = xcb_generate_id(c);
614fe8aea9eSmrg	xcb_xfixes_create_region(c, region, 0, NULL);
615fe8aea9eSmrg
616fe8aea9eSmrg	pixmap = XCreatePixmap(dpy, win, width, height, depth);
617fe8aea9eSmrg	xshmfence_reset(fence[0].addr);
618fe8aea9eSmrg	xshmfence_reset(fence[1].addr);
619fe8aea9eSmrg	target = check_msc(dpy, win, Q, 0, NULL);
620fe8aea9eSmrg	for (n = N_VBLANKS; n--; )
621fe8aea9eSmrg		xcb_present_pixmap(c, win, pixmap, 0,
622fe8aea9eSmrg				   0, /* valid */
623fe8aea9eSmrg				   region, /* update */
624fe8aea9eSmrg				   0, /* x_off */
625fe8aea9eSmrg				   0, /* y_off */
626fe8aea9eSmrg				   None,
627fe8aea9eSmrg				   None, /* wait fence */
628fe8aea9eSmrg				   None,
629fe8aea9eSmrg				   XCB_PRESENT_OPTION_NONE,
630fe8aea9eSmrg				   target + N_VBLANKS, /* target msc */
631fe8aea9eSmrg				   1, /* divisor */
632fe8aea9eSmrg				   0, /* remainder */
633fe8aea9eSmrg				   0, NULL);
634fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap, 0,
635fe8aea9eSmrg			   region, /* valid */
636fe8aea9eSmrg			   region, /* update */
637fe8aea9eSmrg			   0, /* x_off */
638fe8aea9eSmrg			   0, /* y_off */
639fe8aea9eSmrg			   None,
640fe8aea9eSmrg			   None, /* wait fence */
641fe8aea9eSmrg			   fence[0].xid,
642fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
643fe8aea9eSmrg			   target, /* target msc */
644fe8aea9eSmrg			   0, /* divisor */
645fe8aea9eSmrg			   0, /* remainder */
646fe8aea9eSmrg			   0, NULL);
647fe8aea9eSmrg	for (n = 1; n < N_VBLANKS; n++)
648fe8aea9eSmrg		xcb_present_pixmap(c, win, pixmap, 0,
649fe8aea9eSmrg				   region, /* valid */
650fe8aea9eSmrg				   region, /* update */
651fe8aea9eSmrg				   0, /* x_off */
652fe8aea9eSmrg				   0, /* y_off */
653fe8aea9eSmrg				   None,
654fe8aea9eSmrg				   None, /* wait fence */
655fe8aea9eSmrg				   None,
656fe8aea9eSmrg				   XCB_PRESENT_OPTION_NONE,
657fe8aea9eSmrg				   target + n, /* target msc */
658fe8aea9eSmrg				   0, /* divisor */
659fe8aea9eSmrg				   0, /* remainder */
660fe8aea9eSmrg				   0, NULL);
661fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap, 0,
662fe8aea9eSmrg			   region, /* valid */
663fe8aea9eSmrg			   region, /* update */
664fe8aea9eSmrg			   0, /* x_off */
665fe8aea9eSmrg			   0, /* y_off */
666fe8aea9eSmrg			   None,
667fe8aea9eSmrg			   None, /* wait fence */
668fe8aea9eSmrg			   fence[1].xid,
669fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
670fe8aea9eSmrg			   target + N_VBLANKS, /* target msc */
671fe8aea9eSmrg			   0, /* divisor */
672fe8aea9eSmrg			   0, /* remainder */
673fe8aea9eSmrg			   0, NULL);
674fe8aea9eSmrg	xcb_flush(c);
675fe8aea9eSmrg
676fe8aea9eSmrg	ret += !!xshmfence_await(fence[0].addr);
677fe8aea9eSmrg	final = check_msc(dpy, win, Q, 0, NULL);
678fe8aea9eSmrg	if (final < target) {
679fe8aea9eSmrg		printf("\tFirst flip too early, MSC was %llu, expected %llu\n",
680fe8aea9eSmrg		       (long long)final, (long long)target);
681fe8aea9eSmrg		ret++;
682fe8aea9eSmrg	} else if (final > target + 1) {
683fe8aea9eSmrg		printf("\tFirst flip too late, MSC was %llu, expected %llu\n",
684fe8aea9eSmrg		       (long long)final, (long long)target);
685fe8aea9eSmrg		ret++;
686fe8aea9eSmrg	}
687fe8aea9eSmrg
688fe8aea9eSmrg	ret += !!xshmfence_await(fence[1].addr);
689fe8aea9eSmrg	final = check_msc(dpy, win, Q, 0, NULL);
690fe8aea9eSmrg	if (final < target + N_VBLANKS) {
691fe8aea9eSmrg		printf("\tLast flip too early, MSC was %llu, expected %llu\n",
692fe8aea9eSmrg		       (long long)final, (long long)(target + N_VBLANKS));
693fe8aea9eSmrg		ret++;
694fe8aea9eSmrg	} else if (final > target + N_VBLANKS + 1) {
695fe8aea9eSmrg		printf("\tLast flip too late, MSC was %llu, expected %llu\n",
696fe8aea9eSmrg		       (long long)final, (long long)(target + N_VBLANKS));
697fe8aea9eSmrg		ret++;
698fe8aea9eSmrg	}
699fe8aea9eSmrg
700fe8aea9eSmrg	flush_flips(dpy, win, pixmap, Q, NULL);
701fe8aea9eSmrg
702fe8aea9eSmrg	XFreePixmap(dpy, pixmap);
703fe8aea9eSmrg	xcb_xfixes_destroy_region(c, region);
704fe8aea9eSmrg	dri3_fence_free(dpy, &fence[1]);
705fe8aea9eSmrg	dri3_fence_free(dpy, &fence[0]);
706fe8aea9eSmrg
707fe8aea9eSmrg	XSync(dpy, True);
708fe8aea9eSmrg	ret += !!_x_error_occurred;
709fe8aea9eSmrg
710fe8aea9eSmrg	return ret;
711fe8aea9eSmrg#undef N_VBLANKS
712fe8aea9eSmrg}
713fe8aea9eSmrg
714fe8aea9eSmrgstatic int test_accuracy(Display *dpy, Window win, const char *phase, void *Q)
715fe8aea9eSmrg{
716fe8aea9eSmrg#define N_VBLANKS (60 * 120) /* ~2 minutes */
717fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
718fe8aea9eSmrg	Pixmap pixmap;
719fe8aea9eSmrg	Window root;
720fe8aea9eSmrg	unsigned int width, height;
721fe8aea9eSmrg	unsigned border, depth;
722fe8aea9eSmrg	int x, y, ret = 0, n;
723fe8aea9eSmrg	uint64_t target;
724fe8aea9eSmrg	int early = 0, late = 0;
725fe8aea9eSmrg	int earliest = 0, latest = 0;
726fe8aea9eSmrg	int complete, count;
727fe8aea9eSmrg
728fe8aea9eSmrg	XGetGeometry(dpy, win,
729fe8aea9eSmrg		     &root, &x, &y, &width, &height, &border, &depth);
730fe8aea9eSmrg
731fe8aea9eSmrg	printf("%s: Testing flip accuracy: %dx%d\n", phase, width, height);
732fe8aea9eSmrg	_x_error_occurred = 0;
733fe8aea9eSmrg
734fe8aea9eSmrg	pixmap = XCreatePixmap(dpy, win, width, height, depth);
735fe8aea9eSmrg	target = flush_flips(dpy, win, pixmap, Q, NULL);
736fe8aea9eSmrg	for (n = 0; n <= N_VBLANKS; n++)
737fe8aea9eSmrg		xcb_present_pixmap(c, win, pixmap,
738fe8aea9eSmrg				   n, /* serial */
739fe8aea9eSmrg				   0, /* valid */
740fe8aea9eSmrg				   0, /* update */
741fe8aea9eSmrg				   0, /* x_off */
742fe8aea9eSmrg				   0, /* y_off */
743fe8aea9eSmrg				   None,
744fe8aea9eSmrg				   None, /* wait fence */
745fe8aea9eSmrg				   None,
746fe8aea9eSmrg				   XCB_PRESENT_OPTION_NONE,
747fe8aea9eSmrg				   target + 60 + n, /* target msc */
748fe8aea9eSmrg				   0, /* divisor */
749fe8aea9eSmrg				   0, /* remainder */
750fe8aea9eSmrg				   0, NULL);
751fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap,
752fe8aea9eSmrg			   0xdeadbeef, /* serial */
753fe8aea9eSmrg			   0, /* valid */
754fe8aea9eSmrg			   0, /* update */
755fe8aea9eSmrg			   0, /* x_off */
756fe8aea9eSmrg			   0, /* y_off */
757fe8aea9eSmrg			   None,
758fe8aea9eSmrg			   None, /* wait fence */
759fe8aea9eSmrg			   None,
760fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
761fe8aea9eSmrg			   target + 60 + n, /* target msc */
762fe8aea9eSmrg			   0, /* divisor */
763fe8aea9eSmrg			   0, /* remainder */
764fe8aea9eSmrg			   0, NULL);
765fe8aea9eSmrg	xcb_flush(c);
766fe8aea9eSmrg
767fe8aea9eSmrg	complete = 0;
768fe8aea9eSmrg	count = 0;
769fe8aea9eSmrg	do {
770fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
771fe8aea9eSmrg		xcb_generic_event_t *ev;
772fe8aea9eSmrg
773fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
774fe8aea9eSmrg		if (ev == NULL)
775fe8aea9eSmrg			break;
776fe8aea9eSmrg
777fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
778fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
779fe8aea9eSmrg
780fe8aea9eSmrg		if (ce->serial != 0xdeadbeef) {
781fe8aea9eSmrg			int diff = (int64_t)(ce->msc - (target + ce->serial + 60));
782fe8aea9eSmrg			if (diff < 0) {
783fe8aea9eSmrg				if (-diff > earliest) {
784fe8aea9eSmrg					fprintf(stderr, "\tframe %d displayed early by %d frames\n", ce->serial, -diff);
785fe8aea9eSmrg					earliest = -diff;
786fe8aea9eSmrg				}
787fe8aea9eSmrg				early++;
788fe8aea9eSmrg				ret++;
789fe8aea9eSmrg			} else if (diff > 0) {
790fe8aea9eSmrg				if (diff > latest) {
791fe8aea9eSmrg					fprintf(stderr, "\tframe %d displayed late by %d frames\n", ce->serial, diff);
792fe8aea9eSmrg					latest = diff;
793fe8aea9eSmrg				}
794fe8aea9eSmrg				late++;
795fe8aea9eSmrg				ret++;
796fe8aea9eSmrg			}
797fe8aea9eSmrg			count++;
798fe8aea9eSmrg		} else
799fe8aea9eSmrg			complete = 1;
80042542f5fSchristos		free(ev);
801fe8aea9eSmrg	} while (!complete);
802fe8aea9eSmrg
803fe8aea9eSmrg	if (early)
804fe8aea9eSmrg		printf("\t%d frames shown too early (worst %d)!\n", early, earliest);
805fe8aea9eSmrg	if (late)
806fe8aea9eSmrg		printf("\t%d frames shown too late (worst %d)!\n", late, latest);
807fe8aea9eSmrg
808fe8aea9eSmrg	if (count != N_VBLANKS+1) {
809fe8aea9eSmrg		fprintf(stderr, "Sentinel frame received too early! %d frames outstanding\n", N_VBLANKS+1 - count);
810fe8aea9eSmrg		ret++;
811fe8aea9eSmrg		do {
812fe8aea9eSmrg			xcb_present_complete_notify_event_t *ce;
813fe8aea9eSmrg			xcb_generic_event_t *ev;
814fe8aea9eSmrg
815fe8aea9eSmrg			ev = xcb_wait_for_special_event(c, Q);
816fe8aea9eSmrg			if (ev == NULL)
817fe8aea9eSmrg				break;
818fe8aea9eSmrg
819fe8aea9eSmrg			ce = (xcb_present_complete_notify_event_t *)ev;
820fe8aea9eSmrg			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP);
821fe8aea9eSmrg			free(ev);
822fe8aea9eSmrg		} while (++count != N_VBLANKS+1);
823fe8aea9eSmrg	}
824fe8aea9eSmrg
825fe8aea9eSmrg	XFreePixmap(dpy, pixmap);
826fe8aea9eSmrg
827fe8aea9eSmrg	XSync(dpy, True);
828fe8aea9eSmrg	ret += !!_x_error_occurred;
829fe8aea9eSmrg
830fe8aea9eSmrg	return ret;
831fe8aea9eSmrg#undef N_VBLANKS
832fe8aea9eSmrg}
833fe8aea9eSmrg
834fe8aea9eSmrgstatic int test_modulus(Display *dpy, Window win, const char *phase, void *Q)
835fe8aea9eSmrg{
836fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
837fe8aea9eSmrg	Pixmap pixmap;
838fe8aea9eSmrg	Window root;
839fe8aea9eSmrg	unsigned int width, height;
840fe8aea9eSmrg	unsigned border, depth;
841fe8aea9eSmrg	xcb_xfixes_region_t region;
842fe8aea9eSmrg	int x, y, ret = 0;
843fe8aea9eSmrg	uint64_t target;
844fe8aea9eSmrg	int early = 0, late = 0;
845fe8aea9eSmrg	int earliest = 0, latest = 0;
846fe8aea9eSmrg	int complete, expect, count;
847fe8aea9eSmrg
848fe8aea9eSmrg	XGetGeometry(dpy, win,
849fe8aea9eSmrg		     &root, &x, &y, &width, &height, &border, &depth);
850fe8aea9eSmrg
851fe8aea9eSmrg	printf("%s: Testing flip modulus: %dx%d\n", phase, width, height);
852fe8aea9eSmrg	_x_error_occurred = 0;
853fe8aea9eSmrg
854fe8aea9eSmrg	region = xcb_generate_id(c);
855fe8aea9eSmrg	xcb_xfixes_create_region(c, region, 0, NULL);
856fe8aea9eSmrg
857fe8aea9eSmrg	pixmap = XCreatePixmap(dpy, win, width, height, depth);
858fe8aea9eSmrg	target = flush_flips(dpy, win, pixmap, Q, NULL);
859fe8aea9eSmrg	expect = 0;
860fe8aea9eSmrg	for (x = 1; x <= 7; x++) {
861fe8aea9eSmrg		for (y = 0; y < x; y++) {
862fe8aea9eSmrg			xcb_present_pixmap(c, win, pixmap,
863fe8aea9eSmrg					   y << 16 | x, /* serial */
864fe8aea9eSmrg					   region, /* valid */
865fe8aea9eSmrg					   region, /* update */
866fe8aea9eSmrg					   0, /* x_off */
867fe8aea9eSmrg					   0, /* y_off */
868fe8aea9eSmrg					   None,
869fe8aea9eSmrg					   None, /* wait fence */
870fe8aea9eSmrg					   None,
871fe8aea9eSmrg					   XCB_PRESENT_OPTION_NONE,
872fe8aea9eSmrg					   0, /* target msc */
873fe8aea9eSmrg					   x, /* divisor */
874fe8aea9eSmrg					   y, /* remainder */
875fe8aea9eSmrg					   0, NULL);
876fe8aea9eSmrg			expect++;
877fe8aea9eSmrg		}
878fe8aea9eSmrg	}
879fe8aea9eSmrg	xcb_present_pixmap(c, win, pixmap,
880fe8aea9eSmrg			   0xdeadbeef, /* serial */
881fe8aea9eSmrg			   0, /* valid */
882fe8aea9eSmrg			   0, /* update */
883fe8aea9eSmrg			   0, /* x_off */
884fe8aea9eSmrg			   0, /* y_off */
885fe8aea9eSmrg			   None,
886fe8aea9eSmrg			   None, /* wait fence */
887fe8aea9eSmrg			   None,
888fe8aea9eSmrg			   XCB_PRESENT_OPTION_NONE,
889fe8aea9eSmrg			   target + 2*x, /* target msc */
890fe8aea9eSmrg			   0, /* divisor */
891fe8aea9eSmrg			   0, /* remainder */
892fe8aea9eSmrg			   0, NULL);
893fe8aea9eSmrg	xcb_flush(c);
894fe8aea9eSmrg
895fe8aea9eSmrg	complete = 0;
896fe8aea9eSmrg	count = 0;
897fe8aea9eSmrg	do {
898fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
899fe8aea9eSmrg		xcb_generic_event_t *ev;
900fe8aea9eSmrg
901fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
902fe8aea9eSmrg		if (ev == NULL)
903fe8aea9eSmrg			break;
904fe8aea9eSmrg
905fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
906fe8aea9eSmrg		if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP)
907fe8aea9eSmrg			break;
908fe8aea9eSmrg
909fe8aea9eSmrg		assert(ce->serial);
910fe8aea9eSmrg		if (ce->serial != 0xdeadbeef) {
911fe8aea9eSmrg			uint64_t msc;
912fe8aea9eSmrg			int diff;
913fe8aea9eSmrg
914fe8aea9eSmrg			x = ce->serial & 0xffff;
915fe8aea9eSmrg			y = ce->serial >> 16;
916fe8aea9eSmrg
917fe8aea9eSmrg			msc = target;
918fe8aea9eSmrg			msc -= target % x;
919fe8aea9eSmrg			msc += y;
920fe8aea9eSmrg			if (msc <= target)
921fe8aea9eSmrg				msc += x;
922fe8aea9eSmrg
923fe8aea9eSmrg			diff = (int64_t)(ce->msc - msc);
924fe8aea9eSmrg			if (diff < 0) {
925fe8aea9eSmrg				if (-diff > earliest) {
926fe8aea9eSmrg					fprintf(stderr, "\tframe (%d, %d) displayed early by %d frames\n", y, x, -diff);
927fe8aea9eSmrg					earliest = -diff;
928fe8aea9eSmrg				}
929fe8aea9eSmrg				early++;
930fe8aea9eSmrg				ret++;
931fe8aea9eSmrg			} else if (diff > 0) {
932fe8aea9eSmrg				if (diff > latest) {
933fe8aea9eSmrg					fprintf(stderr, "\tframe (%d, %d) displayed late by %d frames\n", y, x, diff);
934fe8aea9eSmrg					latest = diff;
935fe8aea9eSmrg				}
936fe8aea9eSmrg				late++;
937fe8aea9eSmrg				ret++;
938fe8aea9eSmrg			}
939fe8aea9eSmrg			count++;
940fe8aea9eSmrg		} else
941fe8aea9eSmrg			complete = 1;
942fe8aea9eSmrg		free(ev);
943fe8aea9eSmrg	} while (!complete);
944fe8aea9eSmrg
945fe8aea9eSmrg	if (early)
946fe8aea9eSmrg		printf("\t%d frames shown too early (worst %d)!\n", early, earliest);
947fe8aea9eSmrg	if (late)
948fe8aea9eSmrg		printf("\t%d frames shown too late (worst %d)!\n", late, latest);
949fe8aea9eSmrg
950fe8aea9eSmrg	if (count != expect) {
951fe8aea9eSmrg		fprintf(stderr, "Sentinel frame received too early! %d frames outstanding\n", expect - count);
952fe8aea9eSmrg		ret++;
953fe8aea9eSmrg		do {
954fe8aea9eSmrg			xcb_present_complete_notify_event_t *ce;
955fe8aea9eSmrg			xcb_generic_event_t *ev;
956fe8aea9eSmrg
957fe8aea9eSmrg			ev = xcb_wait_for_special_event(c, Q);
958fe8aea9eSmrg			if (ev == NULL)
959fe8aea9eSmrg				break;
960fe8aea9eSmrg
961fe8aea9eSmrg			ce = (xcb_present_complete_notify_event_t *)ev;
962fe8aea9eSmrg			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
963fe8aea9eSmrg			free(ev);
964fe8aea9eSmrg		} while (++count != expect);
965fe8aea9eSmrg	}
966fe8aea9eSmrg
967fe8aea9eSmrg	XFreePixmap(dpy, pixmap);
968fe8aea9eSmrg	xcb_xfixes_destroy_region(c, region);
969fe8aea9eSmrg
970fe8aea9eSmrg	XSync(dpy, True);
971fe8aea9eSmrg	ret += !!_x_error_occurred;
972fe8aea9eSmrg
973fe8aea9eSmrg	return ret;
974fe8aea9eSmrg}
975fe8aea9eSmrg
976fe8aea9eSmrgstatic int test_future_msc(Display *dpy, void *Q)
977fe8aea9eSmrg{
978fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
979fe8aea9eSmrg	Window root = DefaultRootWindow(dpy);
980fe8aea9eSmrg	int ret = 0, n;
981fe8aea9eSmrg	uint64_t msc, ust;
982fe8aea9eSmrg	int complete, count;
983fe8aea9eSmrg	int early = 0, late = 0;
984fe8aea9eSmrg	int earliest = 0, latest = 0;
985fe8aea9eSmrg	uint64_t interval;
986fe8aea9eSmrg
987fe8aea9eSmrg	printf("Testing notifies into the future\n");
988fe8aea9eSmrg	_x_error_occurred = 0;
989fe8aea9eSmrg
990fe8aea9eSmrg	interval = msc_interval(dpy, root, Q);
991fe8aea9eSmrg	if (interval == 0) {
992fe8aea9eSmrg		printf("Zero delay between frames\n");
993fe8aea9eSmrg		return 1;
994fe8aea9eSmrg	}
995fe8aea9eSmrg	msc = check_msc(dpy, root, Q, 0, &ust);
996fe8aea9eSmrg	printf("Initial msc=%llx, interval between frames %lldus\n",
997fe8aea9eSmrg	       (long long)msc, (long long)interval);
998fe8aea9eSmrg
999fe8aea9eSmrg	for (n = 1; n <= 10; n++)
1000fe8aea9eSmrg		xcb_present_notify_msc(c, root, n, msc + 60 + n*15*60, 0, 0);
1001fe8aea9eSmrg	xcb_present_notify_msc(c, root, 0xdeadbeef, msc + 60 + n*15*60, 0, 0);
1002fe8aea9eSmrg	xcb_flush(c);
1003fe8aea9eSmrg
1004fe8aea9eSmrg	complete = 0;
1005fe8aea9eSmrg	count = 0;
1006fe8aea9eSmrg	do {
1007fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
1008fe8aea9eSmrg		xcb_generic_event_t *ev;
1009fe8aea9eSmrg
1010fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
1011fe8aea9eSmrg		if (ev == NULL)
1012fe8aea9eSmrg			break;
1013fe8aea9eSmrg
1014fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
1015fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1016fe8aea9eSmrg
1017fe8aea9eSmrg		if (ce->serial == 0xdeadbeef) {
1018fe8aea9eSmrg			int64_t time, tolerance;
1019fe8aea9eSmrg
1020fe8aea9eSmrg			tolerance = 60 + 15*60*n/10;
1021fe8aea9eSmrg			if (tolerance < interval)
1022fe8aea9eSmrg				tolerance = interval;
1023fe8aea9eSmrg
1024fe8aea9eSmrg			time = ce->ust - (ust + (60 + 15*60*n) * interval);
1025fe8aea9eSmrg			if (time < -(int64_t)tolerance) {
1026fe8aea9eSmrg				fprintf(stderr,
1027fe8aea9eSmrg					"\tnotifies completed too early by %lldms, tolerance %lldus\n",
1028fe8aea9eSmrg					(long long)(-time / 1000), (long long)tolerance);
1029fe8aea9eSmrg			} else if (time > (int64_t)tolerance) {
1030fe8aea9eSmrg				fprintf(stderr,
1031fe8aea9eSmrg					"\tnotifies completed too late by %lldms, tolerance %lldus\n",
1032fe8aea9eSmrg					(long long)(time / 1000), (long long)tolerance);
1033fe8aea9eSmrg			}
1034fe8aea9eSmrg			complete = 1;
1035fe8aea9eSmrg		} else {
1036fe8aea9eSmrg			int diff = (int64_t)(ce->msc - (15*60*ce->serial + msc + 60));
1037fe8aea9eSmrg
1038fe8aea9eSmrg			if (ce->serial != count + 1) {
1039fe8aea9eSmrg				fprintf(stderr, "vblank received out of order! expected %d, received %d\n",
1040fe8aea9eSmrg					count + 1, (int)ce->serial);
1041fe8aea9eSmrg				ret++;
1042fe8aea9eSmrg			}
1043fe8aea9eSmrg			count++;
1044fe8aea9eSmrg
1045fe8aea9eSmrg			if (diff < 0) {
1046fe8aea9eSmrg				if (-diff > earliest) {
1047fe8aea9eSmrg					fprintf(stderr, "\tnotify %d early by %d msc\n", ce->serial, -diff);
1048fe8aea9eSmrg					earliest = -diff;
1049fe8aea9eSmrg				}
1050fe8aea9eSmrg				early++;
1051fe8aea9eSmrg				ret++;
1052fe8aea9eSmrg			} else if (diff > 0) {
1053fe8aea9eSmrg				if (diff > latest) {
1054fe8aea9eSmrg					fprintf(stderr, "\tnotify %d late by %d msc\n", ce->serial, diff);
1055fe8aea9eSmrg					latest = diff;
1056fe8aea9eSmrg				}
1057fe8aea9eSmrg				late++;
1058fe8aea9eSmrg				ret++;
1059fe8aea9eSmrg			}
1060fe8aea9eSmrg		}
1061fe8aea9eSmrg		free(ev);
1062fe8aea9eSmrg	} while (!complete);
1063fe8aea9eSmrg
1064fe8aea9eSmrg	if (early)
1065fe8aea9eSmrg		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1066fe8aea9eSmrg	if (late)
1067fe8aea9eSmrg		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1068fe8aea9eSmrg
1069fe8aea9eSmrg	if (count != 10) {
1070fe8aea9eSmrg		fprintf(stderr, "Sentinel vblank received too early! %d waits outstanding\n", 10 - count);
1071fe8aea9eSmrg		ret++;
1072fe8aea9eSmrg		do {
1073fe8aea9eSmrg			xcb_present_complete_notify_event_t *ce;
1074fe8aea9eSmrg			xcb_generic_event_t *ev;
1075fe8aea9eSmrg
1076fe8aea9eSmrg			ev = xcb_wait_for_special_event(c, Q);
1077fe8aea9eSmrg			if (ev == NULL)
1078fe8aea9eSmrg				break;
1079fe8aea9eSmrg
1080fe8aea9eSmrg			ce = (xcb_present_complete_notify_event_t *)ev;
1081fe8aea9eSmrg			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1082fe8aea9eSmrg			free(ev);
1083fe8aea9eSmrg		} while (++count != 10);
1084fe8aea9eSmrg	}
1085fe8aea9eSmrg
1086fe8aea9eSmrg	XSync(dpy, True);
1087fe8aea9eSmrg	ret += !!_x_error_occurred;
1088fe8aea9eSmrg
1089fe8aea9eSmrg	return ret;
1090fe8aea9eSmrg}
1091fe8aea9eSmrg
1092fe8aea9eSmrgstatic int test_wrap_msc(Display *dpy)
1093fe8aea9eSmrg{
1094fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
1095fe8aea9eSmrg	Window root, win;
1096fe8aea9eSmrg	int x, y;
1097fe8aea9eSmrg	unsigned int width, height;
1098fe8aea9eSmrg	unsigned border, depth;
1099fe8aea9eSmrg	XSetWindowAttributes attr;
1100fe8aea9eSmrg	int ret = 0, n;
1101fe8aea9eSmrg	uint64_t msc, ust;
1102fe8aea9eSmrg	int complete;
1103fe8aea9eSmrg	uint64_t interval;
1104fe8aea9eSmrg	void *Q;
1105fe8aea9eSmrg
1106fe8aea9eSmrg	XGetGeometry(dpy, DefaultRootWindow(dpy),
1107fe8aea9eSmrg		     &root, &x, &y, &width, &height, &border, &depth);
1108fe8aea9eSmrg
1109fe8aea9eSmrg	attr.override_redirect = 1;
1110fe8aea9eSmrg	win = XCreateWindow(dpy, root,
1111fe8aea9eSmrg			    0, 0, width, height, 0, depth,
1112fe8aea9eSmrg			    InputOutput, DefaultVisual(dpy, DefaultScreen(dpy)),
1113fe8aea9eSmrg			    CWOverrideRedirect, &attr);
1114fe8aea9eSmrg	XMapWindow(dpy, win);
1115fe8aea9eSmrg	XSync(dpy, True);
1116fe8aea9eSmrg	if (_x_error_occurred)
1117fe8aea9eSmrg		return 1;
111842542f5fSchristos
1119fe8aea9eSmrg	printf("Testing wraparound notifies\n");
1120fe8aea9eSmrg	_x_error_occurred = 0;
1121fe8aea9eSmrg
1122fe8aea9eSmrg	Q = setup_msc(dpy, win);
1123fe8aea9eSmrg	interval = msc_interval(dpy, win, Q);
1124fe8aea9eSmrg	if (interval == 0) {
1125fe8aea9eSmrg		printf("Zero delay between frames\n");
1126fe8aea9eSmrg		return 1;
112742542f5fSchristos	}
1128fe8aea9eSmrg	msc = check_msc(dpy, win, Q, 0, &ust);
1129fe8aea9eSmrg	printf("Initial msc=%llx, interval between frames %lldus\n",
1130fe8aea9eSmrg	       (long long)msc, (long long)interval);
1131fe8aea9eSmrg
1132fe8aea9eSmrg	for (n = 1; n <= 10; n++)
1133fe8aea9eSmrg		xcb_present_notify_msc(c, win, n,
1134fe8aea9eSmrg				       msc + ((long long)n<<32) + n,
1135fe8aea9eSmrg				       0, 0);
1136fe8aea9eSmrg	for (n = 1; n <= 10; n++)
1137fe8aea9eSmrg		xcb_present_notify_msc(c, win, -n,
1138fe8aea9eSmrg				       0, (long long)n << 32, 0);
1139fe8aea9eSmrg	xcb_present_notify_msc(c, win, 0xdeadbeef, msc + 60*10, 0, 0);
1140fe8aea9eSmrg	xcb_flush(c);
114142542f5fSchristos
1142fe8aea9eSmrg	complete = 0;
1143fe8aea9eSmrg	do {
1144fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
1145fe8aea9eSmrg		xcb_generic_event_t *ev;
1146fe8aea9eSmrg
1147fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
1148fe8aea9eSmrg		if (ev == NULL)
1149fe8aea9eSmrg			break;
1150fe8aea9eSmrg
1151fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
1152fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1153fe8aea9eSmrg
1154fe8aea9eSmrg		if (ce->serial == 0xdeadbeef) {
1155fe8aea9eSmrg			complete = 1;
1156fe8aea9eSmrg		} else {
1157fe8aea9eSmrg			fprintf(stderr,
1158fe8aea9eSmrg				"\tnotify %d recieved at +%llu\n",
1159fe8aea9eSmrg				ce->serial, ce->msc - msc);
1160fe8aea9eSmrg			ret++;
1161fe8aea9eSmrg		}
1162fe8aea9eSmrg		free(ev);
1163fe8aea9eSmrg	} while (!complete);
1164fe8aea9eSmrg
1165fe8aea9eSmrg	teardown_msc(dpy, Q);
1166fe8aea9eSmrg	XDestroyWindow(dpy, win);
1167fe8aea9eSmrg	XSync(dpy, True);
1168fe8aea9eSmrg
1169fe8aea9eSmrg	return ret;
117042542f5fSchristos}
117142542f5fSchristos
1172fe8aea9eSmrgstatic int test_exhaustion_msc(Display *dpy, void *Q)
117342542f5fSchristos{
1174fe8aea9eSmrg#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */
1175fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
1176fe8aea9eSmrg	Window root = DefaultRootWindow(dpy);
1177fe8aea9eSmrg	int ret = 0, n, complete;
1178fe8aea9eSmrg	int earliest = 0, early = 0;
1179fe8aea9eSmrg	int latest = 0, late = 0;
1180fe8aea9eSmrg	uint64_t msc;
1181fe8aea9eSmrg
1182fe8aea9eSmrg	printf("Testing notifies with long queues\n");
1183fe8aea9eSmrg	_x_error_occurred = 0;
1184fe8aea9eSmrg
1185fe8aea9eSmrg	msc = check_msc(dpy, root, Q, 0, NULL);
1186fe8aea9eSmrg	for (n = N_VBLANKS; n--; )
1187fe8aea9eSmrg		xcb_present_notify_msc(c, root, N_VBLANKS, msc + N_VBLANKS, 0, 0);
1188fe8aea9eSmrg	for (n = 1; n <= N_VBLANKS ; n++)
1189fe8aea9eSmrg		xcb_present_notify_msc(c, root, n, msc + n, 0, 0);
1190fe8aea9eSmrg	xcb_flush(c);
1191fe8aea9eSmrg
1192fe8aea9eSmrg	complete = 2*N_VBLANKS;
1193fe8aea9eSmrg	do {
1194fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
1195fe8aea9eSmrg		xcb_generic_event_t *ev;
1196fe8aea9eSmrg		int diff;
1197fe8aea9eSmrg
1198fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
1199fe8aea9eSmrg		if (ev == NULL)
1200fe8aea9eSmrg			break;
1201fe8aea9eSmrg
1202fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
1203fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1204fe8aea9eSmrg
1205fe8aea9eSmrg		diff = (int64_t)(ce->msc - msc - ce->serial);
1206fe8aea9eSmrg		if (diff < 0) {
1207fe8aea9eSmrg			if (-diff > earliest) {
1208fe8aea9eSmrg				fprintf(stderr, "\tnotify %d early by %d msc\n",(int)ce->serial, -diff);
1209fe8aea9eSmrg				earliest = -diff;
1210fe8aea9eSmrg			}
1211fe8aea9eSmrg			early++;
1212fe8aea9eSmrg			ret++;
1213fe8aea9eSmrg		} else if (diff > 0) {
1214fe8aea9eSmrg			if (diff > latest) {
1215fe8aea9eSmrg				fprintf(stderr, "\tnotify %d late by %d msc\n", (int)ce->serial, diff);
1216fe8aea9eSmrg				latest = diff;
1217fe8aea9eSmrg			}
1218fe8aea9eSmrg			late++;
1219fe8aea9eSmrg			ret++;
1220fe8aea9eSmrg		}
1221fe8aea9eSmrg		free(ev);
1222fe8aea9eSmrg	} while (--complete);
1223fe8aea9eSmrg
1224fe8aea9eSmrg	if (early)
1225fe8aea9eSmrg		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1226fe8aea9eSmrg	if (late)
1227fe8aea9eSmrg		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1228fe8aea9eSmrg
1229fe8aea9eSmrg	XSync(dpy, True);
1230fe8aea9eSmrg	ret += !!_x_error_occurred;
1231fe8aea9eSmrg
1232fe8aea9eSmrg	return ret;
1233fe8aea9eSmrg#undef N_VBLANKS
123442542f5fSchristos}
1235fe8aea9eSmrg
1236fe8aea9eSmrgstatic int test_accuracy_msc(Display *dpy, void *Q)
123742542f5fSchristos{
1238fe8aea9eSmrg#define N_VBLANKS (60 * 120) /* ~2 minutes */
1239fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
1240fe8aea9eSmrg	Window root = DefaultRootWindow(dpy);
1241fe8aea9eSmrg	int ret = 0, n;
1242fe8aea9eSmrg	uint64_t msc;
1243fe8aea9eSmrg	int early = 0, late = 0;
1244fe8aea9eSmrg	int earliest = 0, latest = 0;
1245fe8aea9eSmrg	int complete, count;
124642542f5fSchristos
1247fe8aea9eSmrg	printf("Testing notify accuracy\n");
1248fe8aea9eSmrg	_x_error_occurred = 0;
124942542f5fSchristos
1250fe8aea9eSmrg	msc = check_msc(dpy, root, Q, 0, NULL);
1251fe8aea9eSmrg	for (n = 0; n <= N_VBLANKS; n++)
1252fe8aea9eSmrg		xcb_present_notify_msc(c, root, n, msc + 60 + n, 0, 0);
1253fe8aea9eSmrg	xcb_present_notify_msc(c, root, 0xdeadbeef, msc + 60 + n, 0, 0);
1254fe8aea9eSmrg	xcb_flush(c);
1255fe8aea9eSmrg
1256fe8aea9eSmrg	complete = 0;
1257fe8aea9eSmrg	count = 0;
1258fe8aea9eSmrg	do {
1259fe8aea9eSmrg		xcb_present_complete_notify_event_t *ce;
1260fe8aea9eSmrg		xcb_generic_event_t *ev;
1261fe8aea9eSmrg
1262fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
1263fe8aea9eSmrg		if (ev == NULL)
1264fe8aea9eSmrg			break;
1265fe8aea9eSmrg
1266fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
1267fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1268fe8aea9eSmrg
1269fe8aea9eSmrg		if (ce->serial != 0xdeadbeef) {
1270fe8aea9eSmrg			int diff = (int64_t)(ce->msc - (msc + ce->serial + 60));
1271fe8aea9eSmrg			if (diff < 0) {
1272fe8aea9eSmrg				if (-diff > earliest) {
1273fe8aea9eSmrg					fprintf(stderr, "\tnotify %d early by %d msc\n", ce->serial, -diff);
1274fe8aea9eSmrg					earliest = -diff;
1275fe8aea9eSmrg				}
1276fe8aea9eSmrg				early++;
1277fe8aea9eSmrg				ret++;
1278fe8aea9eSmrg			} else if (diff > 0) {
1279fe8aea9eSmrg				if (diff > latest) {
1280fe8aea9eSmrg					fprintf(stderr, "\tnotify %d late by %d msc\n", ce->serial, diff);
1281fe8aea9eSmrg					latest = diff;
1282fe8aea9eSmrg				}
1283fe8aea9eSmrg				late++;
1284fe8aea9eSmrg				ret++;
1285fe8aea9eSmrg			}
1286fe8aea9eSmrg			count++;
1287fe8aea9eSmrg		} else
1288fe8aea9eSmrg			complete = 1;
1289fe8aea9eSmrg		free(ev);
1290fe8aea9eSmrg	} while (!complete);
1291fe8aea9eSmrg
1292fe8aea9eSmrg	if (early)
1293fe8aea9eSmrg		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1294fe8aea9eSmrg	if (late)
1295fe8aea9eSmrg		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1296fe8aea9eSmrg
1297fe8aea9eSmrg	if (count != N_VBLANKS+1) {
1298fe8aea9eSmrg		fprintf(stderr, "Sentinel vblank received too early! %d waits outstanding\n", N_VBLANKS+1 - count);
1299fe8aea9eSmrg		ret++;
1300fe8aea9eSmrg		do {
1301fe8aea9eSmrg			xcb_present_complete_notify_event_t *ce;
1302fe8aea9eSmrg			xcb_generic_event_t *ev;
1303fe8aea9eSmrg
1304fe8aea9eSmrg			ev = xcb_wait_for_special_event(c, Q);
1305fe8aea9eSmrg			if (ev == NULL)
1306fe8aea9eSmrg				break;
1307fe8aea9eSmrg
1308fe8aea9eSmrg			ce = (xcb_present_complete_notify_event_t *)ev;
1309fe8aea9eSmrg			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1310fe8aea9eSmrg			free(ev);
1311fe8aea9eSmrg		} while (++count != N_VBLANKS+1);
1312fe8aea9eSmrg	}
1313fe8aea9eSmrg
1314fe8aea9eSmrg	XSync(dpy, True);
1315fe8aea9eSmrg	ret += !!_x_error_occurred;
1316fe8aea9eSmrg
1317fe8aea9eSmrg	return ret;
1318fe8aea9eSmrg#undef N_VBLANKS
1319fe8aea9eSmrg}
132042542f5fSchristos
1321fe8aea9eSmrgstatic int test_modulus_msc(Display *dpy, void *Q)
1322fe8aea9eSmrg{
1323fe8aea9eSmrg	xcb_connection_t *c = XGetXCBConnection(dpy);
1324fe8aea9eSmrg	Window root = DefaultRootWindow(dpy);
1325fe8aea9eSmrg	xcb_present_complete_notify_event_t *ce;
1326fe8aea9eSmrg	xcb_generic_event_t *ev;
1327fe8aea9eSmrg	int x, y, ret = 0;
1328fe8aea9eSmrg	uint64_t target;
1329fe8aea9eSmrg	int early = 0, late = 0;
1330fe8aea9eSmrg	int earliest = 0, latest = 0;
1331fe8aea9eSmrg	int complete, count, expect;
1332fe8aea9eSmrg
1333fe8aea9eSmrg	printf("Testing notify modulus\n");
133442542f5fSchristos	_x_error_occurred = 0;
133542542f5fSchristos
1336fe8aea9eSmrg	target = wait_vblank(dpy, root, Q);
133742542f5fSchristos
1338fe8aea9eSmrg	expect = 0;
1339fe8aea9eSmrg	xcb_present_notify_msc(c, root, 0, 0, 0, 0);
1340fe8aea9eSmrg	for (x = 1; x <= 19; x++) {
1341fe8aea9eSmrg		for (y = 0; y < x; y++) {
1342fe8aea9eSmrg			xcb_present_notify_msc(c, root, y << 16 | x, 0, x, y);
1343fe8aea9eSmrg			expect++;
1344fe8aea9eSmrg		}
1345fe8aea9eSmrg	}
1346fe8aea9eSmrg	xcb_present_notify_msc(c, root, 0xdeadbeef, target + 2*x, 0, 0);
1347fe8aea9eSmrg	xcb_flush(c);
134842542f5fSchristos
1349fe8aea9eSmrg	ev = xcb_wait_for_special_event(c, Q);
1350fe8aea9eSmrg	if (ev) {
1351fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
1352fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1353fe8aea9eSmrg		assert(ce->serial == 0);
1354fe8aea9eSmrg		assert(target == ce->msc);
1355fe8aea9eSmrg		target = ce->msc;
1356fe8aea9eSmrg	}
135742542f5fSchristos
1358fe8aea9eSmrg	complete = 0;
1359fe8aea9eSmrg	count = 0;
1360fe8aea9eSmrg	do {
1361fe8aea9eSmrg		ev = xcb_wait_for_special_event(c, Q);
1362fe8aea9eSmrg		if (ev == NULL)
1363fe8aea9eSmrg			break;
1364fe8aea9eSmrg
1365fe8aea9eSmrg		ce = (xcb_present_complete_notify_event_t *)ev;
1366fe8aea9eSmrg		assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1367fe8aea9eSmrg
1368fe8aea9eSmrg		assert(ce->serial);
1369fe8aea9eSmrg		if (ce->serial != 0xdeadbeef) {
1370fe8aea9eSmrg			uint64_t msc;
1371fe8aea9eSmrg			int diff;
1372fe8aea9eSmrg
1373fe8aea9eSmrg			x = ce->serial & 0xffff;
1374fe8aea9eSmrg			y = ce->serial >> 16;
1375fe8aea9eSmrg
1376fe8aea9eSmrg			msc = target;
1377fe8aea9eSmrg			msc -= target % x;
1378fe8aea9eSmrg			msc += y;
1379fe8aea9eSmrg			if (msc <= target)
1380fe8aea9eSmrg				msc += x;
1381fe8aea9eSmrg
1382fe8aea9eSmrg			diff = (int64_t)(ce->msc - msc);
1383fe8aea9eSmrg			if (diff < 0) {
1384fe8aea9eSmrg				if (-diff > earliest) {
1385fe8aea9eSmrg					fprintf(stderr, "\tnotify (%d, %d) early by %d msc (target %lld, reported %lld)\n", y, x, -diff, (long long)msc, (long long)ce->msc);
1386fe8aea9eSmrg					earliest = -diff;
1387fe8aea9eSmrg				}
1388fe8aea9eSmrg				early++;
1389fe8aea9eSmrg				ret++;
1390fe8aea9eSmrg			} else if (diff > 0) {
1391fe8aea9eSmrg				if (diff > latest) {
1392fe8aea9eSmrg					fprintf(stderr, "\tnotify (%d, %d) late by %d msc (target %lld, reported %lld)\n", y, x, diff, (long long)msc, (long long)ce->msc);
1393fe8aea9eSmrg					latest = diff;
1394fe8aea9eSmrg				}
1395fe8aea9eSmrg				late++;
1396fe8aea9eSmrg				ret++;
1397fe8aea9eSmrg			}
1398fe8aea9eSmrg			count++;
1399fe8aea9eSmrg		} else
1400fe8aea9eSmrg			complete = 1;
1401fe8aea9eSmrg		free(ev);
1402fe8aea9eSmrg	} while (!complete);
1403fe8aea9eSmrg
1404fe8aea9eSmrg	if (early)
1405fe8aea9eSmrg		printf("\t%d notifies too early (worst %d)!\n", early, earliest);
1406fe8aea9eSmrg	if (late)
1407fe8aea9eSmrg		printf("\t%d notifies too late (worst %d)!\n", late, latest);
1408fe8aea9eSmrg
1409fe8aea9eSmrg	if (count != expect) {
1410fe8aea9eSmrg		fprintf(stderr, "Sentinel vblank received too early! %d waits outstanding\n", expect - count);
1411fe8aea9eSmrg		ret++;
1412fe8aea9eSmrg		do {
1413fe8aea9eSmrg			ev = xcb_wait_for_special_event(c, Q);
1414fe8aea9eSmrg			if (ev == NULL)
1415fe8aea9eSmrg				break;
1416fe8aea9eSmrg
1417fe8aea9eSmrg			ce = (xcb_present_complete_notify_event_t *)ev;
1418fe8aea9eSmrg			assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC);
1419fe8aea9eSmrg			free(ev);
1420fe8aea9eSmrg		} while (++count != expect);
1421fe8aea9eSmrg	}
142242542f5fSchristos
142342542f5fSchristos	XSync(dpy, True);
142442542f5fSchristos	ret += !!_x_error_occurred;
142542542f5fSchristos
142642542f5fSchristos	return ret;
142742542f5fSchristos}
142842542f5fSchristos
142942542f5fSchristosstatic inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
143042542f5fSchristos{
143142542f5fSchristos	XRRScreenResources *res;
143242542f5fSchristos
143342542f5fSchristos	res = XRRGetScreenResourcesCurrent(dpy, window);
143442542f5fSchristos	if (res == NULL)
143542542f5fSchristos		res = XRRGetScreenResources(dpy, window);
143642542f5fSchristos
143742542f5fSchristos	return res;
143842542f5fSchristos}
143942542f5fSchristos
144042542f5fSchristosstatic XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
144142542f5fSchristos{
144242542f5fSchristos	int i;
144342542f5fSchristos
144442542f5fSchristos	for (i = 0; i < res->nmode; i++) {
144542542f5fSchristos		if (res->modes[i].id == id)
144642542f5fSchristos			return &res->modes[i];
144742542f5fSchristos	}
144842542f5fSchristos
144942542f5fSchristos	return NULL;
145042542f5fSchristos}
145142542f5fSchristos
145242542f5fSchristosstatic int for_each_crtc(Display *dpy,
145342542f5fSchristos			  int (*func)(Display *dpy,
145442542f5fSchristos				      RRCrtc crtc,
145542542f5fSchristos				      int width, int height,
145642542f5fSchristos				      void *closure),
145742542f5fSchristos			  void *closure)
145842542f5fSchristos{
145942542f5fSchristos	XRRScreenResources *res;
146042542f5fSchristos	XRRCrtcInfo **original_crtc;
146142542f5fSchristos	int i, j, err = 0;
146242542f5fSchristos
146342542f5fSchristos	if (!XRRQueryVersion(dpy, &i, &j))
146442542f5fSchristos		return -1;
146542542f5fSchristos
146642542f5fSchristos	res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy));
146742542f5fSchristos	if (res == NULL)
146842542f5fSchristos		return -1;
146942542f5fSchristos
147042542f5fSchristos	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
147142542f5fSchristos	for (i = 0; i < res->ncrtc; i++)
147242542f5fSchristos		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
147342542f5fSchristos
147442542f5fSchristos	for (i = 0; i < res->noutput; i++) {
147542542f5fSchristos		XRROutputInfo *output;
147642542f5fSchristos		XRRModeInfo *mode;
147742542f5fSchristos
147842542f5fSchristos		output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
147942542f5fSchristos		if (output == NULL)
148042542f5fSchristos			continue;
148142542f5fSchristos
148242542f5fSchristos		mode = NULL;
148342542f5fSchristos		if (res->nmode)
148442542f5fSchristos			mode = lookup_mode(res, output->modes[0]);
148542542f5fSchristos
148642542f5fSchristos		for (j = 0; mode && j < output->ncrtc; j++) {
148742542f5fSchristos			printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n",
148842542f5fSchristos			       i, j, (long)res->outputs[i], (long)output->crtcs[j]);
148942542f5fSchristos			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
149042542f5fSchristos					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
149142542f5fSchristos			XSync(dpy, True);
149242542f5fSchristos
149342542f5fSchristos			err += func(dpy, output->crtcs[j], mode->width, mode->height, closure);
149442542f5fSchristos
149542542f5fSchristos			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
149642542f5fSchristos					 0, 0, None, RR_Rotate_0, NULL, 0);
149742542f5fSchristos			XSync(dpy, True);
149842542f5fSchristos		}
149942542f5fSchristos
150042542f5fSchristos		XRRFreeOutputInfo(output);
150142542f5fSchristos	}
150242542f5fSchristos
150342542f5fSchristos	for (i = 0; i < res->ncrtc; i++)
150442542f5fSchristos		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
150542542f5fSchristos				 original_crtc[i]->x,
150642542f5fSchristos				 original_crtc[i]->y,
150742542f5fSchristos				 original_crtc[i]->mode,
150842542f5fSchristos				 original_crtc[i]->rotation,
150942542f5fSchristos				 original_crtc[i]->outputs,
151042542f5fSchristos				 original_crtc[i]->noutput);
151142542f5fSchristos
151242542f5fSchristos	free(original_crtc);
151342542f5fSchristos	XRRFreeScreenResources(res);
151442542f5fSchristos
1515fe8aea9eSmrg	return err;
151642542f5fSchristos}
151742542f5fSchristos
151842542f5fSchristosstruct test_crtc {
151942542f5fSchristos	Window win;
152042542f5fSchristos	int depth;
152142542f5fSchristos	unsigned flags;
152242542f5fSchristos
152342542f5fSchristos	struct dri3_fence fence;
152442542f5fSchristos	void *queue;
152542542f5fSchristos	uint64_t msc;
152642542f5fSchristos};
152742542f5fSchristos#define SYNC 0x1
1528fe8aea9eSmrg#define FUTURE 0x2
152942542f5fSchristos
153042542f5fSchristosstatic int __test_crtc(Display *dpy, RRCrtc crtc,
153142542f5fSchristos		       int width, int height,
153242542f5fSchristos		       void *closure)
153342542f5fSchristos{
153442542f5fSchristos	struct test_crtc *test = closure;
153542542f5fSchristos	Pixmap pixmap;
153642542f5fSchristos	int err = 0;
153742542f5fSchristos
1538fe8aea9eSmrg	test->msc = check_msc(dpy, test->win, test->queue, test->msc, NULL);
153942542f5fSchristos
154042542f5fSchristos	if (test->flags & SYNC)
154142542f5fSchristos		xshmfence_reset(test->fence.addr);
154242542f5fSchristos
154342542f5fSchristos	pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth);
154442542f5fSchristos	xcb_present_pixmap(XGetXCBConnection(dpy),
154542542f5fSchristos			   test->win, pixmap,
154642542f5fSchristos			   0, /* sbc */
154742542f5fSchristos			   0, /* valid */
154842542f5fSchristos			   0, /* update */
154942542f5fSchristos			   0, /* x_off */
155042542f5fSchristos			   0, /* y_off */
155142542f5fSchristos			   crtc,
155242542f5fSchristos			   None, /* wait fence */
155342542f5fSchristos			   test->flags & SYNC ? test->fence.xid : None,
155442542f5fSchristos			   XCB_PRESENT_OPTION_NONE,
1555fe8aea9eSmrg			   test->msc, /* target msc */
155642542f5fSchristos			   1, /* divisor */
155742542f5fSchristos			   0, /* remainder */
155842542f5fSchristos			   0, NULL);
155942542f5fSchristos	if (test->flags & SYNC) {
1560fe8aea9eSmrg		Pixmap tmp = XCreatePixmap(dpy, test->win, width, height, test->depth);
156142542f5fSchristos		xcb_present_pixmap(XGetXCBConnection(dpy),
1562fe8aea9eSmrg				   test->win, tmp,
156342542f5fSchristos				   1, /* sbc */
156442542f5fSchristos				   0, /* valid */
156542542f5fSchristos				   0, /* update */
156642542f5fSchristos				   0, /* x_off */
156742542f5fSchristos				   0, /* y_off */
156842542f5fSchristos				   crtc,
156942542f5fSchristos				   None, /* wait fence */
157042542f5fSchristos				   None, /* sync fence */
157142542f5fSchristos				   XCB_PRESENT_OPTION_NONE,
1572fe8aea9eSmrg				   test->msc + (test->flags & FUTURE ? 5 * 16 : 1), /* target msc */
157342542f5fSchristos				   1, /* divisor */
157442542f5fSchristos				   0, /* remainder */
157542542f5fSchristos				   0, NULL);
1576fe8aea9eSmrg		XFreePixmap(dpy, tmp);
157742542f5fSchristos		XFlush(dpy);
157842542f5fSchristos		err += !!xshmfence_await(test->fence.addr);
157942542f5fSchristos	}
1580fe8aea9eSmrg	XFreePixmap(dpy, pixmap);
158142542f5fSchristos
1582fe8aea9eSmrg	test->msc = check_msc(dpy, test->win, test->queue, test->msc, NULL);
158342542f5fSchristos	return err;
158442542f5fSchristos}
158542542f5fSchristos
158642542f5fSchristosstatic int test_crtc(Display *dpy, void *queue, uint64_t last_msc)
158742542f5fSchristos{
158842542f5fSchristos	struct test_crtc test;
158942542f5fSchristos	int err = 0;
159042542f5fSchristos
159142542f5fSchristos	XSync(dpy, True);
159242542f5fSchristos	_x_error_occurred = 0;
159342542f5fSchristos
159442542f5fSchristos	test.win = DefaultRootWindow(dpy);
159542542f5fSchristos	test.depth = DefaultDepth(dpy, DefaultScreen(dpy));
159642542f5fSchristos	if (dri3_create_fence(dpy, test.win, &test.fence))
159742542f5fSchristos		return -1;
159842542f5fSchristos	test.queue = queue;
159942542f5fSchristos	test.msc = last_msc;
160042542f5fSchristos
160142542f5fSchristos	printf("Testing each crtc, without waiting for each flip\n");
160242542f5fSchristos	test.flags = 0;
1603fe8aea9eSmrg	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
160442542f5fSchristos	err += for_each_crtc(dpy, __test_crtc, &test);
1605fe8aea9eSmrg	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
160642542f5fSchristos
160742542f5fSchristos	printf("Testing each crtc, waiting for flips to complete\n");
160842542f5fSchristos	test.flags = SYNC;
1609fe8aea9eSmrg	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
161042542f5fSchristos	err += for_each_crtc(dpy, __test_crtc, &test);
1611fe8aea9eSmrg	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
161242542f5fSchristos
1613fe8aea9eSmrg	printf("Testing each crtc, with future flips\n");
1614fe8aea9eSmrg	test.flags = FUTURE | SYNC;
1615fe8aea9eSmrg	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
1616fe8aea9eSmrg	err += for_each_crtc(dpy, __test_crtc, &test);
1617fe8aea9eSmrg	test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL);
161842542f5fSchristos
1619fe8aea9eSmrg	dri3_fence_free(dpy, &test.fence);
162042542f5fSchristos	XSync(dpy, True);
162142542f5fSchristos	err += !!_x_error_occurred;
162242542f5fSchristos
162342542f5fSchristos	if (err)
162442542f5fSchristos		printf("%s: failures=%d\n", __func__, err);
162542542f5fSchristos
162642542f5fSchristos	return err;
162742542f5fSchristos}
162842542f5fSchristos
162942542f5fSchristosstatic int
163042542f5fSchristoscan_use_shm(Display *dpy)
163142542f5fSchristos{
163242542f5fSchristos	int major, minor, has_pixmap;
163342542f5fSchristos
163442542f5fSchristos	if (!XShmQueryExtension(dpy))
163542542f5fSchristos		return 0;
163642542f5fSchristos
163742542f5fSchristos	XShmQueryVersion(dpy, &major, &minor, &has_pixmap);
163842542f5fSchristos	return has_pixmap;
163942542f5fSchristos}
164042542f5fSchristos
164142542f5fSchristosstatic int test_shm(Display *dpy)
164242542f5fSchristos{
164342542f5fSchristos	Window win = DefaultRootWindow(dpy);
164442542f5fSchristos	XShmSegmentInfo shm;
164542542f5fSchristos	Pixmap pixmap;
164642542f5fSchristos	Window root;
164742542f5fSchristos	unsigned int width, height;
164842542f5fSchristos	unsigned border, depth;
164942542f5fSchristos	int x, y, ret = 1;
165042542f5fSchristos
165142542f5fSchristos	if (!can_use_shm(dpy))
165242542f5fSchristos		return 0;
165342542f5fSchristos
165442542f5fSchristos	_x_error_occurred = 0;
165542542f5fSchristos
165642542f5fSchristos	XGetGeometry(dpy, win, &root, &x, &y,
165742542f5fSchristos		     &width, &height, &border, &depth);
165842542f5fSchristos
165942542f5fSchristos	printf("Using %dx%d SHM\n", width, height);
166042542f5fSchristos
166142542f5fSchristos	shm.shmid = shmget(IPC_PRIVATE, height * 4*width, IPC_CREAT | 0666);
166242542f5fSchristos	if (shm.shmid == -1)
166342542f5fSchristos		return 0;
166442542f5fSchristos
166542542f5fSchristos	shm.shmaddr = shmat(shm.shmid, 0, 0);
166642542f5fSchristos	if (shm.shmaddr == (char *) -1)
166742542f5fSchristos		goto rmid;
166842542f5fSchristos
166942542f5fSchristos	shm.readOnly = False;
167042542f5fSchristos	XShmAttach(dpy, &shm);
167142542f5fSchristos
167242542f5fSchristos	pixmap = XShmCreatePixmap(dpy, DefaultRootWindow(dpy),
167342542f5fSchristos				  shm.shmaddr, &shm, width, height, 24);
167442542f5fSchristos	if (_x_error_occurred)
167542542f5fSchristos		goto detach;
167642542f5fSchristos
167742542f5fSchristos	xcb_present_pixmap(XGetXCBConnection(dpy),
167842542f5fSchristos			   win, pixmap,
167942542f5fSchristos			   0, /* sbc */
168042542f5fSchristos			   0, /* valid */
168142542f5fSchristos			   0, /* update */
168242542f5fSchristos			   0, /* x_off */
168342542f5fSchristos			   0, /* y_off */
168442542f5fSchristos			   None,
168542542f5fSchristos			   None, /* wait fence */
168642542f5fSchristos			   None,
168742542f5fSchristos			   XCB_PRESENT_OPTION_NONE,
168842542f5fSchristos			   0, /* target msc */
168942542f5fSchristos			   0, /* divisor */
169042542f5fSchristos			   0, /* remainder */
169142542f5fSchristos			   0, NULL);
169242542f5fSchristos	XFreePixmap(dpy, pixmap);
169342542f5fSchristos
169442542f5fSchristos	XSync(dpy, True);
169542542f5fSchristos	if (_x_error_occurred)
169642542f5fSchristos		goto detach;
169742542f5fSchristos
169842542f5fSchristos	ret = 0;
169942542f5fSchristosdetach:
170042542f5fSchristos	XShmDetach(dpy, &shm);
170142542f5fSchristos	shmdt(shm.shmaddr);
170242542f5fSchristos	XSync(dpy, False);
170342542f5fSchristosrmid:
170442542f5fSchristos	shmctl(shm.shmid, IPC_RMID, NULL);
170542542f5fSchristos	return ret;
170642542f5fSchristos}
170742542f5fSchristos
170842542f5fSchristosstatic uint32_t gem_create(int fd, int size)
170942542f5fSchristos{
171042542f5fSchristos	struct drm_i915_gem_create create;
171142542f5fSchristos
171242542f5fSchristos	create.handle = 0;
171342542f5fSchristos	create.size = size;
171442542f5fSchristos	(void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
171542542f5fSchristos
171642542f5fSchristos	return create.handle;
171742542f5fSchristos}
171842542f5fSchristos
171942542f5fSchristosstruct local_i915_gem_caching {
172042542f5fSchristos	uint32_t handle;
172142542f5fSchristos	uint32_t caching;
172242542f5fSchristos};
172342542f5fSchristos
172442542f5fSchristos#define LOCAL_I915_GEM_SET_CACHING	0x2f
172542542f5fSchristos#define LOCAL_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_SET_CACHING, struct local_i915_gem_caching)
172642542f5fSchristos
172742542f5fSchristosstatic int gem_set_caching(int fd, uint32_t handle, int caching)
172842542f5fSchristos{
172942542f5fSchristos	struct local_i915_gem_caching arg;
173042542f5fSchristos
173142542f5fSchristos	arg.handle = handle;
173242542f5fSchristos	arg.caching = caching;
173342542f5fSchristos
173442542f5fSchristos	return drmIoctl(fd, LOCAL_IOCTL_I915_GEM_SET_CACHING, &arg) == 0;
173542542f5fSchristos}
173642542f5fSchristos
1737fe8aea9eSmrgstatic int gem_set_tiling(int fd, uint32_t handle, int tiling, int stride)
1738fe8aea9eSmrg{
1739fe8aea9eSmrg	struct drm_i915_gem_set_tiling set_tiling;
1740fe8aea9eSmrg	int err;
1741fe8aea9eSmrg
1742fe8aea9eSmrgrestart:
1743fe8aea9eSmrg	set_tiling.handle = handle;
1744fe8aea9eSmrg	set_tiling.tiling_mode = tiling;
1745fe8aea9eSmrg	set_tiling.stride = stride;
1746fe8aea9eSmrg
1747fe8aea9eSmrg	if (drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0)
1748fe8aea9eSmrg		return 1;
1749fe8aea9eSmrg
1750fe8aea9eSmrg	err = errno;
1751fe8aea9eSmrg	if (err == EINTR)
1752fe8aea9eSmrg		goto restart;
1753fe8aea9eSmrg
1754fe8aea9eSmrg	if (err == EAGAIN) {
1755fe8aea9eSmrg		sched_yield();
1756fe8aea9eSmrg		goto restart;
1757fe8aea9eSmrg	}
1758fe8aea9eSmrg
1759fe8aea9eSmrg	return 0;
1760fe8aea9eSmrg}
1761fe8aea9eSmrg
176242542f5fSchristosstatic int gem_export(int fd, uint32_t handle)
176342542f5fSchristos{
176442542f5fSchristos	struct drm_prime_handle args;
176542542f5fSchristos
176642542f5fSchristos	args.handle = handle;
176742542f5fSchristos	args.flags = O_CLOEXEC;
176842542f5fSchristos
176942542f5fSchristos	if (drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args))
177042542f5fSchristos		return -1;
177142542f5fSchristos
177242542f5fSchristos	return args.fd;
177342542f5fSchristos}
177442542f5fSchristos
177542542f5fSchristosstatic void gem_close(int fd, uint32_t handle)
177642542f5fSchristos{
177742542f5fSchristos	struct drm_gem_close close;
177842542f5fSchristos
177942542f5fSchristos	close.handle = handle;
178042542f5fSchristos	(void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
178142542f5fSchristos}
178242542f5fSchristos
1783fe8aea9eSmrgstatic int test_dri3_tiling(Display *dpy)
1784fe8aea9eSmrg{
1785fe8aea9eSmrg	Window win = DefaultRootWindow(dpy);
1786fe8aea9eSmrg	const int tiling[] = { I915_TILING_NONE, I915_TILING_X, I915_TILING_Y };
1787fe8aea9eSmrg	Window root;
1788fe8aea9eSmrg	unsigned int width, height;
1789fe8aea9eSmrg	unsigned border, depth, bpp;
1790fe8aea9eSmrg	unsigned stride, size;
1791fe8aea9eSmrg	void *Q;
1792fe8aea9eSmrg	int x, y;
1793fe8aea9eSmrg	int device;
1794fe8aea9eSmrg	int line = -1;
1795fe8aea9eSmrg	int t;
1796fe8aea9eSmrg
1797fe8aea9eSmrg	device = dri3_open(dpy);
1798fe8aea9eSmrg	if (device < 0)
1799fe8aea9eSmrg		return 0;
1800fe8aea9eSmrg
1801fe8aea9eSmrg	if (!is_intel(device))
1802fe8aea9eSmrg		return 0;
1803fe8aea9eSmrg
1804fe8aea9eSmrg	printf("Opened Intel DRI3 device\n");
1805fe8aea9eSmrg
1806fe8aea9eSmrg	XGetGeometry(dpy, win, &root, &x, &y,
1807fe8aea9eSmrg		     &width, &height, &border, &depth);
1808fe8aea9eSmrg
1809fe8aea9eSmrg	switch (depth) {
1810fe8aea9eSmrg	case 8: bpp = 8; break;
1811fe8aea9eSmrg	case 15: case 16: bpp = 16; break;
1812fe8aea9eSmrg	case 24: case 32: bpp = 32; break;
1813fe8aea9eSmrg	default: return 0;
1814fe8aea9eSmrg	}
1815fe8aea9eSmrg
1816fe8aea9eSmrg	stride = ALIGN(width * bpp/8, 512);
1817fe8aea9eSmrg	size = PAGE_ALIGN(stride * ALIGN(height, 32));
1818fe8aea9eSmrg	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n",
1819fe8aea9eSmrg	       width, height, stride, size);
1820fe8aea9eSmrg
1821fe8aea9eSmrg	_x_error_occurred = 0;
1822fe8aea9eSmrg	Q = setup_msc(dpy, root);
1823fe8aea9eSmrg
1824fe8aea9eSmrg	for (t = 0; t < sizeof(tiling)/sizeof(tiling[0]); t++) {
1825fe8aea9eSmrg		uint64_t msc;
1826fe8aea9eSmrg		uint32_t src;
1827fe8aea9eSmrg		int src_fd;
1828fe8aea9eSmrg		Pixmap src_pix;
1829fe8aea9eSmrg
1830fe8aea9eSmrg		src = gem_create(device, size);
1831fe8aea9eSmrg		if (!src) {
1832fe8aea9eSmrg			line = __LINE__;
1833fe8aea9eSmrg			goto fail;
1834fe8aea9eSmrg		}
1835fe8aea9eSmrg
1836fe8aea9eSmrg		gem_set_tiling(device, src, tiling[t], stride);
1837fe8aea9eSmrg
1838fe8aea9eSmrg		src_fd = gem_export(device, src);
1839fe8aea9eSmrg		if (src_fd < 0) {
1840fe8aea9eSmrg			line = __LINE__;
1841fe8aea9eSmrg			goto fail;
1842fe8aea9eSmrg		}
1843fe8aea9eSmrg
1844fe8aea9eSmrg		src_pix = dri3_create_pixmap(dpy, root,
1845fe8aea9eSmrg					     width, height, depth,
1846fe8aea9eSmrg					     src_fd, bpp, stride, size);
1847fe8aea9eSmrg
1848fe8aea9eSmrg		msc = wait_vblank(dpy, root, Q);
1849fe8aea9eSmrg
1850fe8aea9eSmrg		xcb_present_pixmap(XGetXCBConnection(dpy),
1851fe8aea9eSmrg				   win, src_pix,
1852fe8aea9eSmrg				   0, /* sbc */
1853fe8aea9eSmrg				   0, /* valid */
1854fe8aea9eSmrg				   0, /* update */
1855fe8aea9eSmrg				   0, /* x_off */
1856fe8aea9eSmrg				   0, /* y_off */
1857fe8aea9eSmrg				   None,
1858fe8aea9eSmrg				   None, /* wait fence */
1859fe8aea9eSmrg				   None,
1860fe8aea9eSmrg				   XCB_PRESENT_OPTION_NONE,
1861fe8aea9eSmrg				   msc + 2, /* target msc */
1862fe8aea9eSmrg				   1, /* divisor */
1863fe8aea9eSmrg				   0, /* remainder */
1864fe8aea9eSmrg				   0, NULL);
1865fe8aea9eSmrg
1866fe8aea9eSmrg		xcb_present_pixmap(XGetXCBConnection(dpy),
1867fe8aea9eSmrg				   win, src_pix,
1868fe8aea9eSmrg				   0, /* sbc */
1869fe8aea9eSmrg				   0, /* valid */
1870fe8aea9eSmrg				   0, /* update */
1871fe8aea9eSmrg				   0, /* x_off */
1872fe8aea9eSmrg				   0, /* y_off */
1873fe8aea9eSmrg				   None,
1874fe8aea9eSmrg				   None, /* wait fence */
1875fe8aea9eSmrg				   None,
1876fe8aea9eSmrg				   XCB_PRESENT_OPTION_NONE,
1877fe8aea9eSmrg				   msc + 3, /* target msc */
1878fe8aea9eSmrg				   1, /* divisor */
1879fe8aea9eSmrg				   0, /* remainder */
1880fe8aea9eSmrg				   0, NULL);
1881fe8aea9eSmrg
1882fe8aea9eSmrg		XSync(dpy, True);
1883fe8aea9eSmrg		if (_x_error_occurred) {
1884fe8aea9eSmrg			line = __LINE__;
1885fe8aea9eSmrg			goto fail;
1886fe8aea9eSmrg		}
1887fe8aea9eSmrg		XFreePixmap(dpy, src_pix);
1888fe8aea9eSmrg		_x_error_occurred = 0;
1889fe8aea9eSmrg
1890fe8aea9eSmrg		close(src_fd);
1891fe8aea9eSmrg		gem_close(device, src);
1892fe8aea9eSmrg	}
1893fe8aea9eSmrg
1894fe8aea9eSmrg	teardown_msc(dpy, Q);
1895fe8aea9eSmrg	return 0;
1896fe8aea9eSmrg
1897fe8aea9eSmrgfail:
1898fe8aea9eSmrg	printf("%s failed with tiling %d, line %d\n", __func__, tiling[t], line);
1899fe8aea9eSmrg	teardown_msc(dpy, Q);
1900fe8aea9eSmrg	return 1;
1901fe8aea9eSmrg}
1902fe8aea9eSmrg
190342542f5fSchristosstatic int test_dri3(Display *dpy)
190442542f5fSchristos{
190542542f5fSchristos	Window win = DefaultRootWindow(dpy);
190642542f5fSchristos	Pixmap pixmap;
190742542f5fSchristos	Window root;
190842542f5fSchristos	unsigned int width, height;
190942542f5fSchristos	unsigned border, depth;
191042542f5fSchristos	unsigned stride, size;
191142542f5fSchristos	int x, y, ret = 1;
191242542f5fSchristos	int device, handle;
191342542f5fSchristos	int bpp;
191442542f5fSchristos
191542542f5fSchristos	device = dri3_open(dpy);
191642542f5fSchristos	if (device < 0)
191742542f5fSchristos		return 0;
191842542f5fSchristos
191942542f5fSchristos	if (!is_intel(device))
192042542f5fSchristos		return 0;
192142542f5fSchristos
192242542f5fSchristos	printf("Opened Intel DRI3 device\n");
192342542f5fSchristos
192442542f5fSchristos	XGetGeometry(dpy, win, &root, &x, &y,
192542542f5fSchristos		     &width, &height, &border, &depth);
192642542f5fSchristos
192742542f5fSchristos	switch (depth) {
192842542f5fSchristos	case 8: bpp = 8; break;
192942542f5fSchristos	case 15: case 16: bpp = 16; break;
193042542f5fSchristos	case 24: case 32: bpp = 32; break;
193142542f5fSchristos	default: return 0;
193242542f5fSchristos	}
193342542f5fSchristos
193442542f5fSchristos	stride = width * bpp/8;
193542542f5fSchristos	size = PAGE_ALIGN(stride * height);
193642542f5fSchristos	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n",
193742542f5fSchristos	       width, height, stride, size);
193842542f5fSchristos
193942542f5fSchristos	pixmap = 0;
194042542f5fSchristos	handle = gem_create(device, size);
194142542f5fSchristos	if (handle) {
194242542f5fSchristos		pixmap = dri3_create_pixmap(dpy, root,
194342542f5fSchristos					     width, height, depth,
194442542f5fSchristos					     gem_export(device, handle), bpp, stride, size);
194542542f5fSchristos		gem_close(device, handle);
194642542f5fSchristos	}
194742542f5fSchristos	if (pixmap == 0)
194842542f5fSchristos		goto fail;
194942542f5fSchristos
195042542f5fSchristos	xcb_present_pixmap(XGetXCBConnection(dpy),
195142542f5fSchristos			   win, pixmap,
195242542f5fSchristos			   0, /* sbc */
195342542f5fSchristos			   0, /* valid */
195442542f5fSchristos			   0, /* update */
195542542f5fSchristos			   0, /* x_off */
195642542f5fSchristos			   0, /* y_off */
195742542f5fSchristos			   None,
195842542f5fSchristos			   None, /* wait fence */
195942542f5fSchristos			   None,
196042542f5fSchristos			   XCB_PRESENT_OPTION_NONE,
196142542f5fSchristos			   0, /* target msc */
196242542f5fSchristos			   0, /* divisor */
196342542f5fSchristos			   0, /* remainder */
196442542f5fSchristos			   0, NULL);
196542542f5fSchristos	XFreePixmap(dpy, pixmap);
196642542f5fSchristos
196742542f5fSchristos	XSync(dpy, True);
196842542f5fSchristos	if (_x_error_occurred)
196942542f5fSchristos		goto fail;
197042542f5fSchristos
197142542f5fSchristos	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for CPU\n",
197242542f5fSchristos	       width, height, stride, size);
197342542f5fSchristos
197442542f5fSchristos	pixmap = 0;
197542542f5fSchristos	handle = gem_create(device, size);
197642542f5fSchristos	if (handle) {
197742542f5fSchristos		gem_set_caching(device, handle, CPU);
197842542f5fSchristos		handle = dri3_create_pixmap(dpy, root,
197942542f5fSchristos					     width, height, depth,
198042542f5fSchristos					     gem_export(device, handle), bpp, stride, size);
198142542f5fSchristos		gem_close(device, handle);
198242542f5fSchristos	}
198342542f5fSchristos	if (pixmap == 0)
198442542f5fSchristos		goto fail;
198542542f5fSchristos
198642542f5fSchristos	xcb_present_pixmap(XGetXCBConnection(dpy),
198742542f5fSchristos			   win, pixmap,
198842542f5fSchristos			   0, /* sbc */
198942542f5fSchristos			   0, /* valid */
199042542f5fSchristos			   0, /* update */
199142542f5fSchristos			   0, /* x_off */
199242542f5fSchristos			   0, /* y_off */
199342542f5fSchristos			   None,
199442542f5fSchristos			   None, /* wait fence */
199542542f5fSchristos			   None,
199642542f5fSchristos			   XCB_PRESENT_OPTION_NONE,
199742542f5fSchristos			   0, /* target msc */
199842542f5fSchristos			   0, /* divisor */
199942542f5fSchristos			   0, /* remainder */
200042542f5fSchristos			   0, NULL);
200142542f5fSchristos	XFreePixmap(dpy, pixmap);
200242542f5fSchristos
200342542f5fSchristos	XSync(dpy, True);
200442542f5fSchristos	if (_x_error_occurred)
200542542f5fSchristos		goto fail;
200642542f5fSchristos
200742542f5fSchristos	ret = 0;
200842542f5fSchristosfail:
200942542f5fSchristos	close(device);
201042542f5fSchristos	return ret;
201142542f5fSchristos}
201242542f5fSchristos
201342542f5fSchristosstatic int has_present(Display *dpy)
201442542f5fSchristos{
201542542f5fSchristos	xcb_connection_t *c = XGetXCBConnection(dpy);
201642542f5fSchristos	xcb_generic_error_t *error = NULL;
2017fe8aea9eSmrg	void *reply;
2018fe8aea9eSmrg
2019fe8aea9eSmrg	reply = xcb_xfixes_query_version_reply(c,
2020fe8aea9eSmrg					       xcb_xfixes_query_version(c,
2021fe8aea9eSmrg									XCB_XFIXES_MAJOR_VERSION,
2022fe8aea9eSmrg									XCB_XFIXES_MINOR_VERSION),
2023fe8aea9eSmrg					       &error);
2024fe8aea9eSmrg	free(reply);
2025fe8aea9eSmrg	free(error);
2026fe8aea9eSmrg	if (reply == NULL) {
2027fe8aea9eSmrg		fprintf(stderr, "XFixes not supported on %s\n", DisplayString(dpy));
2028fe8aea9eSmrg		return 0;
2029fe8aea9eSmrg	}
2030fe8aea9eSmrg
2031fe8aea9eSmrg	reply = xcb_dri3_query_version_reply(c,
2032fe8aea9eSmrg					     xcb_dri3_query_version(c,
2033fe8aea9eSmrg								    XCB_DRI3_MAJOR_VERSION,
2034fe8aea9eSmrg								    XCB_DRI3_MINOR_VERSION),
2035fe8aea9eSmrg					     &error);
2036fe8aea9eSmrg	free(reply);
2037fe8aea9eSmrg	free(error);
2038fe8aea9eSmrg	if (reply == NULL) {
2039fe8aea9eSmrg		fprintf(stderr, "DRI3 not supported on %s\n", DisplayString(dpy));
2040fe8aea9eSmrg		return 0;
2041fe8aea9eSmrg	}
204242542f5fSchristos
204342542f5fSchristos	reply = xcb_present_query_version_reply(c,
204442542f5fSchristos						xcb_present_query_version(c,
204542542f5fSchristos									  XCB_PRESENT_MAJOR_VERSION,
204642542f5fSchristos									  XCB_PRESENT_MINOR_VERSION),
204742542f5fSchristos						&error);
204842542f5fSchristos
204942542f5fSchristos	free(reply);
205042542f5fSchristos	free(error);
2051fe8aea9eSmrg	if (reply == NULL) {
2052fe8aea9eSmrg		fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy));
2053fe8aea9eSmrg		return 0;
2054fe8aea9eSmrg	}
2055fe8aea9eSmrg
2056fe8aea9eSmrg	return 1;
2057fe8aea9eSmrg}
2058fe8aea9eSmrg
2059fe8aea9eSmrgstatic int has_composite(Display *dpy)
2060fe8aea9eSmrg{
2061fe8aea9eSmrg	int event, error;
2062fe8aea9eSmrg	int major, minor;
2063fe8aea9eSmrg
2064fe8aea9eSmrg	if (!XCompositeQueryExtension(dpy, &event, &error))
2065fe8aea9eSmrg		return 0;
2066fe8aea9eSmrg
2067fe8aea9eSmrg	XCompositeQueryVersion(dpy, &major, &minor);
206842542f5fSchristos
2069fe8aea9eSmrg	return major > 0 || minor >= 4;
207042542f5fSchristos}
207142542f5fSchristos
207242542f5fSchristosint main(void)
207342542f5fSchristos{
207442542f5fSchristos	Display *dpy;
207542542f5fSchristos	Window root;
2076fe8aea9eSmrg	int dummy;
207742542f5fSchristos	int error = 0;
207842542f5fSchristos	uint64_t last_msc;
207942542f5fSchristos	void *queue;
208042542f5fSchristos
208142542f5fSchristos	dpy = XOpenDisplay(NULL);
208242542f5fSchristos	if (dpy == NULL)
208342542f5fSchristos		return 77;
208442542f5fSchristos
208542542f5fSchristos	if (!has_present(dpy))
208642542f5fSchristos		return 77;
208742542f5fSchristos
2088fe8aea9eSmrg	if (DPMSQueryExtension(dpy, &dummy, &dummy))
2089fe8aea9eSmrg		DPMSDisable(dpy);
2090fe8aea9eSmrg
209142542f5fSchristos	root = DefaultRootWindow(dpy);
209242542f5fSchristos
209342542f5fSchristos	signal(SIGALRM, SIG_IGN);
209442542f5fSchristos	XSetErrorHandler(_check_error_handler);
209542542f5fSchristos
209642542f5fSchristos	queue = setup_msc(dpy, root);
2097fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, 0, NULL);
2098fe8aea9eSmrg
2099fe8aea9eSmrg	error += test_future_msc(dpy, queue);
2100fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2101fe8aea9eSmrg
2102fe8aea9eSmrg	error += test_wrap_msc(dpy);
2103fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2104fe8aea9eSmrg
2105fe8aea9eSmrg	error += test_accuracy_msc(dpy, queue);
2106fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2107fe8aea9eSmrg
2108fe8aea9eSmrg	error += test_modulus_msc(dpy, queue);
2109fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2110fe8aea9eSmrg
2111fe8aea9eSmrg	error += test_exhaustion_msc(dpy, queue);
2112fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2113fe8aea9eSmrg
2114fe8aea9eSmrg	for (dummy = 0; dummy <= 3; dummy++) {
2115fe8aea9eSmrg		Window win;
2116fe8aea9eSmrg		uint64_t msc = 0;
2117fe8aea9eSmrg		XSetWindowAttributes attr;
2118fe8aea9eSmrg		Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
2119fe8aea9eSmrg		unsigned int width, height;
2120fe8aea9eSmrg		unsigned border, depth;
2121fe8aea9eSmrg		const char *phase;
2122fe8aea9eSmrg		int x, y;
2123fe8aea9eSmrg		void *Q;
2124fe8aea9eSmrg
2125fe8aea9eSmrg		attr.override_redirect = 1;
2126fe8aea9eSmrg
2127fe8aea9eSmrg		XGetGeometry(dpy, root, &win, &x, &y,
2128fe8aea9eSmrg			     &width, &height, &border, &depth);
2129fe8aea9eSmrg
2130fe8aea9eSmrg		_x_error_occurred = 0;
2131fe8aea9eSmrg		switch (dummy) {
2132fe8aea9eSmrg		case 0:
2133fe8aea9eSmrg			win = root;
2134fe8aea9eSmrg			phase = "root";
2135fe8aea9eSmrg			break;
2136fe8aea9eSmrg		case 1:
2137fe8aea9eSmrg			win = XCreateWindow(dpy, root,
2138fe8aea9eSmrg					    0, 0, width, height, 0, depth,
2139fe8aea9eSmrg					    InputOutput, visual,
2140fe8aea9eSmrg					    CWOverrideRedirect, &attr);
2141fe8aea9eSmrg			phase = "fullscreen";
2142fe8aea9eSmrg			break;
2143fe8aea9eSmrg		case 2:
2144fe8aea9eSmrg			win = XCreateWindow(dpy, root,
2145fe8aea9eSmrg					    0, 0, width/2, height/2, 0, depth,
2146fe8aea9eSmrg					    InputOutput, visual,
2147fe8aea9eSmrg					    CWOverrideRedirect, &attr);
2148fe8aea9eSmrg			phase = "window";
2149fe8aea9eSmrg			break;
2150fe8aea9eSmrg		case 3:
2151fe8aea9eSmrg			if (!has_composite(dpy))
2152fe8aea9eSmrg				continue;
2153fe8aea9eSmrg
2154fe8aea9eSmrg			win = XCreateWindow(dpy, root,
2155fe8aea9eSmrg					    0, 0, width, height, 0,
2156fe8aea9eSmrg					    DefaultDepth(dpy, DefaultScreen(dpy)),
2157fe8aea9eSmrg					    InputOutput,
2158fe8aea9eSmrg					    DefaultVisual(dpy, DefaultScreen(dpy)),
2159fe8aea9eSmrg					    CWOverrideRedirect, &attr);
2160fe8aea9eSmrg			XCompositeRedirectWindow(dpy, win, CompositeRedirectManual);
2161fe8aea9eSmrg			phase = "composite";
2162fe8aea9eSmrg			break;
2163fe8aea9eSmrg
2164fe8aea9eSmrg		default:
2165fe8aea9eSmrg			phase = "broken";
2166fe8aea9eSmrg			win = root;
2167fe8aea9eSmrg			abort();
2168fe8aea9eSmrg			break;
2169fe8aea9eSmrg		}
2170fe8aea9eSmrg
2171fe8aea9eSmrg		XMapWindow(dpy, win);
2172fe8aea9eSmrg		XSync(dpy, True);
2173fe8aea9eSmrg		if (_x_error_occurred)
2174fe8aea9eSmrg			continue;
2175fe8aea9eSmrg
2176fe8aea9eSmrg		Q = setup_msc(dpy, win);
2177fe8aea9eSmrg		msc = check_msc(dpy, win, Q, msc, NULL);
217842542f5fSchristos
2179fe8aea9eSmrg		error += test_whole(dpy, win, phase);
2180fe8aea9eSmrg		msc = check_msc(dpy, win, Q, msc, NULL);
2181fe8aea9eSmrg
2182fe8aea9eSmrg		error += test_double(dpy, win, phase, Q);
2183fe8aea9eSmrg		msc = check_msc(dpy, win, Q, msc, NULL);
2184fe8aea9eSmrg
2185fe8aea9eSmrg		error += test_future(dpy, win, phase, Q);
2186fe8aea9eSmrg		msc = check_msc(dpy, win, Q, msc, NULL);
2187fe8aea9eSmrg
2188fe8aea9eSmrg		error += test_accuracy(dpy, win, phase, Q);
2189fe8aea9eSmrg		msc = check_msc(dpy, win, Q, msc, NULL);
2190fe8aea9eSmrg
2191fe8aea9eSmrg		error += test_modulus(dpy, win, phase, Q);
2192fe8aea9eSmrg		msc = check_msc(dpy, win, Q, msc, NULL);
2193fe8aea9eSmrg
2194fe8aea9eSmrg		error += test_exhaustion(dpy, win, phase, Q);
2195fe8aea9eSmrg		msc = check_msc(dpy, win, Q, msc, NULL);
2196fe8aea9eSmrg
2197fe8aea9eSmrg		teardown_msc(dpy, Q);
2198fe8aea9eSmrg		if (win != root)
2199fe8aea9eSmrg			XDestroyWindow(dpy, win);
2200fe8aea9eSmrg	}
220142542f5fSchristos
220242542f5fSchristos	error += test_crtc(dpy, queue, last_msc);
2203fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
220442542f5fSchristos
220542542f5fSchristos	error += test_shm(dpy);
2206fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
220742542f5fSchristos
220842542f5fSchristos	error += test_dri3(dpy);
2209fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
2210fe8aea9eSmrg
2211fe8aea9eSmrg	error += test_dri3_tiling(dpy);
2212fe8aea9eSmrg	last_msc = check_msc(dpy, root, queue, last_msc, NULL);
221342542f5fSchristos
221442542f5fSchristos	teardown_msc(dpy, queue);
221542542f5fSchristos
2216fe8aea9eSmrg	if (DPMSQueryExtension(dpy, &dummy, &dummy))
2217fe8aea9eSmrg		DPMSEnable(dpy);
221842542f5fSchristos	return !!error;
221942542f5fSchristos}
2220