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