From 72c888869f4e5074e57c349ec356798959be791e Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 11 Oct 2007 18:25:12 -0600 Subject: [PATCH] Example of cooperative rendering into one window by two processes --- progs/xdemos/Makefile | 12 +- progs/xdemos/corender.c | 396 ++++++++++++++++++++++++++++++++++++++++++++++++ progs/xdemos/ipc.c | 264 ++++++++++++++++++++++++++++++++ progs/xdemos/ipc.h | 16 ++ 4 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 progs/xdemos/corender.c create mode 100644 progs/xdemos/ipc.c create mode 100644 progs/xdemos/ipc.h diff --git a/progs/xdemos/Makefile b/progs/xdemos/Makefile index 4c4ecac..a7ba9af 100644 --- a/progs/xdemos/Makefile +++ b/progs/xdemos/Makefile @@ -8,7 +8,9 @@ INCDIR = $(TOP)/include LIB_DEP = $(TOP)/$(LIB_DIR)/$(GL_LIB_NAME) $(TOP)/$(LIB_DIR)/$(GLU_LIB_NAME) -PROGS = glthreads \ +PROGS = \ + corender \ + glthreads \ glxdemo \ glxgears \ glxgears_fbconfig \ @@ -83,3 +85,11 @@ xuserotfont.o: xuserotfont.c xuserotfont.h xrotfontdemo.o: xrotfontdemo.c xuserotfont.h $(CC) -c -I. -I$(INCDIR) $(X11_INCLUDES) $(CFLAGS) xrotfontdemo.c +corender: corender.o ipc.o + $(CC) $(CFLAGS) corender.o ipc.o $(APP_LIB_DEPS) -o $@ + +corender.o: corender.c ipc.h + $(CC) -c -I. -I$(INCDIR) $(X11_INCLUDES) $(CFLAGS) corender.c + +ipc.o: ipc.c ipc.h + $(CC) -c -I. -I$(INCDIR) $(X11_INCLUDES) $(CFLAGS) ipc.c \ No newline at end of file diff --git a/progs/xdemos/corender.c b/progs/xdemos/corender.c new file mode 100644 index 0000000..02e4ac0 --- /dev/null +++ b/progs/xdemos/corender.c @@ -0,0 +1,396 @@ +/** + * Example of cooperative rendering into one window by two processes. + * The first instance of the program creates the GLX window. + * The second instance of the program gets the window ID from the first + * and draws into it. + * Socket IPC is used for synchronization. + * + * Usage: + * 1. run 'corender &' + * 2. run 'corender 2' (any arg will do) + * + * Brian Paul + * 11 Oct 2007 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipc.h" + + +static int MyID = 0; /* 0 or 1 */ +static int WindowID = 0; +static GLXContext Context = 0; +static int Width = 700, Height = 350; +static int Rot = 0; +static int Sock = 0; + +static GLfloat Red[4] = {1.0, 0.2, 0.2, 1.0}; +static GLfloat Blue[4] = {0.2, 0.2, 1.0, 1.0}; + +static int Sync = 1; /** synchronized rendering? */ + + +static void +setup_ipc(void) +{ + int k, port = 10001; + + if (MyID == 0) { + /* I'm the first one, wait for connection from second */ + k = CreatePort(&port); + assert(k != -1); + + printf("Waiting for connection from another 'corender'\n"); + Sock = AcceptConnection(k); + + printf("Got connection, sending windowID\n"); + + /* send windowID */ + SendData(Sock, &WindowID, sizeof(WindowID)); + } + else { + /* I'm the second one, connect to first */ + char hostname[1000]; + + MyHostName(hostname, 1000); + Sock = Connect(hostname, port); + assert(Sock != -1); + + /* get windowID */ + ReceiveData(Sock, &WindowID, sizeof(WindowID)); + printf("Contacted first 'corender', getting WindowID\n"); + } +} + + + +/** from GLUT */ +static void +doughnut(GLfloat r, GLfloat R, GLint nsides, GLint rings) +{ + int i, j; + GLfloat theta, phi, theta1; + GLfloat cosTheta, sinTheta; + GLfloat cosTheta1, sinTheta1; + GLfloat ringDelta, sideDelta; + + ringDelta = 2.0 * M_PI / rings; + sideDelta = 2.0 * M_PI / nsides; + + theta = 0.0; + cosTheta = 1.0; + sinTheta = 0.0; + for (i = rings - 1; i >= 0; i--) { + theta1 = theta + ringDelta; + cosTheta1 = cos(theta1); + sinTheta1 = sin(theta1); + glBegin(GL_QUAD_STRIP); + phi = 0.0; + for (j = nsides; j >= 0; j--) { + GLfloat cosPhi, sinPhi, dist; + + phi += sideDelta; + cosPhi = cos(phi); + sinPhi = sin(phi); + dist = R + r * cosPhi; + + glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi); + glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi); + glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi); + glVertex3f(cosTheta * dist, -sinTheta * dist, r * sinPhi); + } + glEnd(); + theta = theta1; + cosTheta = cosTheta1; + sinTheta = sinTheta1; + } +} + + +static void +redraw(Display *dpy) +{ + int dbg = 0; + + glXMakeCurrent(dpy, WindowID, Context); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_DEPTH_TEST); + glClearColor(0.5, 0.5, 0.5, 0.0); + + if (MyID == 0) { + /* First process */ + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + glTranslatef(-1, 0, 0); + glRotatef(Rot, 1, 0, 0); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Red); + doughnut(0.5, 2.0, 20, 30); + glPopMatrix(); + + glFinish(); + if (!Sync) { + usleep(1000*10); + } + + /* signal second process to render */ + if (Sync) { + int code = 1; + if (dbg) printf("0: send signal\n"); + SendData(Sock, &code, sizeof(code)); + SendData(Sock, &Rot, sizeof(Rot)); + } + + /* wait for second process to finish rendering */ + if (Sync) { + int code = 0; + if (dbg) printf("0: wait signal\n"); + ReceiveData(Sock, &code, sizeof(code)); + if (dbg) printf("0: got signal\n"); + assert(code == 2); + } + + } + else { + /* Second process */ + + /* wait for first process's signal for me to render */ + if (Sync) { + int code = 0; + if (dbg) printf("1: wait signal\n"); + ReceiveData(Sock, &code, sizeof(code)); + ReceiveData(Sock, &Rot, sizeof(Rot)); + + if (dbg) printf("1: got signal\n"); + assert(code == 1); + } + + /* XXX this clear should not be here, but for some reason, it + * makes things _mostly_ work correctly w/ NVIDIA's driver. + * There's only occasional glitches. + * Without this glClear(), depth buffer for the second process + * is pretty much broken. + */ + //glClear(GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + glTranslatef(1, 0, 0); + glRotatef(Rot + 90 , 1, 0, 0); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Blue); + doughnut(0.5, 2.0, 20, 30); + glPopMatrix(); + glFinish(); + + glXSwapBuffers(dpy, WindowID); + usleep(1000*10); + + /* signal first process that I'm done rendering */ + if (Sync) { + int code = 2; + if (dbg) printf("1: send signal\n"); + SendData(Sock, &code, sizeof(code)); + } + } +} + + +static void +resize(Display *dpy, int width, int height) +{ + float ar = (float) width / height; + + glXMakeCurrent(dpy, WindowID, Context); + + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-ar, ar, 1.0, -1.0, 5.0, 200.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -15); + + Width = width; + Height = height; +} + + + +static void +set_window_title(Display *dpy, Window win, const char *title) +{ + XSizeHints sizehints; + sizehints.flags = 0; + XSetStandardProperties(dpy, win, title, title, + None, (char **)NULL, 0, &sizehints); +} + + +static Window +make_gl_window(Display *dpy, XVisualInfo *visinfo, int width, int height) +{ + int scrnum; + XSetWindowAttributes attr; + unsigned long mask; + Window root; + Window win; + int x = 0, y = 0; + char *name = NULL; + + scrnum = DefaultScreen( dpy ); + root = RootWindow( dpy, scrnum ); + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); + attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + win = XCreateWindow( dpy, root, x, y, width, height, + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &attr ); + + /* set hints and properties */ + { + XSizeHints sizehints; + sizehints.x = x; + sizehints.y = y; + sizehints.width = width; + sizehints.height = height; + sizehints.flags = USSize | USPosition; + XSetNormalHints(dpy, win, &sizehints); + XSetStandardProperties(dpy, win, name, name, + None, (char **)NULL, 0, &sizehints); + } + + return win; +} + + +static void +set_event_mask(Display *dpy, Window win) +{ + XSetWindowAttributes attr; + attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; + XChangeWindowAttributes(dpy, win, CWEventMask, &attr); +} + + +static void +event_loop(Display *dpy) +{ + while (1) { + while (XPending(dpy) > 0) { + XEvent event; + XNextEvent(dpy, &event); + + switch (event.type) { + case Expose: + redraw(dpy); + break; + case ConfigureNotify: + resize(dpy, event.xconfigure.width, event.xconfigure.height); + break; + case KeyPress: + { + char buffer[10]; + int r, code; + code = XLookupKeysym(&event.xkey, 0); + if (code == XK_Left) { + } + else { + r = XLookupString(&event.xkey, buffer, sizeof(buffer), + NULL, NULL); + if (buffer[0] == 27) { + exit(0); + } + } + } + default: + /* nothing */ + ; + } + } + + if (MyID == 0 || !Sync) + Rot += 1; + redraw(dpy); + } +} + + +static XVisualInfo * +choose_visual(Display *dpy) +{ + int attribs[] = { GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None }; + int scrnum = DefaultScreen( dpy ); + return glXChooseVisual(dpy, scrnum, attribs); +} + + +static void +parse_opts(int argc, char *argv[]) +{ + if (argc > 1) { + MyID = 1; + } +} + + +int +main( int argc, char *argv[] ) +{ + Display *dpy; + XVisualInfo *visinfo; + + parse_opts(argc, argv); + + dpy = XOpenDisplay(NULL); + + visinfo = choose_visual(dpy); + + Context = glXCreateContext( dpy, visinfo, NULL, True ); + if (!Context) { + printf("Error: glXCreateContext failed\n"); + exit(1); + } + + if (MyID == 0) { + WindowID = make_gl_window(dpy, visinfo, Width, Height); + set_window_title(dpy, WindowID, "corender"); + XMapWindow(dpy, WindowID); + /*printf("WindowID 0x%x\n", (int) WindowID);*/ + } + + /* do ipc hand-shake here */ + setup_ipc(); + assert(Sock); + assert(WindowID); + + if (MyID == 1) { + set_event_mask(dpy, WindowID); + } + + resize(dpy, Width, Height); + + event_loop(dpy); + + return 0; +} diff --git a/progs/xdemos/ipc.c b/progs/xdemos/ipc.c new file mode 100644 index 0000000..fa52b09 --- /dev/null +++ b/progs/xdemos/ipc.c @@ -0,0 +1,264 @@ +/* Copyright (c) 2003 Tungsten Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files ("the + * Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: The above copyright notice, the Tungsten + * Graphics splash screen, and this permission notice shall be included + * in all copies or substantial portions of the Software. THE SOFTWARE + * IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Simple IPC API + * Brian Paul + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipc.h" + +#if defined(IRIX) || defined(irix) +typedef int socklen_t; +#endif + +#define NO_DELAY 1 + +#define DEFAULT_MASTER_PORT 7011 + + +/* + * Return my hostname in . + * Return 1 for success, 0 for error. + */ +int +MyHostName(char *nameOut, int maxNameLength) +{ + int k = gethostname(nameOut, maxNameLength); + return k==0; +} + + +/* + * Create a socket attached to a port. Later, we can call AcceptConnection + * on the socket returned from this function. + * Return the new socket number or -1 if error. + */ +int +CreatePort(int *port) +{ + char hostname[1000]; + struct sockaddr_in servaddr; + struct hostent *hp; + int so_reuseaddr = 1; + int tcp_nodelay = 1; + int sock, k; + + /* create socket */ + sock = socket(AF_INET, SOCK_STREAM, 0); + assert(sock > 2); + + /* get my host name */ + k = gethostname(hostname, 1000); + assert(k == 0); + + /* get hostent info */ + hp = gethostbyname(hostname); + assert(hp); + + /* initialize the servaddr struct */ + memset(&servaddr, 0, sizeof(servaddr) ); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons((unsigned short) (*port)); + memcpy((char *) &servaddr.sin_addr, hp->h_addr, + sizeof(servaddr.sin_addr)); + + /* deallocate when we exit */ + k = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *) &so_reuseaddr, sizeof(so_reuseaddr)); + assert(k==0); + + /* send packets immediately */ +#if NO_DELAY + k = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &tcp_nodelay, sizeof(tcp_nodelay)); + assert(k==0); +#endif + + if (*port == 0) + *port = DEFAULT_MASTER_PORT; + + k = 1; + while (k && (*port < 65534)) { + /* bind our address to the socket */ + servaddr.sin_port = htons((unsigned short) (*port)); + k = bind(sock, (struct sockaddr *) &servaddr, sizeof(servaddr)); + if (k) + *port = *port + 1; + } + +#if 0 + printf("###### Real Port: %d\n", *port); +#endif + + /* listen for connections */ + k = listen(sock, 100); + assert(k == 0); + + return sock; +} + + +/* + * Accept a connection on the named socket. + * Return a new socket for the new connection, or -1 if error. + */ +int +AcceptConnection(int socket) +{ + struct sockaddr addr; + socklen_t addrLen; + int newSock; + + addrLen = sizeof(addr); + newSock = accept(socket, &addr, &addrLen); + if (newSock == 1) + return -1; + else + return newSock; +} + + +/* + * Contact the server running on the given host on the named port. + * Return socket number or -1 if error. + */ +int +Connect(const char *hostname, int port) +{ + struct sockaddr_in servaddr; + struct hostent *hp; + int sock, k; + int tcp_nodelay = 1; + + assert(port); + + sock = socket(AF_INET, SOCK_STREAM, 0); + assert(sock >= 0); + + hp = gethostbyname(hostname); + assert(hp); + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons((unsigned short) port); + memcpy((char *) &servaddr.sin_addr, hp->h_addr, sizeof(servaddr.sin_addr)); + + k = connect(sock, (struct sockaddr *) &servaddr, sizeof(servaddr)); + if (k != 0) { + perror("Connect:"); + return -1; + } + +#if NO_DELAY + /* send packets immediately */ + k = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &tcp_nodelay, sizeof(tcp_nodelay)); + assert(k==0); +#endif + + return sock; +} + + +void +CloseSocket(int socket) +{ + close(socket); +} + + +int +SendData(int socket, const void *data, int bytes) +{ + int sent = 0; + int b; + + while (sent < bytes) { + b = write(socket, (char *) data + sent, bytes - sent); + if (b <= 0) + return -1; /* something broke */ + sent += b; + } + return sent; +} + + +int +ReceiveData(int socket, void *data, int bytes) +{ + int received = 0, b; + + while (received < bytes) { + b = read(socket, (char *) data + received, bytes - received); + if (b <= 0) + return -1; + received += b; + } + return received; +} + + +int +SendString(int socket, const char *str) +{ + const int len = strlen(str); + int sent, b; + + /* first, send a 4-byte length indicator */ + b = write(socket, &len, sizeof(len)); + if (b <= 0) + return -1; + + sent = SendData(socket, str, len); + assert(sent == len); + return sent; +} + + +int +ReceiveString(int socket, char *str, int maxLen) +{ + int len, received, b; + + /* first, read 4 bytes to see how long of string to receive */ + b = read(socket, &len, sizeof(len)); + if (b <= 0) + return -1; + + assert(len <= maxLen); /* XXX fix someday */ + assert(len >= 0); + received = ReceiveData(socket, str, len); + assert(received != -1); + assert(received == len); + str[len] = 0; + return received; +} diff --git a/progs/xdemos/ipc.h b/progs/xdemos/ipc.h new file mode 100644 index 0000000..3f43445 --- /dev/null +++ b/progs/xdemos/ipc.h @@ -0,0 +1,16 @@ +#ifndef IPC_H +#define IPC_H + + +extern int MyHostName(char *nameOut, int maxNameLength); +extern int CreatePort(int *port); +extern int AcceptConnection(int socket); +extern int Connect(const char *hostname, int port); +extern void CloseSocket(int socket); +extern int SendData(int socket, const void *data, int bytes); +extern int ReceiveData(int socket, void *data, int bytes); +extern int SendString(int socket, const char *str); +extern int ReceiveString(int socket, char *str, int maxLen); + + +#endif /* IPC_H */ -- 2.7.4