dri2-test.c revision fe8aea9e
1#include <stdint.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5#include <X11/Xlib.h>
6#include <X11/Xutil.h>
7#include <X11/extensions/Xfixes.h>
8#include <X11/extensions/Xrandr.h>
9#include <X11/Xlib-xcb.h>
10#include <xcb/xcb.h>
11#include <xcb/xcbext.h>
12#include <xcb/dri2.h>
13#include <unistd.h>
14#include <fcntl.h>
15#include <string.h>
16#include <time.h>
17
18#include <xf86drm.h>
19#include <drm.h>
20
21#include "dri2.h"
22
23#define COUNT 60
24
25static int prime[] = { 0, 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47, 51, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131 };
26
27static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
28{
29	XRRScreenResources *res;
30
31	res = XRRGetScreenResourcesCurrent(dpy, window);
32	if (res == NULL)
33		res = XRRGetScreenResources(dpy, window);
34
35	return res;
36}
37
38static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
39{
40	int i;
41
42	for (i = 0; i < res->nmode; i++) {
43		if (res->modes[i].id == id)
44			return &res->modes[i];
45	}
46
47	return NULL;
48}
49
50static int dri2_open(Display *dpy)
51{
52	drm_auth_t auth;
53	char *driver, *device;
54	int fd;
55
56	if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device))
57		return -1;
58
59	printf ("Connecting to %s driver on %s\n", driver, device);
60
61	fd = open(device, O_RDWR);
62	if (fd < 0)
63		return -1;
64
65	if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
66		return -1;
67
68	if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic))
69		return -1;
70
71	return fd;
72}
73
74static void dri2_copy_swap(Display *dpy, Drawable d,
75			   int width, int height, int has_front)
76{
77	XRectangle rect;
78	XserverRegion region;
79
80	rect.x = 0;
81	rect.y = 0;
82	rect.width = width;
83	rect.height = height;
84
85	region = XFixesCreateRegion(dpy, &rect, 1);
86	DRI2CopyRegion(dpy, d, region, DRI2BufferFrontLeft, DRI2BufferBackLeft);
87	if (has_front)
88		DRI2CopyRegion(dpy, d, region, DRI2BufferFakeFrontLeft, DRI2BufferFrontLeft);
89	XFixesDestroyRegion(dpy, region);
90}
91
92static double elapsed(const struct timespec *start,
93		      const struct timespec *end)
94{
95	return (end->tv_sec - start->tv_sec) +
96		1e-9*(end->tv_nsec - start->tv_nsec);
97}
98
99static uint64_t check_msc(Display *dpy, Window win, uint64_t last_msc)
100{
101	uint64_t current_msc, current_ust, current_sbc;
102	DRI2GetMSC(dpy, win, &current_ust, &current_msc, &current_sbc);
103	if (current_msc < last_msc) {
104		printf("Invalid MSC: was %llu, now %llu\n",
105		       (long long)last_msc, (long long)current_msc);
106	}
107	return current_msc;
108}
109
110static void wait_next_vblank(Display *dpy, Window win)
111{
112	uint64_t msc, ust, sbc;
113	DRI2WaitMSC(dpy, win, 0, 1, 0, &ust, &msc, &sbc);
114}
115
116static void swap_buffers(xcb_connection_t *c, Window win,
117		unsigned int *attachments, int nattachments)
118{
119	unsigned int seq[2];
120
121	seq[0] = xcb_dri2_swap_buffers_unchecked(c, win,
122						 0, 0, 0, 0, 0, 0).sequence;
123
124
125	seq[1] = xcb_dri2_get_buffers_unchecked(c, win,
126						nattachments, nattachments,
127						attachments).sequence;
128
129	xcb_flush(c);
130	xcb_discard_reply(c, seq[0]);
131	xcb_discard_reply(c, seq[1]);
132}
133
134static void run(Display *dpy, int width, int height,
135		unsigned int *attachments, int nattachments,
136		const char *name)
137{
138	xcb_connection_t *c = XGetXCBConnection(dpy);
139	Window win;
140	XSetWindowAttributes attr;
141	DRI2Buffer *buffers;
142	struct timespec start, end;
143	uint64_t start_msc, end_msc;
144	int modulus, remainder, count;
145
146	/* Be nasty and install a fullscreen window on top so that we
147	 * can guarantee we do not get clipped by children.
148	 */
149	attr.override_redirect = 1;
150	win = XCreateWindow(dpy, DefaultRootWindow(dpy),
151			 0, 0, width, height, 0,
152			 DefaultDepth(dpy, DefaultScreen(dpy)),
153			 InputOutput,
154			 DefaultVisual(dpy, DefaultScreen(dpy)),
155			 CWOverrideRedirect, &attr);
156	XMapWindow(dpy, win);
157
158	DRI2CreateDrawable(dpy, win);
159	DRI2SwapInterval(dpy, win, 1);
160	start_msc = check_msc(dpy, win, 0);
161
162	buffers = DRI2GetBuffers(dpy, win, &width, &height,
163				 attachments, nattachments, &count);
164	if (count != nattachments)
165		return;
166
167	swap_buffers(c, win, attachments, nattachments);
168	start_msc = check_msc(dpy, win, start_msc);
169	clock_gettime(CLOCK_MONOTONIC, &start);
170	for (count = 0; count < COUNT; count++)
171		swap_buffers(c, win, attachments, nattachments);
172	end_msc = check_msc(dpy, win, start_msc);
173	clock_gettime(CLOCK_MONOTONIC, &end);
174	printf("%d [%ld] %s (%dx%d) swaps in %fs.\n",
175	       count, (long)(end_msc - start_msc),
176	       name, width, height, elapsed(&start, &end));
177
178	swap_buffers(c, win, attachments, nattachments);
179	start_msc = check_msc(dpy, win, end_msc);
180	clock_gettime(CLOCK_MONOTONIC, &start);
181	for (count = 0; count < COUNT; count++)
182		dri2_copy_swap(dpy, win, width, height, nattachments == 2);
183	end_msc = check_msc(dpy, win, start_msc);
184	clock_gettime(CLOCK_MONOTONIC, &end);
185
186	printf("%d [%ld] %s (%dx%d) blits in %fs.\n",
187	       count, (long)(end_msc - start_msc),
188	       name, width, height, elapsed(&start, &end));
189
190	DRI2SwapInterval(dpy, win, 0);
191	wait_next_vblank(dpy, win);
192
193	swap_buffers(c, win, attachments, nattachments);
194	start_msc = check_msc(dpy, win, end_msc);
195	clock_gettime(CLOCK_MONOTONIC, &start);
196	for (count = 0; count < COUNT; count++)
197		swap_buffers(c, win, attachments, nattachments);
198	end_msc = check_msc(dpy, win, start_msc);
199	clock_gettime(CLOCK_MONOTONIC, &end);
200	printf("%d [%ld] %s (%dx%d) vblank=0 swaps in %fs.\n",
201	       count, (long)(end_msc - start_msc),
202	       name, width, height, elapsed(&start, &end));
203
204	start_msc = check_msc(dpy, win, end_msc);
205	clock_gettime(CLOCK_MONOTONIC, &start);
206	for (count = 0; count < COUNT; count++)
207		wait_next_vblank(dpy, win);
208	end_msc = check_msc(dpy, win, start_msc);
209	clock_gettime(CLOCK_MONOTONIC, &end);
210	printf("%d [%ld] %s waits in %fs.\n",
211	       count, (long)(end_msc - start_msc),
212	       name, elapsed(&start, &end));
213
214	printf("Testing past & future waits\n");
215	for (modulus = 1; modulus <= 128; modulus <<= 1) {
216		for (count = 0;  prime[count] < modulus; count++) {
217			uint64_t msc, ust, sbc;
218			uint64_t target;
219
220			remainder = prime[count];
221
222			DRI2WaitMSC(dpy, win, 0, 1, 0, &ust, &msc, &sbc);
223
224			target = msc + modulus + 1;
225			target &= -modulus;
226			target += remainder;
227
228			DRI2WaitMSC(dpy, win, target, modulus, remainder,
229				    &ust, &msc, &sbc);
230			if (msc != target) {
231				printf("Missed future MSC (%d, %d): expected=%lld, found=%lld\n",
232				       modulus, remainder,
233				       (long long)target, (long long)msc);
234			}
235
236			target = msc;
237			target &= -modulus;
238			target += remainder;
239			if (target <= msc)
240				target += modulus;
241
242			DRI2WaitMSC(dpy, win, msc, modulus, remainder,
243				    &ust, &msc, &sbc);
244
245			if (msc != target) {
246				printf("Missed past MSC (%d, %d): expected=%lld, found=%lld\n",
247				       modulus, remainder,
248				       (long long)target, (long long)msc);
249			}
250		}
251	}
252
253	XDestroyWindow(dpy, win);
254	free(buffers);
255
256	XSync(dpy, 1);
257}
258
259int main(void)
260{
261	Display *dpy;
262	int i, j, fd;
263	unsigned int attachments[] = {
264		DRI2BufferBackLeft,
265		DRI2BufferFrontLeft,
266	};
267	XRRScreenResources *res;
268	XRRCrtcInfo **original_crtc;
269	Window root;
270	uint64_t last_msc;
271
272	dpy = XOpenDisplay(NULL);
273	if (dpy == NULL)
274		return 77;
275
276	if (!XRRQueryVersion(dpy, &i, &j))
277		return 77;
278
279	fd = dri2_open(dpy);
280	if (fd < 0)
281		return 1;
282
283	root = DefaultRootWindow(dpy);
284	DRI2CreateDrawable(dpy, root);
285
286	res = _XRRGetScreenResourcesCurrent(dpy, root);
287	if (res == NULL)
288		return 1;
289
290	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
291	for (i = 0; i < res->ncrtc; i++)
292		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
293
294	printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc);
295	last_msc = check_msc(dpy, root, 0);
296	for (i = 0; i < res->ncrtc; i++)
297		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
298				 0, 0, None, RR_Rotate_0, NULL, 0);
299	last_msc = check_msc(dpy, root, last_msc);
300
301	for (i = 0; i < res->noutput; i++) {
302		XRROutputInfo *output;
303		XRRModeInfo *mode;
304
305		output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
306		if (output == NULL)
307			continue;
308
309		mode = NULL;
310		if (res->nmode)
311			mode = lookup_mode(res, output->modes[0]);
312
313		for (j = 0; mode && j < 2*output->ncrtc; j++) {
314			int c = j;
315			if (c >= output->ncrtc)
316				c = 2*output->ncrtc - j - 1;
317
318			printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n",
319			       i, c, (long)res->outputs[i], (long)output->crtcs[c]);
320			last_msc = check_msc(dpy, root, last_msc);
321			XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
322					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
323			last_msc = check_msc(dpy, root, last_msc);
324
325			run(dpy, mode->width, mode->height, attachments, 1, "fullscreen");
326			run(dpy, mode->width, mode->height, attachments, 2, "fullscreen (with front)");
327
328			run(dpy, mode->width/2, mode->height/2, attachments, 1, "windowed");
329			run(dpy, mode->width/2, mode->height/2, attachments, 2, "windowed (with front)");
330
331			last_msc = check_msc(dpy, root, last_msc);
332			XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
333					 0, 0, None, RR_Rotate_0, NULL, 0);
334			last_msc = check_msc(dpy, root, last_msc);
335		}
336
337		XRRFreeOutputInfo(output);
338	}
339
340	for (i = 0; i < res->ncrtc; i++)
341		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
342				 original_crtc[i]->x,
343				 original_crtc[i]->y,
344				 original_crtc[i]->mode,
345				 original_crtc[i]->rotation,
346				 original_crtc[i]->outputs,
347				 original_crtc[i]->noutput);
348	return 0;
349}
350