132001f49Smrg/*
232001f49Smrg * Copyright © 2007 Intel Corporation
332001f49Smrg *
432001f49Smrg * Permission is hereby granted, free of charge, to any person obtaining a
532001f49Smrg * copy of this software and associated documentation files (the "Software"),
632001f49Smrg * to deal in the Software without restriction, including without limitation
732001f49Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
832001f49Smrg * and/or sell copies of the Software, and to permit persons to whom the
932001f49Smrg * Software is furnished to do so, subject to the following conditions:
1032001f49Smrg *
1132001f49Smrg * The above copyright notice and this permission notice (including the next
1232001f49Smrg * paragraph) shall be included in all copies or substantial portions of the
1332001f49Smrg * Software.
1432001f49Smrg *
1532001f49Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1632001f49Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1732001f49Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1832001f49Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1932001f49Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2032001f49Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2132001f49Smrg * IN THE SOFTWARE.
2232001f49Smrg *
2332001f49Smrg * Authors:
2432001f49Smrg *    Jesse Barnes <jesse.barnes@intel.com>
2532001f49Smrg *
2632001f49Smrg */
2732001f49Smrg
2832001f49Smrg/** @file glsync.c
2932001f49Smrg * The program is simple:  it paints a window alternating colors (red &
3032001f49Smrg * white) either as fast as possible or synchronized to vblank events
3132001f49Smrg *
3232001f49Smrg * If run normally, the program should display a window that exhibits
3332001f49Smrg * significant tearing between red and white colors (e.g. you might get
3432001f49Smrg * a "waterfall" effect of red and white horizontal bars).
3532001f49Smrg *
3632001f49Smrg * If run with the '-s b' option, the program should synchronize the
3732001f49Smrg * window color changes with the vertical blank period, resulting in a
3832001f49Smrg * window that looks orangish with a high frequency flicker (which may
3932001f49Smrg * be invisible).  If the window is moved to another screen, this
4032001f49Smrg * property should be preserved.  If the window spans two screens, it
4132001f49Smrg * shouldn't tear on whichever screen most of the window is on; the
4232001f49Smrg * portion on the other screen may show some tearing (like the
4332001f49Smrg * waterfall effect above).
4432001f49Smrg *
4532001f49Smrg * Other options include '-w <width>' and '-h <height' to set the
4632001f49Smrg * window size.
4732001f49Smrg */
4832001f49Smrg#include <stdio.h>
4932001f49Smrg#include <stdlib.h>
5032001f49Smrg#include <string.h>
5132001f49Smrg#include <unistd.h>
5232001f49Smrg#include <GL/gl.h>
5332001f49Smrg#include <GL/glx.h>
5432001f49Smrg#include <GL/glxext.h>
5532001f49Smrg#include <X11/X.h>
5632001f49Smrg#include <X11/Xlib.h>
5732001f49Smrg#include <X11/Xutil.h>
5832001f49Smrg
5932001f49Smrgstatic PFNGLXGETVIDEOSYNCSGIPROC video_sync_get;
6032001f49Smrgstatic PFNGLXSWAPINTERVALSGIPROC swap_interval;
6132001f49Smrgstatic PFNGLXWAITVIDEOSYNCSGIPROC video_sync;
6232001f49Smrg
6332001f49Smrg
6432001f49Smrgstatic int GLXExtensionSupported(Display *dpy, const char *extension)
6532001f49Smrg{
6632001f49Smrg	const char *extensionsString, *pos;
6732001f49Smrg
6832001f49Smrg	extensionsString = glXQueryExtensionsString(dpy, DefaultScreen(dpy));
6932001f49Smrg
7032001f49Smrg	pos = strstr(extensionsString, extension);
7132001f49Smrg
7232001f49Smrg	if (pos != NULL && (pos == extensionsString || pos[-1] == ' ') &&
7332001f49Smrg	    (pos[strlen(extension)] == ' ' || pos[strlen(extension)] == '\0'))
7432001f49Smrg		return 1;
7532001f49Smrg
7632001f49Smrg	return 0;
7732001f49Smrg}
7832001f49Smrg
7932001f49Smrgextern char *optarg;
8032001f49Smrgextern int optind, opterr, optopt;
8132001f49Smrgstatic char optstr[] = "w:h:s:vi:";
8232001f49Smrg
8332001f49Smrgenum sync_type {
8432001f49Smrg	none = 0,
8532001f49Smrg	sgi_video_sync,
8632001f49Smrg	buffer_swap
8732001f49Smrg};
8832001f49Smrg
8932001f49Smrgstatic void usage(char *name)
9032001f49Smrg{
9132001f49Smrg	printf("usage: %s [-w <width>] [-h <height>] [-s<sync method>] "
9232001f49Smrg	       "[-v]\n", name);
9332001f49Smrg	printf("\t-s<sync method>:\n");
9432001f49Smrg	printf("\t\tn: none\n");
9532001f49Smrg	printf("\t\ts: SGI video sync extension\n");
9632001f49Smrg	printf("\t\tb: buffer swap\n");
9732001f49Smrg	printf("\t-i<swap interval>\n");
9832001f49Smrg	printf("\t-v: verbose (print count)\n");
9932001f49Smrg	exit(-1);
10032001f49Smrg}
10132001f49Smrg
10232001f49Smrgint main(int argc, char *argv[])
10332001f49Smrg{
10432001f49Smrg	Display *disp;
10532001f49Smrg	XVisualInfo *pvi;
10632001f49Smrg	XSetWindowAttributes swa;
10732001f49Smrg	GLint last_val = -1;
10832001f49Smrg        unsigned int count = 0;
10932001f49Smrg	Window winGL;
11032001f49Smrg	GLXContext context;
11132001f49Smrg	int dummy;
11232001f49Smrg	Atom wmDelete;
11332001f49Smrg	enum sync_type waitforsync = none;
11432001f49Smrg	int width = 500, height = 500, verbose = 0, interval = 1;
11532001f49Smrg	int c, i = 1;
11632001f49Smrg	int ret;
11732001f49Smrg	int attribs[] = { GLX_RGBA,
11832001f49Smrg                     GLX_RED_SIZE, 1,
11932001f49Smrg                     GLX_GREEN_SIZE, 1,
12032001f49Smrg                     GLX_BLUE_SIZE, 1,
12132001f49Smrg                     None };
12232001f49Smrg	int db_attribs[] = { GLX_RGBA,
12332001f49Smrg                     GLX_RED_SIZE, 1,
12432001f49Smrg                     GLX_GREEN_SIZE, 1,
12532001f49Smrg                     GLX_BLUE_SIZE, 1,
12632001f49Smrg                     GLX_DOUBLEBUFFER,
12732001f49Smrg                     GLX_DEPTH_SIZE, 1,
12832001f49Smrg                     None };
12932001f49Smrg	XSizeHints sizehints;
13032001f49Smrg
13132001f49Smrg	opterr = 0;
13232001f49Smrg	while ((c = getopt(argc, argv, optstr)) != -1) {
13332001f49Smrg		switch (c) {
13432001f49Smrg		case 'w':
13532001f49Smrg			width = atoi(optarg);
13632001f49Smrg			break;
13732001f49Smrg		case 'h':
13832001f49Smrg			height = atoi(optarg);
13932001f49Smrg			break;
14032001f49Smrg		case 's':
14132001f49Smrg			switch (optarg[0]) {
14232001f49Smrg			case 'n':
14332001f49Smrg				waitforsync = none;
14432001f49Smrg				break;
14532001f49Smrg			case 's':
14632001f49Smrg				waitforsync = sgi_video_sync;
14732001f49Smrg				break;
14832001f49Smrg			case 'b':
14932001f49Smrg				waitforsync = buffer_swap;
15032001f49Smrg				break;
15132001f49Smrg			default:
15232001f49Smrg				usage(argv[0]);
15332001f49Smrg				break;
15432001f49Smrg			}
15532001f49Smrg			break;
15632001f49Smrg		case 'v':
15732001f49Smrg			verbose = 1;
15832001f49Smrg			break;
15932001f49Smrg		case 'i':
16032001f49Smrg			interval = atoi(optarg);
16132001f49Smrg			break;
16232001f49Smrg		default:
16332001f49Smrg			usage(argv[0]);
16432001f49Smrg			break;
16532001f49Smrg		}
16632001f49Smrg	}
16732001f49Smrg
16832001f49Smrg	disp = XOpenDisplay(NULL);
16932001f49Smrg	if (!disp) {
17032001f49Smrg		fprintf(stderr, "failed to open display\n");
17132001f49Smrg		return -1;
17232001f49Smrg	}
17332001f49Smrg
17432001f49Smrg	if (!glXQueryExtension(disp, &dummy, &dummy)) {
17532001f49Smrg		fprintf(stderr, "glXQueryExtension failed\n");
17632001f49Smrg		return -1;
17732001f49Smrg	}
17832001f49Smrg
17932001f49Smrg	if (!GLXExtensionSupported(disp, "GLX_SGI_video_sync")) {
18032001f49Smrg		fprintf(stderr, "GLX_SGI_video_sync not supported, exiting\n");
18132001f49Smrg		return -1;
18232001f49Smrg	}
18332001f49Smrg
18432001f49Smrg	if (waitforsync != buffer_swap) {
18532001f49Smrg		pvi = glXChooseVisual(disp, DefaultScreen(disp), attribs);
18632001f49Smrg	} else {
18732001f49Smrg		pvi = glXChooseVisual(disp, DefaultScreen(disp), db_attribs);
18832001f49Smrg	}
18932001f49Smrg
19032001f49Smrg	if (!pvi) {
19132001f49Smrg		fprintf(stderr, "failed to choose visual, exiting\n");
19232001f49Smrg		return -1;
19332001f49Smrg	}
19432001f49Smrg
19532001f49Smrg	pvi->screen = DefaultScreen(disp);
19632001f49Smrg
19732001f49Smrg	swa.colormap = XCreateColormap(disp, RootWindow(disp, pvi->screen),
19832001f49Smrg				       pvi->visual, AllocNone);
19932001f49Smrg	swa.border_pixel = 0;
20032001f49Smrg	swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |
20132001f49Smrg		StructureNotifyMask;
20232001f49Smrg	winGL = XCreateWindow(disp, RootWindow(disp, pvi->screen),
20332001f49Smrg			      0, 0,
20432001f49Smrg			      width, height,
20532001f49Smrg			      0, pvi->depth, InputOutput, pvi->visual,
20632001f49Smrg			      CWBorderPixel | CWColormap | CWEventMask, &swa);
20732001f49Smrg	if (!winGL) {
20832001f49Smrg		fprintf(stderr, "window creation failed\n");
20932001f49Smrg		return -1;
21032001f49Smrg	}
21132001f49Smrg        wmDelete = XInternAtom(disp, "WM_DELETE_WINDOW", True);
21232001f49Smrg        XSetWMProtocols(disp, winGL, &wmDelete, 1);
21332001f49Smrg
21432001f49Smrg	sizehints.x = 0;
21532001f49Smrg	sizehints.y = 0;
21632001f49Smrg	sizehints.width  = width;
21732001f49Smrg	sizehints.height = height;
21832001f49Smrg	sizehints.flags = USSize | USPosition;
21932001f49Smrg
22032001f49Smrg	XSetNormalHints(disp, winGL, &sizehints);
22132001f49Smrg	XSetStandardProperties(disp, winGL, "glsync test", "glsync text",
22232001f49Smrg			       None, NULL, 0, &sizehints);
22332001f49Smrg
22432001f49Smrg	context = glXCreateContext(disp, pvi, NULL, GL_TRUE);
22532001f49Smrg	if (!context) {
22632001f49Smrg		fprintf(stderr, "failed to create glx context\n");
22732001f49Smrg		return -1;
22832001f49Smrg	}
22932001f49Smrg
23032001f49Smrg	XMapWindow(disp, winGL);
23132001f49Smrg	ret = glXMakeCurrent(disp, winGL, context);
23232001f49Smrg	if (!ret) {
23332001f49Smrg		fprintf(stderr, "failed to make context current: %d\n", ret);
23432001f49Smrg	}
23532001f49Smrg
23632001f49Smrg	video_sync_get = (PFNGLXGETVIDEOSYNCSGIPROC) glXGetProcAddress((unsigned char *)"glXGetVideoSyncSGI");
23732001f49Smrg	video_sync = (PFNGLXWAITVIDEOSYNCSGIPROC) glXGetProcAddress((unsigned char *)"glXWaitVideoSyncSGI");
23832001f49Smrg
23932001f49Smrg	swap_interval = (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddress((unsigned char *)"glXSwapIntervalSGI");
24032001f49Smrg
24132001f49Smrg	if (!video_sync_get || !video_sync || !swap_interval) {
24232001f49Smrg		fprintf(stderr, "failed to get sync functions\n");
24332001f49Smrg		return -1;
24432001f49Smrg	}
24532001f49Smrg
24632001f49Smrg	if (waitforsync == buffer_swap) {
24732001f49Smrg		swap_interval(interval);
24832001f49Smrg		fprintf(stderr, "set swap interval to %d\n", interval);
24932001f49Smrg	}
25032001f49Smrg	video_sync_get(&count);
25132001f49Smrg	count++;
25232001f49Smrg	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
25332001f49Smrg	while (i++) {
25432001f49Smrg		/* Alternate colors to make tearing obvious */
25532001f49Smrg		if (i & 1) {
25632001f49Smrg			glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
25732001f49Smrg			glColor3f(1.0f, 1.0f, 1.0f);
25832001f49Smrg		} else {
25932001f49Smrg			glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
26032001f49Smrg			glColor3f(1.0f, 0.0f, 0.0f);
26132001f49Smrg		}
26232001f49Smrg
26332001f49Smrg		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
26432001f49Smrg		glRectf(0, 0, width, height);
26532001f49Smrg
26632001f49Smrg		/* Wait for vsync */
26732001f49Smrg		if (waitforsync == sgi_video_sync) {
26832001f49Smrg			if (verbose)
26932001f49Smrg				fprintf(stderr, "waiting on count %d\n", count);
27032001f49Smrg			video_sync(2, (count + 1) % 2, &count);
27132001f49Smrg			if (count < last_val)
27232001f49Smrg				fprintf(stderr, "error:  vblank count went backwards: %d -> %d\n", last_val, count);
27332001f49Smrg			if (count == last_val)
27432001f49Smrg				fprintf(stderr, "error:  count didn't change: %d\n", count);
27532001f49Smrg			last_val = count;
27632001f49Smrg			glFlush();
27732001f49Smrg		} else if (waitforsync == buffer_swap) {
27832001f49Smrg			glXSwapBuffers(disp, winGL);
27932001f49Smrg		} else {
28032001f49Smrg			video_sync_get(&count);
28132001f49Smrg			sleep(1);
28232001f49Smrg			glFinish();
28332001f49Smrg		}
28432001f49Smrg
28532001f49Smrg		if (verbose) {
28632001f49Smrg			video_sync_get(&count);
28732001f49Smrg			fprintf(stderr, "current count: %d\n", count);
28832001f49Smrg		}
28932001f49Smrg	}
29032001f49Smrg
29132001f49Smrg	XDestroyWindow(disp, winGL);
29232001f49Smrg	glXDestroyContext(disp, context);
29332001f49Smrg	XCloseDisplay(disp);
29432001f49Smrg
29532001f49Smrg	return 0;
29632001f49Smrg}
297