2 * Copyright (C) 2000 Brian Paul All Rights Reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 * Port to windows done by Michal Krol.
27 * This program tests WGL thread safety.
28 * Command line options:
30 * -l Enable application-side locking
31 * -n <num threads> Number of threads to create (default is 2)
32 * -t Use texture mapping
33 * -s Force single-threaded.
35 * Brian Paul 20 July 2000
41 * - Each thread gets its own WGL context.
43 * - The WGL contexts share texture objects.
45 * - When 't' is pressed to update the texture image, the window/thread which
46 * has input focus is signalled to change the texture. The other threads
47 * should see the updated texture the next time they call glBindTexture.
61 * Each window/thread/context:
70 int WinWidth
, WinHeight
;
72 HANDLE hEventInitialised
;
73 GLboolean Initialized
;
74 GLboolean MakeNewTexture
;
79 #define MAX_WINTHREADS 128
80 static struct winthread WinThreads
[MAX_WINTHREADS
];
81 static int NumWinThreads
= 2;
82 static HANDLE ExitEvent
= NULL
;
84 static GLboolean Locking
= 0;
85 static GLboolean Texture
= GL_FALSE
;
86 static GLboolean SingleThreaded
= GL_FALSE
;
87 static GLuint TexObj
= 12;
88 static GLboolean Animate
= GL_TRUE
;
90 static CRITICAL_SECTION Mutex
;
94 Error(const char *msg
)
96 fprintf(stderr
, "Error: %s\n", msg
);
106 for (i
= 0; i
< NumWinThreads
; i
++) {
107 SetEvent(WinThreads
[i
].hEventRedraw
);
113 MakeNewTexture(struct winthread
*wt
)
116 static float step
= 0.0f
;
117 GLfloat image
[TEX_SIZE
][TEX_SIZE
][4];
121 for (j
= 0; j
< TEX_SIZE
; j
++) {
122 for (i
= 0; i
< TEX_SIZE
; i
++) {
123 float dt
= 5.0f
* (j
- 0.5f
* TEX_SIZE
) / TEX_SIZE
;
124 float ds
= 5.0f
* (i
- 0.5f
* TEX_SIZE
) / TEX_SIZE
;
125 float r
= dt
* dt
+ ds
* ds
+ step
;
128 image
[j
][i
][2] = 0.75f
+ 0.25f
* (float) cos(r
);
129 image
[j
][i
][3] = 1.0f
;
135 glBindTexture(GL_TEXTURE_2D
, TexObj
);
137 glGetTexLevelParameteriv(GL_TEXTURE_2D
, 0, GL_TEXTURE_WIDTH
, &width
);
139 assert(width
== TEX_SIZE
);
140 /* sub-tex replace */
141 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, TEX_SIZE
, TEX_SIZE
,
142 GL_RGBA
, GL_FLOAT
, image
);
146 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
147 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
149 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, TEX_SIZE
, TEX_SIZE
, 0,
150 GL_RGBA
, GL_FLOAT
, image
);
156 /* draw a colored cube */
161 glScalef(0.75f
, 0.75f
, 0.75f
);
166 glBindTexture(GL_TEXTURE_2D
, TexObj
);
167 glEnable(GL_TEXTURE_2D
);
170 glDisable(GL_TEXTURE_2D
);
177 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
178 glTexCoord2f(1, 0); glVertex3f(-1, 1, -1);
179 glTexCoord2f(1, 1); glVertex3f(-1, 1, 1);
180 glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
184 glTexCoord2f(0, 0); glVertex3f(1, -1, -1);
185 glTexCoord2f(1, 0); glVertex3f(1, 1, -1);
186 glTexCoord2f(1, 1); glVertex3f(1, 1, 1);
187 glTexCoord2f(0, 1); glVertex3f(1, -1, 1);
191 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
192 glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
193 glTexCoord2f(1, 1); glVertex3f( 1, -1, 1);
194 glTexCoord2f(0, 1); glVertex3f(-1, -1, 1);
198 glTexCoord2f(0, 0); glVertex3f(-1, 1, -1);
199 glTexCoord2f(1, 0); glVertex3f( 1, 1, -1);
200 glTexCoord2f(1, 1); glVertex3f( 1, 1, 1);
201 glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
205 glTexCoord2f(0, 0); glVertex3f(-1, -1, -1);
206 glTexCoord2f(1, 0); glVertex3f( 1, -1, -1);
207 glTexCoord2f(1, 1); glVertex3f( 1, 1, -1);
208 glTexCoord2f(0, 1); glVertex3f(-1, 1, -1);
212 glTexCoord2f(0, 0); glVertex3f(-1, -1, 1);
213 glTexCoord2f(1, 0); glVertex3f( 1, -1, 1);
214 glTexCoord2f(1, 1); glVertex3f( 1, 1, 1);
215 glTexCoord2f(0, 1); glVertex3f(-1, 1, 1);
223 /* signal resize of given window */
225 resize(struct winthread
*wt
, int w
, int h
)
227 wt
->NewSize
= GL_TRUE
;
231 SetEvent(wt
->hEventRedraw
);
236 * We have an instance of this for each thread.
239 draw_loop(struct winthread
*wt
)
242 GLboolean draw
= Animate
;
246 /* wait 5 ms for signal either to exit or process messages */
247 switch (MsgWaitForMultipleObjects(1, &ExitEvent
, FALSE
, 5, QS_ALLINPUT
)) {
249 SendMessage(wt
->Win
, WM_CLOSE
, 0, 0);
251 case WAIT_OBJECT_0
+ 1:
258 events
[0] = wt
->hEventRedraw
;
259 events
[1] = ExitEvent
;
261 /* wait for signal either to draw, exit or process messages */
262 switch (MsgWaitForMultipleObjects(2, events
, FALSE
, INFINITE
, QS_ALLINPUT
)) {
266 case WAIT_OBJECT_0
+ 1:
267 SendMessage(wt
->Win
, WM_CLOSE
, 0, 0);
269 case WAIT_OBJECT_0
+ 2:
274 while (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
)) {
275 if (msg
.message
== WM_QUIT
) {
278 TranslateMessage(&msg
);
279 DispatchMessage(&msg
);
286 EnterCriticalSection(&Mutex
);
288 wglMakeCurrent(wt
->hDC
, wt
->Context
);
290 if (!wt
->Initialized
) {
291 printf("wglthreads: %d: GL_RENDERER = %s\n", wt
->Index
,
292 (char *) glGetString(GL_RENDERER
));
293 if (Texture
/*&& wt->Index == 0*/) {
296 wt
->Initialized
= GL_TRUE
;
300 LeaveCriticalSection(&Mutex
);
302 glEnable(GL_DEPTH_TEST
);
305 GLfloat w
= (float) wt
->WinWidth
/ (float) wt
->WinHeight
;
306 glViewport(0, 0, wt
->WinWidth
, wt
->WinHeight
);
307 glMatrixMode(GL_PROJECTION
);
309 glFrustum(-w
, w
, -1.0, 1.0, 1.5, 10);
310 glMatrixMode(GL_MODELVIEW
);
312 glTranslatef(0, 0, -2.5);
313 wt
->NewSize
= GL_FALSE
;
316 if (wt
->MakeNewTexture
) {
318 wt
->MakeNewTexture
= GL_FALSE
;
321 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
324 glRotatef(wt
->Angle
, 0, 1, 0);
325 glRotatef(wt
->Angle
, 1, 0, 0);
326 glScalef(0.7f
, 0.7f
, 0.7f
);
331 EnterCriticalSection(&Mutex
);
333 SwapBuffers(wt
->hDC
);
336 LeaveCriticalSection(&Mutex
);
344 keypress(WPARAM keySym
, struct winthread
*wt
)
348 /* tell all threads to exit */
350 /*printf("exit draw_loop %d\n", wt->Index);*/
355 wt
->MakeNewTexture
= GL_TRUE
;
377 static LRESULT CALLBACK
383 struct winthread
*wt
= (struct winthread
*)(INT_PTR
)GetWindowLongPtr(hWnd
, GWLP_USERDATA
);
387 keypress(wParam
, wt
);
392 GetClientRect(hWnd
, &r
);
393 resize(wt
, r
.right
, r
.bottom
);
400 return DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
407 * we'll call this once for each thread, before the threads are created.
410 create_window(struct winthread
*wt
, HGLRC shareCtx
)
413 int width
= 160, height
= 160;
414 int xpos
= (wt
->Index
% 8) * (width
+ 10);
415 int ypos
= (wt
->Index
/ 8) * (width
+ 20);
418 PIXELFORMATDESCRIPTOR pfd
;
422 wc
.hbrBackground
= (HBRUSH
) (COLOR_BTNFACE
+ 1);
423 wc
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
424 wc
.hIcon
= LoadIcon(NULL
, IDI_APPLICATION
);
425 wc
.lpfnWndProc
= WndProc
;
426 wc
.lpszClassName
= "wglthreads";
427 wc
.style
= CS_OWNDC
| CS_HREDRAW
| CS_VREDRAW
;
430 win
= CreateWindowEx(0,
433 WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
| WS_TILEDWINDOW
,
443 Error("Couldn't create window");
446 SetWindowLongPtr(win
, GWLP_USERDATA
, (LONG_PTR
)wt
);
450 Error("Couldn't obtain HDC");
453 memset(&pfd
, 0, sizeof(pfd
));
456 pfd
.dwFlags
= PFD_DOUBLEBUFFER
| PFD_DRAW_TO_WINDOW
| PFD_SUPPORT_OPENGL
;
457 pfd
.iLayerType
= PFD_MAIN_PLANE
;
458 pfd
.iPixelType
= PFD_TYPE_RGBA
;
459 pfd
.nSize
= sizeof(pfd
);
462 visinfo
= ChoosePixelFormat(hdc
, &pfd
);
464 Error("Unable to find RGB, Z, double-buffered visual");
467 SetPixelFormat(hdc
, visinfo
, &pfd
);
468 ctx
= wglCreateContext(hdc
);
470 Error("Couldn't create WGL context");
474 if(!wglShareLists(shareCtx
, ctx
))
475 Error("Couldn't share WGL context lists");
478 /* save the info for this window/context */
483 wt
->WinWidth
= width
;
484 wt
->WinHeight
= height
;
485 wt
->NewSize
= GL_TRUE
;
490 * Called by pthread_create()
495 struct winthread
*wt
= (struct winthread
*) p
;
498 /* Wait for the previous thread */
499 if(Texture
&& wt
->Index
> 0) {
500 WaitForSingleObject(WinThreads
[wt
->Index
- 1].hEventInitialised
, INFINITE
);
501 share
= WinThreads
[0].Context
;
506 share
= (Texture
&& wt
->Index
> 0) ? WinThreads
[0].Context
: 0;
507 create_window(wt
, share
);
508 SetEvent(wt
->hEventInitialised
);
510 /* Wait for all threads to initialize otherwise wglShareLists will fail */
511 if(wt
->Index
< NumWinThreads
- 1)
512 WaitForSingleObject(WinThreads
[NumWinThreads
- 1].hEventInitialised
, INFINITE
);
522 printf("wglthreads: test of GL thread safety (any key = exit)\n");
524 printf(" wglthreads [options]\n");
525 printf("Options:\n");
526 printf(" -h Show this usage screen\n");
527 printf(" -n NUMTHREADS Number of threads to create\n");
528 printf(" -l Use application-side locking\n");
529 printf(" -t Enable texturing\n");
530 printf(" -s Force single-threaded\n");
531 printf("Keyboard:\n");
532 printf(" Esc Exit\n");
533 printf(" t Change texture image (requires -t option)\n");
534 printf(" a Toggle animation\n");
535 printf(" s Step rotation (when not animating)\n");
540 main(int argc
, char *argv
[])
544 for (i
= 1; i
< argc
; i
++) {
545 if (strcmp(argv
[i
], "-h") == 0) {
549 else if (strcmp(argv
[i
], "-l") == 0) {
552 else if (strcmp(argv
[i
], "-t") == 0) {
555 else if (strcmp(argv
[i
], "-n") == 0 && i
+ 1 < argc
) {
556 NumWinThreads
= atoi(argv
[i
+ 1]);
557 if (NumWinThreads
< 1)
559 else if (NumWinThreads
> MAX_WINTHREADS
)
560 NumWinThreads
= MAX_WINTHREADS
;
563 else if (strcmp(argv
[i
], "-s") == 0) {
564 SingleThreaded
= GL_TRUE
;
573 printf("wglthreads: Forcing single-threaded, no other threads will be created.\n");
576 printf("wglthreads: Using explicit locks around WGL calls.\n");
578 printf("wglthreads: No explict locking.\n");
580 InitializeCriticalSection(&Mutex
);
581 ExitEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
583 if (SingleThreaded
) {
586 WinThreads
[0].Index
= 0;
587 WinThreads
[0].hEventInitialised
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
588 WinThreads
[0].hEventRedraw
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
590 ThreadProc((void*) &WinThreads
[0]);
593 HANDLE threads
[MAX_WINTHREADS
];
595 printf("wglthreads: creating threads\n");
597 /* Create the events */
598 for (i
= 0; i
< NumWinThreads
; i
++) {
599 WinThreads
[i
].Index
= i
;
600 WinThreads
[i
].hEventInitialised
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
601 WinThreads
[i
].hEventRedraw
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
604 /* Create the threads */
605 for (i
= 0; i
< NumWinThreads
; i
++) {
608 WinThreads
[i
].Thread
= CreateThread(NULL
,
611 (void*) &WinThreads
[i
],
614 printf("wglthreads: Created thread %p\n", (void *) WinThreads
[i
].Thread
);
616 threads
[i
] = WinThreads
[i
].Thread
;
619 /* Wait for all threads to finish. */
620 WaitForMultipleObjects(NumWinThreads
, threads
, TRUE
, INFINITE
);