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 <unistd.h>
10#include <fcntl.h>
11#include <string.h>
12#include <time.h>
13
14#include <xf86drm.h>
15#include <drm.h>
16
17#include "dri2.h"
18
19#define COUNT 60
20
21static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
22{
23	XRRScreenResources *res;
24
25	res = XRRGetScreenResourcesCurrent(dpy, window);
26	if (res == NULL)
27		res = XRRGetScreenResources(dpy, window);
28
29	return res;
30}
31
32static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
33{
34	int i;
35
36	for (i = 0; i < res->nmode; i++) {
37		if (res->modes[i].id == id)
38			return &res->modes[i];
39	}
40
41	return NULL;
42}
43
44static int dri2_open(Display *dpy)
45{
46	drm_auth_t auth;
47	char *driver, *device;
48	int fd;
49
50	if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device))
51		return -1;
52
53	printf ("Connecting to %s driver on %s\n", driver, device);
54
55	fd = open(device, O_RDWR);
56	if (fd < 0)
57		return -1;
58
59	if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
60		return -1;
61
62	if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic))
63		return -1;
64
65	return fd;
66}
67
68static void dri2_copy_swap(Display *dpy, Drawable d,
69			   int width, int height, int has_front)
70{
71	XRectangle rect;
72	XserverRegion region;
73
74	rect.x = 0;
75	rect.y = 0;
76	rect.width = width;
77	rect.height = height;
78
79	region = XFixesCreateRegion(dpy, &rect, 1);
80	DRI2CopyRegion(dpy, d, region, DRI2BufferFrontLeft, DRI2BufferBackLeft);
81	if (has_front)
82		DRI2CopyRegion(dpy, d, region, DRI2BufferFakeFrontLeft, DRI2BufferFrontLeft);
83	XFixesDestroyRegion(dpy, region);
84}
85
86static double elapsed(const struct timespec *start,
87		      const struct timespec *end)
88{
89	return (end->tv_sec - start->tv_sec) +
90		1e-9*(end->tv_nsec - start->tv_nsec);
91}
92
93static uint64_t check_msc(Display *dpy, Window win, uint64_t last_msc)
94{
95	uint64_t current_msc, current_ust, current_sbc;
96	DRI2GetMSC(dpy, win, &current_ust, &current_msc, &current_sbc);
97	if (current_msc < last_msc) {
98		printf("Invalid MSC: was %llu, now %llu\n",
99		       (long long)last_msc, (long long)current_msc);
100	}
101	return current_msc;
102}
103
104static void run(Display *dpy, int width, int height,
105		unsigned int *attachments, int nattachments,
106		const char *name)
107{
108	Window win;
109	XSetWindowAttributes attr;
110	int count;
111	DRI2Buffer *buffers;
112	struct timespec start, end;
113	uint64_t msc;
114
115	/* Be nasty and install a fullscreen window on top so that we
116	 * can guarantee we do not get clipped by children.
117	 */
118	attr.override_redirect = 1;
119	win = XCreateWindow(dpy, DefaultRootWindow(dpy),
120			 0, 0, width, height, 0,
121			 DefaultDepth(dpy, DefaultScreen(dpy)),
122			 InputOutput,
123			 DefaultVisual(dpy, DefaultScreen(dpy)),
124			 CWOverrideRedirect, &attr);
125	XMapWindow(dpy, win);
126
127	DRI2CreateDrawable(dpy, win);
128	msc = check_msc(dpy, win, 0);
129
130	buffers = DRI2GetBuffers(dpy, win, &width, &height,
131				 attachments, nattachments, &count);
132	if (count != nattachments)
133		return;
134
135	msc = check_msc(dpy, win, msc);
136	clock_gettime(CLOCK_MONOTONIC, &start);
137	for (count = 0; count < COUNT; count++)
138		DRI2SwapBuffers(dpy, win, 0, 0, 0);
139	msc = check_msc(dpy, win, msc);
140	clock_gettime(CLOCK_MONOTONIC, &end);
141	printf("%d %s (%dx%d) swaps in %fs.\n",
142	       count, name, width, height, elapsed(&start, &end));
143
144	msc = check_msc(dpy, win, msc);
145	clock_gettime(CLOCK_MONOTONIC, &start);
146	for (count = 0; count < COUNT; count++)
147		dri2_copy_swap(dpy, win, width, height, nattachments == 2);
148	msc = check_msc(dpy, win, msc);
149	clock_gettime(CLOCK_MONOTONIC, &end);
150
151	printf("%d %s (%dx%d) blits in %fs.\n",
152	       count, name, width, height, elapsed(&start, &end));
153
154	DRI2SwapInterval(dpy, win, 0);
155
156	msc = check_msc(dpy, win, msc);
157	clock_gettime(CLOCK_MONOTONIC, &start);
158	for (count = 0; count < COUNT; count++)
159		DRI2SwapBuffers(dpy, win, 0, 0, 0);
160	msc = check_msc(dpy, win, msc);
161	clock_gettime(CLOCK_MONOTONIC, &end);
162	printf("%d %s (%dx%d) vblank=0 swaps in %fs.\n",
163	       count, name, width, height, elapsed(&start, &end));
164
165	XDestroyWindow(dpy, win);
166	free(buffers);
167
168	XSync(dpy, 1);
169}
170
171int main(void)
172{
173	Display *dpy;
174	int i, j, fd;
175	unsigned int attachments[] = {
176		DRI2BufferBackLeft,
177		DRI2BufferFrontLeft,
178	};
179	XRRScreenResources *res;
180	XRRCrtcInfo **original_crtc;
181	Window root;
182	uint64_t last_msc;
183
184	dpy = XOpenDisplay(NULL);
185	if (dpy == NULL)
186		return 77;
187
188	if (!XRRQueryVersion(dpy, &i, &j))
189		return 77;
190
191	fd = dri2_open(dpy);
192	if (fd < 0)
193		return 1;
194
195	root = DefaultRootWindow(dpy);
196	DRI2CreateDrawable(dpy, root);
197
198	res = _XRRGetScreenResourcesCurrent(dpy, root);
199	if (res == NULL)
200		return 1;
201
202	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
203	for (i = 0; i < res->ncrtc; i++)
204		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
205
206	printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc);
207	last_msc = check_msc(dpy, root, 0);
208	for (i = 0; i < res->ncrtc; i++)
209		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
210				 0, 0, None, RR_Rotate_0, NULL, 0);
211	last_msc = check_msc(dpy, root, last_msc);
212
213	for (i = 0; i < res->noutput; i++) {
214		XRROutputInfo *output;
215		XRRModeInfo *mode;
216
217		output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
218		if (output == NULL)
219			continue;
220
221		mode = NULL;
222		if (res->nmode)
223			mode = lookup_mode(res, output->modes[0]);
224
225		for (j = 0; mode && j < 2*output->ncrtc; j++) {
226			int c = j;
227			if (c >= output->ncrtc)
228				c = 2*output->ncrtc - j - 1;
229
230			printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n",
231			       i, c, (long)res->outputs[i], (long)output->crtcs[c]);
232			last_msc = check_msc(dpy, root, last_msc);
233			XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
234					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
235			last_msc = check_msc(dpy, root, last_msc);
236
237			run(dpy, mode->width, mode->height, attachments, 1, "fullscreen");
238			run(dpy, mode->width, mode->height, attachments, 2, "fullscreen (with front)");
239
240			run(dpy, mode->width/2, mode->height/2, attachments, 1, "windowed");
241			run(dpy, mode->width/2, mode->height/2, attachments, 2, "windowed (with front)");
242
243			last_msc = check_msc(dpy, root, last_msc);
244			XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
245					 0, 0, None, RR_Rotate_0, NULL, 0);
246			last_msc = check_msc(dpy, root, last_msc);
247		}
248
249		XRRFreeOutputInfo(output);
250	}
251
252	for (i = 0; i < res->ncrtc; i++)
253		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
254				 original_crtc[i]->x,
255				 original_crtc[i]->y,
256				 original_crtc[i]->mode,
257				 original_crtc[i]->rotation,
258				 original_crtc[i]->outputs,
259				 original_crtc[i]->noutput);
260	return 0;
261}
262