diff options
Diffstat (limited to 'progs/wgl')
-rw-r--r-- | progs/wgl/SConscript | 23 | ||||
-rw-r--r-- | progs/wgl/sharedtex_mt/sharedtex_mt.c | 540 | ||||
-rw-r--r-- | progs/wgl/wglthreads/wglthreads.c | 614 |
3 files changed, 1177 insertions, 0 deletions
diff --git a/progs/wgl/SConscript b/progs/wgl/SConscript new file mode 100644 index 0000000000..b7cf35b7fc --- /dev/null +++ b/progs/wgl/SConscript @@ -0,0 +1,23 @@ +Import('*') + +if env['platform'] != 'windows': + Return() + +env = env.Clone() + +env.Append(LIBS = [ + 'kernel32', + 'user32', + 'gdi32', +]) + +progs = [ + 'sharedtex_mt', + 'wglthreads', +] + +for prog in progs: + env.Program( + target = prog, + source = prog + '/' + prog + '.c', + ) diff --git a/progs/wgl/sharedtex_mt/sharedtex_mt.c b/progs/wgl/sharedtex_mt/sharedtex_mt.c new file mode 100644 index 0000000000..137c9c10af --- /dev/null +++ b/progs/wgl/sharedtex_mt/sharedtex_mt.c @@ -0,0 +1,540 @@ +/* + * Test sharing of display lists and texture objects between GLX contests. + * Brian Paul + * Summer 2000 + * + * + * Copyright (C) 2000 Brian Paul All Rights Reserved. + * + * 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 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 + * BRIAN PAUL 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. + * + * + * Modified 2009 for multithreading by Thomas Hellstrom. + * + * Port to windows by Michal Krol. + */ + + +#include <windows.h> +#include <GL/gl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#pragma comment(lib, "opengl32.lib") + +struct thread_init_arg { + int id; +}; + +struct window { + CRITICAL_SECTION drawMutex; + HDC hDC; + HWND Win; + HGLRC Context; + float Angle; + int Id; +}; + + +#define MAX_WINDOWS 20 +static struct window Windows[MAX_WINDOWS]; +static int NumWindows = 0; +static HANDLE terminate = NULL; +static HGLRC gCtx = NULL; +static HDC gHDC = NULL; +static GLuint Textures[3]; + + + +static void +Error(const char *msg) +{ + fprintf(stderr, "Error - %s\n", msg); + exit(1); +} + +static void +Resize(struct window *h, unsigned int width, unsigned int height); + +static LRESULT CALLBACK +WndProc(HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam ) +{ + switch (uMsg) { + case WM_KEYDOWN: + SetEvent(terminate); + break; + case WM_SIZE: + { + LONG index = GetWindowLong(hWnd, GWL_USERDATA); + + if (index >= 0 && index < MAX_WINDOWS) { + RECT r; + + GetClientRect(hWnd, &r); + Resize(&Windows[index], r.right, r.bottom); + } + } + break; + case WM_CREATE: + { + CREATESTRUCT *pcs = (CREATESTRUCT *) lParam; + + SetWindowLong(hWnd, GWL_USERDATA, (LONG) pcs->lpCreateParams); + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + return 0; +} + +static int +initMainthread(void) +{ + WNDCLASS wc = {0}; + HWND win; + PIXELFORMATDESCRIPTOR pfd = {0}; + int visinfo; + + wc.lpfnWndProc = WndProc; + wc.lpszClassName = "sharedtex_mt.hidden"; + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + win = CreateWindowEx(0, + wc.lpszClassName, + "sharedtex_mt.hidden", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + wc.hInstance, + (LPVOID) -1); + if (!win) { + Error("Couldn't create window"); + } + + gHDC = GetDC(win); + if (!gHDC) { + Error("Couldn't obtain HDC"); + } + + pfd.cColorBits = 24; + pfd.cDepthBits = 24; + pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + + visinfo = ChoosePixelFormat(gHDC, &pfd); + if (!visinfo) { + Error("Unable to find RGB, Z, double-buffered visual"); + } + + SetPixelFormat(gHDC, visinfo, &pfd); + gCtx = wglCreateContext(gHDC); + if (!gCtx) { + Error("Couldn't create WGL context"); + } + + return 0; +} + +static struct window * +AddWindow(int xpos, int ypos, HGLRC sCtx) +{ + struct window *win = &Windows[NumWindows]; + WNDCLASS wc = {0}; + PIXELFORMATDESCRIPTOR pfd = {0}; + int visinfo; + int width = 300, height = 300; + + if (NumWindows >= MAX_WINDOWS) + return NULL; + + memset(win, 0, sizeof(*win)); + InitializeCriticalSection(&win->drawMutex); + win->Angle = 0.0; + win->Id = NumWindows++; + + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.lpfnWndProc = WndProc; + wc.lpszClassName = "sharedtex_mt"; + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + win->Win = CreateWindowEx(0, + wc.lpszClassName, + "sharedtex_mt", + WS_SIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + xpos, + ypos, + width, + height, + NULL, + NULL, + wc.hInstance, + (LPVOID) win->Id); + if (!win->Win) { + Error("Couldn't create window"); + } + + win->hDC = GetDC(win->Win); + if (!win->hDC) { + Error("Couldn't obtain HDC"); + } + + pfd.cColorBits = 24; + pfd.cDepthBits = 24; + pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + + visinfo = ChoosePixelFormat(win->hDC, &pfd); + if (!visinfo) { + Error("Unable to find RGB, Z, double-buffered visual"); + } + + SetPixelFormat(win->hDC, visinfo, &pfd); + win->Context = wglCreateContext(win->hDC); + if (!win->Context) { + Error("Couldn't create WGL context"); + } + + if (sCtx) { + wglShareLists(sCtx, win->Context); + } + + ShowWindow(win->Win, SW_SHOW); + + return win; +} + + +static void +InitGLstuff(void) + +{ + glGenTextures(3, Textures); + + /* setup first texture object */ + { + GLubyte image[16][16][4]; + GLint i, j; + glBindTexture(GL_TEXTURE_2D, Textures[0]); + + /* red/white checkerboard */ + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + if ((i ^ j) & 1) { + image[i][j][0] = 255; + image[i][j][1] = 255; + image[i][j][2] = 255; + image[i][j][3] = 255; + } + else { + image[i][j][0] = 255; + image[i][j][1] = 0; + image[i][j][2] = 0; + image[i][j][3] = 255; + } + } + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, + GL_UNSIGNED_BYTE, image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + /* setup second texture object */ + { + GLubyte image[8][8][3]; + GLint i, j; + glBindTexture(GL_TEXTURE_2D, Textures[1]); + + /* green/yellow checkerboard */ + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + if ((i ^ j) & 1) { + image[i][j][0] = 0; + image[i][j][1] = 255; + image[i][j][2] = 0; + } + else { + image[i][j][0] = 255; + image[i][j][1] = 255; + image[i][j][2] = 0; + } + } + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, + GL_UNSIGNED_BYTE, image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + /* setup second texture object */ + { + GLubyte image[4][4][3]; + GLint i, j; + glBindTexture(GL_TEXTURE_2D, Textures[2]); + + /* blue/gray checkerboard */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + if ((i ^ j) & 1) { + image[i][j][0] = 0; + image[i][j][1] = 0; + image[i][j][2] = 255; + } + else { + image[i][j][0] = 200; + image[i][j][1] = 200; + image[i][j][2] = 200; + } + } + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 4, 4, 0, GL_RGB, + GL_UNSIGNED_BYTE, image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + /* Now make the cube object display list */ + + printf("GL_RENDERER: %s\n", (char *) glGetString(GL_RENDERER)); + printf("GL_VERSION: %s\n", (char *) glGetString(GL_VERSION)); + printf("GL_VENDOR: %s\n", (char *) glGetString(GL_VENDOR)); +} + +static void +Redraw(struct window *h) +{ + EnterCriticalSection(&h->drawMutex); + if (!wglMakeCurrent(h->hDC, h->Context)) { + LeaveCriticalSection(&h->drawMutex); + Error("wglMakeCurrent failed in Redraw"); + return; + } + + h->Angle += 1.0; + + glShadeModel(GL_FLAT); + glClearColor(0.25, 0.25, 0.25, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor3f(1, 1, 1); + + glPushMatrix(); + if (h->Id == 0) + glRotatef(h->Angle, 0, 1, -1); + else if (h->Id == 1) + glRotatef(-(h->Angle), 0, 1, -1); + else if (h->Id == 2) + glRotatef(h->Angle, 0, 1, 1); + else if (h->Id == 3) + glRotatef(-(h->Angle), 0, 1, 1); + glBindTexture(GL_TEXTURE_2D, Textures[0]); + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f(-1, 1, -1); + glTexCoord2f(1, 1); glVertex3f(-1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); + glEnd(); + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex3f(1, -1, -1); + glTexCoord2f(1, 0); glVertex3f(1, 1, -1); + glTexCoord2f(1, 1); glVertex3f(1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(1, -1, 1); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, Textures[1]); + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, -1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); + glEnd(); + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex3f(-1, 1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, 1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, Textures[2]); + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, -1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); + glEnd(); + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); + glEnd(); + + glPopMatrix(); + + SwapBuffers(h->hDC); + + if (!wglMakeCurrent(NULL, NULL)) { + Error("wglMakeCurrent failed in Redraw"); + } + LeaveCriticalSection(&h->drawMutex); +} + +static DWORD WINAPI +threadRunner (void *arg) +{ + struct thread_init_arg *tia = (struct thread_init_arg *) arg; + struct window *win; + + win = &Windows[tia->id]; + + while (1) { + MSG msg; + + /* wait 1 ms for signal either to exit or process messages */ + switch (MsgWaitForMultipleObjects(1, &terminate, FALSE, 1, QS_ALLINPUT)) { + case WAIT_OBJECT_0: + SendMessage(win->Win, WM_CLOSE, 0, 0); + break; + case WAIT_OBJECT_0 + 1: + break; + } + + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + return 0; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + Redraw(win); + } + + return 0; +} + +static void +Resize(struct window *h, unsigned int width, unsigned int height) +{ + EnterCriticalSection(&h->drawMutex); + + if (!wglMakeCurrent(h->hDC, h->Context)) { + LeaveCriticalSection(&h->drawMutex); + Error("wglMakeCurrent failed in Resize()"); + return; + } + + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-1, 1, -1, 1, 2, 10); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -4.5); + if (!wglMakeCurrent(NULL, NULL)) { + Error("wglMakeCurrent failed in Resize()"); + } + LeaveCriticalSection(&h->drawMutex); +} + +int +main(int argc, char *argv[]) +{ + struct thread_init_arg tia[MAX_WINDOWS]; + struct window *h[MAX_WINDOWS]; + HANDLE threads[MAX_WINDOWS]; + int i; + + terminate = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (initMainthread()) + return -1; + + /* four windows and contexts sharing display lists and texture objects */ + h[0] = AddWindow( 10, 10, gCtx); + h[1] = AddWindow(330, 10, gCtx); + h[2] = AddWindow( 10, 350, gCtx); + h[3] = AddWindow(330, 350, gCtx); + + if (!wglMakeCurrent(gHDC, gCtx)) { + Error("wglMakeCurrent failed for init thread."); + return -1; + } + + InitGLstuff(); + + for (i = 0; i < NumWindows; i++) { + DWORD id; + + tia[i].id = i; + threads[i] = CreateThread(NULL, 0, threadRunner, &tia[i], 0, &id); + } + + while (1) { + MSG msg; + + /* wait 1 ms for signal either to exit or process messages */ + switch (MsgWaitForMultipleObjects(NumWindows, threads, TRUE, 1, QS_ALLINPUT)) { + case WAIT_OBJECT_0: + return 0; + } + + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + return 0; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return 0; +} diff --git a/progs/wgl/wglthreads/wglthreads.c b/progs/wgl/wglthreads/wglthreads.c new file mode 100644 index 0000000000..32f3e45edf --- /dev/null +++ b/progs/wgl/wglthreads/wglthreads.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2000 Brian Paul All Rights Reserved. + * + * 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 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 + * BRIAN PAUL 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. + * + * + * Port to windows done by Michal Krol. + */ + + +/* + * This program tests WGL thread safety. + * Command line options: + * -h Print usage + * -l Enable application-side locking + * -n <num threads> Number of threads to create (default is 2) + * -t Use texture mapping + * -s Force single-threaded. + * + * Brian Paul 20 July 2000 + */ + + +/* + * Notes: + * - Each thread gets its own WGL context. + * + * - The WGL contexts share texture objects. + * + * - When 't' is pressed to update the texture image, the window/thread which + * has input focus is signalled to change the texture. The other threads + * should see the updated texture the next time they call glBindTexture. + */ + + +#include <assert.h> +#include <windows.h> +#include <GL/gl.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#pragma comment(lib, "opengl32.lib") + + +/* + * Each window/thread/context: + */ +struct winthread { + int Index; + HANDLE Thread; + HWND Win; + HDC hDC; + HGLRC Context; + float Angle; + int WinWidth, WinHeight; + GLboolean NewSize; + HANDLE hEventInitialised; + GLboolean MakeNewTexture; + HANDLE hEventRedraw; +}; + + +#define MAX_WINTHREADS 100 +static struct winthread WinThreads[MAX_WINTHREADS]; +static int NumWinThreads = 2; +static HANDLE ExitEvent = NULL; + +static GLboolean Locking = 0; +static GLboolean Texture = GL_FALSE; +static GLboolean SingleThreaded = GL_FALSE; +static GLuint TexObj = 12; +static GLboolean Animate = GL_TRUE; + +static CRITICAL_SECTION Mutex; + + +static void +Error(const char *msg) +{ + fprintf(stderr, "Error: %s\n", msg); + exit(1); +} + + +static void +signal_redraw(void) +{ + int i; + + for (i = 0; i < NumWinThreads; i++) { + SetEvent(WinThreads[i].hEventRedraw); + } +} + + +static void +MakeNewTexture(struct winthread *wt) +{ +#define TEX_SIZE 128 + static float step = 0.0; + GLfloat image[TEX_SIZE][TEX_SIZE][4]; + GLint width; + int i, j; + + for (j = 0; j < TEX_SIZE; j++) { + for (i = 0; i < TEX_SIZE; i++) { + float dt = 5.0 * (j - 0.5 * TEX_SIZE) / TEX_SIZE; + float ds = 5.0 * (i - 0.5 * TEX_SIZE) / TEX_SIZE; + float r = dt * dt + ds * ds + step; + image[j][i][0] = + image[j][i][1] = + image[j][i][2] = 0.75 + 0.25 * cos(r); + image[j][i][3] = 1.0; + } + } + + step += 0.5; + + glBindTexture(GL_TEXTURE_2D, TexObj); + + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + if (width) { + assert(width == TEX_SIZE); + /* sub-tex replace */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEX_SIZE, TEX_SIZE, + GL_RGBA, GL_FLOAT, image); + } + else { + /* create new */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0, + GL_RGBA, GL_FLOAT, image); + } +} + + + +/* draw a colored cube */ +static void +draw_object(void) +{ + glPushMatrix(); + glScalef(0.75, 0.75, 0.75); + + glColor3f(1, 0, 0); + + if (Texture) { + glBindTexture(GL_TEXTURE_2D, TexObj); + glEnable(GL_TEXTURE_2D); + } + else { + glDisable(GL_TEXTURE_2D); + } + + glBegin(GL_QUADS); + + /* -X */ + glColor3f(0, 1, 1); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f(-1, 1, -1); + glTexCoord2f(1, 1); glVertex3f(-1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); + + /* +X */ + glColor3f(1, 0, 0); + glTexCoord2f(0, 0); glVertex3f(1, -1, -1); + glTexCoord2f(1, 0); glVertex3f(1, 1, -1); + glTexCoord2f(1, 1); glVertex3f(1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(1, -1, 1); + + /* -Y */ + glColor3f(1, 0, 1); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, -1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, -1, 1); + + /* +Y */ + glColor3f(0, 1, 0); + glTexCoord2f(0, 0); glVertex3f(-1, 1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, 1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); + + /* -Z */ + glColor3f(1, 1, 0); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, -1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, -1); + + /* +Y */ + glColor3f(0, 0, 1); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 1); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 1); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 1); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 1); + + glEnd(); + + glPopMatrix(); +} + + +/* signal resize of given window */ +static void +resize(struct winthread *wt, int w, int h) +{ + wt->NewSize = GL_TRUE; + wt->WinWidth = w; + wt->WinHeight = h; + if (!Animate) + SetEvent(wt->hEventRedraw); +} + + +/* + * We have an instance of this for each thread. + */ +static void +draw_loop(struct winthread *wt) +{ + while (1) { + GLboolean draw = Animate; + MSG msg; + + if (Animate) { + /* wait 5 ms for signal either to exit or process messages */ + switch (MsgWaitForMultipleObjects(1, &ExitEvent, FALSE, 5, QS_ALLINPUT)) { + case WAIT_OBJECT_0: + SendMessage(wt->Win, WM_CLOSE, 0, 0); + break; + case WAIT_OBJECT_0 + 1: + break; + } + } + else { + HANDLE events[2]; + + events[0] = wt->hEventRedraw; + events[1] = ExitEvent; + + /* wait for signal either to draw, exit or process messages */ + switch (MsgWaitForMultipleObjects(2, events, FALSE, INFINITE, QS_ALLINPUT)) { + case WAIT_OBJECT_0: + draw = GL_TRUE; + break; + case WAIT_OBJECT_0 + 1: + SendMessage(wt->Win, WM_CLOSE, 0, 0); + break; + case WAIT_OBJECT_0 + 2: + break; + } + } + + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (!draw) + continue; + + if (Locking) + EnterCriticalSection(&Mutex); + + wglMakeCurrent(wt->hDC, wt->Context); + + if (Locking) + LeaveCriticalSection(&Mutex); + + glEnable(GL_DEPTH_TEST); + + if (wt->NewSize) { + GLfloat w = (float) wt->WinWidth / (float) wt->WinHeight; + glViewport(0, 0, wt->WinWidth, wt->WinHeight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-w, w, -1.0, 1.0, 1.5, 10); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2.5); + wt->NewSize = GL_FALSE; + } + + if (wt->MakeNewTexture) { + MakeNewTexture(wt); + wt->MakeNewTexture = GL_FALSE; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + glRotatef(wt->Angle, 0, 1, 0); + glRotatef(wt->Angle, 1, 0, 0); + glScalef(0.7, 0.7, 0.7); + draw_object(); + glPopMatrix(); + + if (Locking) + EnterCriticalSection(&Mutex); + SwapBuffers(wt->hDC); + if (Locking) + LeaveCriticalSection(&Mutex); + + wt->Angle += 1.0; + } +} + + +static void +keypress(WPARAM keySym, struct winthread *wt) +{ + switch (keySym) { + case VK_ESCAPE: + /* tell all threads to exit */ + SetEvent(ExitEvent); + /*printf("exit draw_loop %d\n", wt->Index);*/ + return; + case 't': + case 'T': + if (Texture) { + wt->MakeNewTexture = GL_TRUE; + if (!Animate) + signal_redraw(); + } + break; + case 'a': + case 'A': + Animate = !Animate; + if (Animate) + signal_redraw(); + break; + case 's': + case 'S': + if (!Animate) + signal_redraw(); + break; + default: + ; /* nop */ + } +} + + +static LRESULT CALLBACK +WndProc(HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam ) +{ + int i; + + switch (uMsg) { + case WM_KEYDOWN: + for (i = 0; i < NumWinThreads; i++) { + struct winthread *wt = &WinThreads[i]; + + if (hWnd == wt->Win) { + keypress(wParam, wt); + break; + } + } + break; + case WM_SIZE: + for (i = 0; i < NumWinThreads; i++) { + struct winthread *wt = &WinThreads[i]; + + if (hWnd == wt->Win) { + RECT r; + + GetClientRect(hWnd, &r); + resize(wt, r.right, r.bottom); + break; + } + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + return 0; +} + +/* + * we'll call this once for each thread, before the threads are created. + */ +static void +create_window(struct winthread *wt, HGLRC shareCtx) +{ + WNDCLASS wc = {0}; + int width = 160, height = 160; + int xpos = (wt->Index % 8) * (width + 10); + int ypos = (wt->Index / 8) * (width + 20); + HWND win; + HDC hdc; + PIXELFORMATDESCRIPTOR pfd = {0}; + int visinfo; + HGLRC ctx; + + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.lpfnWndProc = WndProc; + wc.lpszClassName = "wglthreads"; + wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + win = CreateWindowEx(0, + wc.lpszClassName, + "wglthreads", + WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + xpos, + ypos, + width, + height, + NULL, + NULL, + wc.hInstance, + (LPVOID) wt); + if (!win) { + Error("Couldn't create window"); + } + + hdc = GetDC(win); + if (!hdc) { + Error("Couldn't obtain HDC"); + } + + pfd.cColorBits = 24; + pfd.cDepthBits = 24; + pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + + visinfo = ChoosePixelFormat(hdc, &pfd); + if (!visinfo) { + Error("Unable to find RGB, Z, double-buffered visual"); + } + + SetPixelFormat(hdc, visinfo, &pfd); + ctx = wglCreateContext(hdc); + if (!ctx) { + Error("Couldn't create WGL context"); + } + + if (shareCtx) { + wglShareLists(shareCtx, ctx); + } + + /* save the info for this window/context */ + wt->Win = win; + wt->hDC = hdc; + wt->Context = ctx; + wt->Angle = 0.0; + wt->WinWidth = width; + wt->WinHeight = height; + wt->NewSize = GL_TRUE; + + printf("wglthreads: %d: GL_RENDERER = %s\n", wt->Index, (char *) glGetString(GL_RENDERER)); + + if (Texture/* && wt->Index == 0*/) { + MakeNewTexture(wt); + } +} + + +/* + * Called by pthread_create() + */ +static DWORD WINAPI +ThreadProc(void *p) +{ + struct winthread *wt = (struct winthread *) p; + HGLRC share; + + share = (Texture && wt->Index > 0) ? WinThreads[0].Context : 0; + create_window(wt, share); + SetEvent(wt->hEventInitialised); + + draw_loop(wt); + return 0; +} + + +static void +usage(void) +{ + printf("wglthreads: test of GL thread safety (any key = exit)\n"); + printf("Usage:\n"); + printf(" wglthreads [options]\n"); + printf("Options:\n"); + printf(" -h Show this usage screen\n"); + printf(" -n NUMTHREADS Number of threads to create\n"); + printf(" -l Use application-side locking\n"); + printf(" -t Enable texturing\n"); + printf(" -s Force single-threaded\n"); + printf("Keyboard:\n"); + printf(" Esc Exit\n"); + printf(" t Change texture image (requires -t option)\n"); + printf(" a Toggle animation\n"); + printf(" s Step rotation (when not animating)\n"); +} + + +int +main(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + usage(); + } + else if (strcmp(argv[i], "-l") == 0) { + Locking = 1; + } + else if (strcmp(argv[i], "-t") == 0) { + Texture = 1; + } + else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) { + NumWinThreads = atoi(argv[i + 1]); + if (NumWinThreads < 1) + NumWinThreads = 1; + else if (NumWinThreads > MAX_WINTHREADS) + NumWinThreads = MAX_WINTHREADS; + i++; + } + else if (strcmp(argv[i], "-s") == 0) { + SingleThreaded = GL_TRUE; + } + else { + usage(); + exit(1); + } + } + + if (SingleThreaded) + printf("wglthreads: Forcing single-threaded, no other threads will be created.\n"); + + if (Locking) + printf("wglthreads: Using explicit locks around WGL calls.\n"); + else + printf("wglthreads: No explict locking.\n"); + + InitializeCriticalSection(&Mutex); + ExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (SingleThreaded) { + NumWinThreads = 1; + + WinThreads[0].Index = 0; + WinThreads[0].hEventInitialised = CreateEvent(NULL, TRUE, FALSE, NULL); + WinThreads[0].hEventRedraw = CreateEvent(NULL, FALSE, FALSE, NULL); + + ThreadProc((void*) &WinThreads[0]); + } + else { + HANDLE threads[MAX_WINTHREADS]; + + printf("wglthreads: creating threads\n"); + + /* Create the threads */ + for (i = 0; i < NumWinThreads; i++) { + DWORD id; + + WinThreads[i].Index = i; + WinThreads[i].hEventInitialised = CreateEvent(NULL, TRUE, FALSE, NULL); + WinThreads[i].hEventRedraw = CreateEvent(NULL, FALSE, FALSE, NULL); + WinThreads[i].Thread = CreateThread(NULL, + 0, + ThreadProc, + (void*) &WinThreads[i], + 0, + &id); + printf("wglthreads: Created thread %p\n", (void *) WinThreads[i].Thread); + + WaitForSingleObject(WinThreads[i].hEventInitialised, INFINITE); + + threads[i] = WinThreads[i].Thread; + } + + /* Wait for all threads to finish. */ + WaitForMultipleObjects(NumWinThreads, threads, TRUE, INFINITE); + } + + return 0; +} |