/** * 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" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif 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; }