1428d7b3dSmrg#include <stdint.h>
2428d7b3dSmrg#include <stdio.h>
3428d7b3dSmrg#include <stdlib.h>
4428d7b3dSmrg
5428d7b3dSmrg#include <X11/Xlib.h>
6428d7b3dSmrg#include <X11/Xutil.h>
7428d7b3dSmrg#include <X11/extensions/Xfixes.h>
8428d7b3dSmrg#include <X11/extensions/Xrandr.h>
9428d7b3dSmrg#include <unistd.h>
10428d7b3dSmrg#include <fcntl.h>
11428d7b3dSmrg#include <string.h>
12428d7b3dSmrg#include <time.h>
13428d7b3dSmrg
14428d7b3dSmrg#include <xf86drm.h>
15428d7b3dSmrg#include <drm.h>
16428d7b3dSmrg
17428d7b3dSmrg#include "dri2.h"
18428d7b3dSmrg
19428d7b3dSmrg#define COUNT 60
20428d7b3dSmrg
21428d7b3dSmrgstatic inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
22428d7b3dSmrg{
23428d7b3dSmrg	XRRScreenResources *res;
24428d7b3dSmrg
25428d7b3dSmrg	res = XRRGetScreenResourcesCurrent(dpy, window);
26428d7b3dSmrg	if (res == NULL)
27428d7b3dSmrg		res = XRRGetScreenResources(dpy, window);
28428d7b3dSmrg
29428d7b3dSmrg	return res;
30428d7b3dSmrg}
31428d7b3dSmrg
32428d7b3dSmrgstatic XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
33428d7b3dSmrg{
34428d7b3dSmrg	int i;
35428d7b3dSmrg
36428d7b3dSmrg	for (i = 0; i < res->nmode; i++) {
37428d7b3dSmrg		if (res->modes[i].id == id)
38428d7b3dSmrg			return &res->modes[i];
39428d7b3dSmrg	}
40428d7b3dSmrg
41428d7b3dSmrg	return NULL;
42428d7b3dSmrg}
43428d7b3dSmrg
44428d7b3dSmrgstatic int dri2_open(Display *dpy)
45428d7b3dSmrg{
46428d7b3dSmrg	drm_auth_t auth;
47428d7b3dSmrg	char *driver, *device;
48428d7b3dSmrg	int fd;
49428d7b3dSmrg
50428d7b3dSmrg	if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device))
51428d7b3dSmrg		return -1;
52428d7b3dSmrg
53428d7b3dSmrg	printf ("Connecting to %s driver on %s\n", driver, device);
54428d7b3dSmrg
55428d7b3dSmrg	fd = open(device, O_RDWR);
56428d7b3dSmrg	if (fd < 0)
57428d7b3dSmrg		return -1;
58428d7b3dSmrg
59428d7b3dSmrg	if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
60428d7b3dSmrg		return -1;
61428d7b3dSmrg
62428d7b3dSmrg	if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic))
63428d7b3dSmrg		return -1;
64428d7b3dSmrg
65428d7b3dSmrg	return fd;
66428d7b3dSmrg}
67428d7b3dSmrg
68428d7b3dSmrgstatic void dri2_copy_swap(Display *dpy, Drawable d,
69428d7b3dSmrg			   int width, int height, int has_front)
70428d7b3dSmrg{
71428d7b3dSmrg	XRectangle rect;
72428d7b3dSmrg	XserverRegion region;
73428d7b3dSmrg
74428d7b3dSmrg	rect.x = 0;
75428d7b3dSmrg	rect.y = 0;
76428d7b3dSmrg	rect.width = width;
77428d7b3dSmrg	rect.height = height;
78428d7b3dSmrg
79428d7b3dSmrg	region = XFixesCreateRegion(dpy, &rect, 1);
80428d7b3dSmrg	DRI2CopyRegion(dpy, d, region, DRI2BufferFrontLeft, DRI2BufferBackLeft);
81428d7b3dSmrg	if (has_front)
82428d7b3dSmrg		DRI2CopyRegion(dpy, d, region, DRI2BufferFakeFrontLeft, DRI2BufferFrontLeft);
83428d7b3dSmrg	XFixesDestroyRegion(dpy, region);
84428d7b3dSmrg}
85428d7b3dSmrg
86428d7b3dSmrgstatic double elapsed(const struct timespec *start,
87428d7b3dSmrg		      const struct timespec *end)
88428d7b3dSmrg{
89428d7b3dSmrg	return (end->tv_sec - start->tv_sec) +
90428d7b3dSmrg		1e-9*(end->tv_nsec - start->tv_nsec);
91428d7b3dSmrg}
92428d7b3dSmrg
93428d7b3dSmrgstatic uint64_t check_msc(Display *dpy, Window win, uint64_t last_msc)
94428d7b3dSmrg{
95428d7b3dSmrg	uint64_t current_msc, current_ust, current_sbc;
96428d7b3dSmrg	DRI2GetMSC(dpy, win, &current_ust, &current_msc, &current_sbc);
97428d7b3dSmrg	if (current_msc < last_msc) {
98428d7b3dSmrg		printf("Invalid MSC: was %llu, now %llu\n",
99428d7b3dSmrg		       (long long)last_msc, (long long)current_msc);
100428d7b3dSmrg	}
101428d7b3dSmrg	return current_msc;
102428d7b3dSmrg}
103428d7b3dSmrg
104428d7b3dSmrgstatic void run(Display *dpy, int width, int height,
105428d7b3dSmrg		unsigned int *attachments, int nattachments,
106428d7b3dSmrg		const char *name)
107428d7b3dSmrg{
108428d7b3dSmrg	Window win;
109428d7b3dSmrg	XSetWindowAttributes attr;
110428d7b3dSmrg	int count;
111428d7b3dSmrg	DRI2Buffer *buffers;
112428d7b3dSmrg	struct timespec start, end;
113428d7b3dSmrg	uint64_t msc;
114428d7b3dSmrg
115428d7b3dSmrg	/* Be nasty and install a fullscreen window on top so that we
116428d7b3dSmrg	 * can guarantee we do not get clipped by children.
117428d7b3dSmrg	 */
118428d7b3dSmrg	attr.override_redirect = 1;
119428d7b3dSmrg	win = XCreateWindow(dpy, DefaultRootWindow(dpy),
120428d7b3dSmrg			 0, 0, width, height, 0,
121428d7b3dSmrg			 DefaultDepth(dpy, DefaultScreen(dpy)),
122428d7b3dSmrg			 InputOutput,
123428d7b3dSmrg			 DefaultVisual(dpy, DefaultScreen(dpy)),
124428d7b3dSmrg			 CWOverrideRedirect, &attr);
125428d7b3dSmrg	XMapWindow(dpy, win);
126428d7b3dSmrg
127428d7b3dSmrg	DRI2CreateDrawable(dpy, win);
128428d7b3dSmrg	msc = check_msc(dpy, win, 0);
129428d7b3dSmrg
130428d7b3dSmrg	buffers = DRI2GetBuffers(dpy, win, &width, &height,
131428d7b3dSmrg				 attachments, nattachments, &count);
132428d7b3dSmrg	if (count != nattachments)
133428d7b3dSmrg		return;
134428d7b3dSmrg
135428d7b3dSmrg	msc = check_msc(dpy, win, msc);
136428d7b3dSmrg	clock_gettime(CLOCK_MONOTONIC, &start);
137428d7b3dSmrg	for (count = 0; count < COUNT; count++)
138428d7b3dSmrg		DRI2SwapBuffers(dpy, win, 0, 0, 0);
139428d7b3dSmrg	msc = check_msc(dpy, win, msc);
140428d7b3dSmrg	clock_gettime(CLOCK_MONOTONIC, &end);
141428d7b3dSmrg	printf("%d %s (%dx%d) swaps in %fs.\n",
142428d7b3dSmrg	       count, name, width, height, elapsed(&start, &end));
143428d7b3dSmrg
144428d7b3dSmrg	msc = check_msc(dpy, win, msc);
145428d7b3dSmrg	clock_gettime(CLOCK_MONOTONIC, &start);
146428d7b3dSmrg	for (count = 0; count < COUNT; count++)
147428d7b3dSmrg		dri2_copy_swap(dpy, win, width, height, nattachments == 2);
148428d7b3dSmrg	msc = check_msc(dpy, win, msc);
149428d7b3dSmrg	clock_gettime(CLOCK_MONOTONIC, &end);
150428d7b3dSmrg
151428d7b3dSmrg	printf("%d %s (%dx%d) blits in %fs.\n",
152428d7b3dSmrg	       count, name, width, height, elapsed(&start, &end));
153428d7b3dSmrg
154428d7b3dSmrg	DRI2SwapInterval(dpy, win, 0);
155428d7b3dSmrg
156428d7b3dSmrg	msc = check_msc(dpy, win, msc);
157428d7b3dSmrg	clock_gettime(CLOCK_MONOTONIC, &start);
158428d7b3dSmrg	for (count = 0; count < COUNT; count++)
159428d7b3dSmrg		DRI2SwapBuffers(dpy, win, 0, 0, 0);
160428d7b3dSmrg	msc = check_msc(dpy, win, msc);
161428d7b3dSmrg	clock_gettime(CLOCK_MONOTONIC, &end);
162428d7b3dSmrg	printf("%d %s (%dx%d) vblank=0 swaps in %fs.\n",
163428d7b3dSmrg	       count, name, width, height, elapsed(&start, &end));
164428d7b3dSmrg
165428d7b3dSmrg	XDestroyWindow(dpy, win);
166428d7b3dSmrg	free(buffers);
167428d7b3dSmrg
168428d7b3dSmrg	XSync(dpy, 1);
169428d7b3dSmrg}
170428d7b3dSmrg
171428d7b3dSmrgint main(void)
172428d7b3dSmrg{
173428d7b3dSmrg	Display *dpy;
174428d7b3dSmrg	int i, j, fd;
175428d7b3dSmrg	unsigned int attachments[] = {
176428d7b3dSmrg		DRI2BufferBackLeft,
177428d7b3dSmrg		DRI2BufferFrontLeft,
178428d7b3dSmrg	};
179428d7b3dSmrg	XRRScreenResources *res;
180428d7b3dSmrg	XRRCrtcInfo **original_crtc;
181428d7b3dSmrg	Window root;
182428d7b3dSmrg	uint64_t last_msc;
183428d7b3dSmrg
184428d7b3dSmrg	dpy = XOpenDisplay(NULL);
185428d7b3dSmrg	if (dpy == NULL)
186428d7b3dSmrg		return 77;
187428d7b3dSmrg
188428d7b3dSmrg	if (!XRRQueryVersion(dpy, &i, &j))
189428d7b3dSmrg		return 77;
190428d7b3dSmrg
191428d7b3dSmrg	fd = dri2_open(dpy);
192428d7b3dSmrg	if (fd < 0)
193428d7b3dSmrg		return 1;
194428d7b3dSmrg
195428d7b3dSmrg	root = DefaultRootWindow(dpy);
196428d7b3dSmrg	DRI2CreateDrawable(dpy, root);
197428d7b3dSmrg
198428d7b3dSmrg	res = _XRRGetScreenResourcesCurrent(dpy, root);
199428d7b3dSmrg	if (res == NULL)
200428d7b3dSmrg		return 1;
201428d7b3dSmrg
202428d7b3dSmrg	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
203428d7b3dSmrg	for (i = 0; i < res->ncrtc; i++)
204428d7b3dSmrg		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
205428d7b3dSmrg
206428d7b3dSmrg	printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc);
207428d7b3dSmrg	last_msc = check_msc(dpy, root, 0);
208428d7b3dSmrg	for (i = 0; i < res->ncrtc; i++)
209428d7b3dSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
210428d7b3dSmrg				 0, 0, None, RR_Rotate_0, NULL, 0);
211428d7b3dSmrg	last_msc = check_msc(dpy, root, last_msc);
212428d7b3dSmrg
213428d7b3dSmrg	for (i = 0; i < res->noutput; i++) {
214428d7b3dSmrg		XRROutputInfo *output;
215428d7b3dSmrg		XRRModeInfo *mode;
216428d7b3dSmrg
217428d7b3dSmrg		output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
218428d7b3dSmrg		if (output == NULL)
219428d7b3dSmrg			continue;
220428d7b3dSmrg
221428d7b3dSmrg		mode = NULL;
222428d7b3dSmrg		if (res->nmode)
223428d7b3dSmrg			mode = lookup_mode(res, output->modes[0]);
224428d7b3dSmrg
225428d7b3dSmrg		for (j = 0; mode && j < 2*output->ncrtc; j++) {
226428d7b3dSmrg			int c = j;
227428d7b3dSmrg			if (c >= output->ncrtc)
228428d7b3dSmrg				c = 2*output->ncrtc - j - 1;
229428d7b3dSmrg
230428d7b3dSmrg			printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n",
231428d7b3dSmrg			       i, c, (long)res->outputs[i], (long)output->crtcs[c]);
232428d7b3dSmrg			last_msc = check_msc(dpy, root, last_msc);
233428d7b3dSmrg			XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
234428d7b3dSmrg					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
235428d7b3dSmrg			last_msc = check_msc(dpy, root, last_msc);
236428d7b3dSmrg
237428d7b3dSmrg			run(dpy, mode->width, mode->height, attachments, 1, "fullscreen");
238428d7b3dSmrg			run(dpy, mode->width, mode->height, attachments, 2, "fullscreen (with front)");
239428d7b3dSmrg
240428d7b3dSmrg			run(dpy, mode->width/2, mode->height/2, attachments, 1, "windowed");
241428d7b3dSmrg			run(dpy, mode->width/2, mode->height/2, attachments, 2, "windowed (with front)");
242428d7b3dSmrg
243428d7b3dSmrg			last_msc = check_msc(dpy, root, last_msc);
244428d7b3dSmrg			XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime,
245428d7b3dSmrg					 0, 0, None, RR_Rotate_0, NULL, 0);
246428d7b3dSmrg			last_msc = check_msc(dpy, root, last_msc);
247428d7b3dSmrg		}
248428d7b3dSmrg
249428d7b3dSmrg		XRRFreeOutputInfo(output);
250428d7b3dSmrg	}
251428d7b3dSmrg
252428d7b3dSmrg	for (i = 0; i < res->ncrtc; i++)
253428d7b3dSmrg		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
254428d7b3dSmrg				 original_crtc[i]->x,
255428d7b3dSmrg				 original_crtc[i]->y,
256428d7b3dSmrg				 original_crtc[i]->mode,
257428d7b3dSmrg				 original_crtc[i]->rotation,
258428d7b3dSmrg				 original_crtc[i]->outputs,
259428d7b3dSmrg				 original_crtc[i]->noutput);
260428d7b3dSmrg	return 0;
261428d7b3dSmrg}
262