Apologies for this being a long post but I wanted to include some code to make it clear what I am trying.
I wish to have my OpenGL rendering in a seperate thread to my main app. Now I know that a single OpenGL context can only be accessed by a single thread but that isn’t my problem. The code below creates a new child thread which in turn creates a window, sets up the context and repeatedly clears the background. It then deletes the context and the window ready for the next time the thread is started. This code works repeatedly on all OpenGL hardware I’ve tried except nVIDIA GeForce with Detonator3 drivers. With that particular setup the app is fine the first time the thread is created but crashes the second time I try and create the thread. The call stack shows that there is a null function pointer in the nVIDIA ICD. This happens with NT4(SP6a) and Win2K.
Has anyone else seen this with the Detonator3 drivers or can you see a problem with my code? I can fix the crash by installing Hercules drivers for the same GeForce card but that doesn’t help my customers.
Many thanks
Rob
Code snippet from basic MFC AppWizard app follows (note I’ve simplified the code by removing checks on return values - I have checked all function returns and they are valid)…
volatile BOOL thread_running = FALSE;
volatile BOOL stop_thread = TRUE;
static const char szViewClassName [] = {“ThreadTestWindowClass”};
static const char szViewWindowName [] = {“ThreadTest”};
BOOL RegisterWindowClass (void);
HWND CreateViewWindow ( int width, int height, BOOL bShow);
UINT RenderThread(LPVOID param);
// Two message handlers to control creation and deletion of the thread
void CMainFrame::OnGo()
{
// Don’t start if the thread is running
if (!thread_running)
{
stop_thread = FALSE;
// The thread is not running so start it
AfxBeginThread(RenderThread, NULL);
// Wait until the thread has actually started
while (!thread_running)
{
_sleep(0);
}
}
}
void CMainFrame::OnStop()
{
// Don’t stop unless the thread is running
if (thread_running)
{
// Flag the thread to stop
stop_thread = TRUE;
// Wait for the thread to stop
while (thread_running)
{
_sleep(0);
}
}
}
// This thread is controlled by the parent process - created by AfxBeginThread
UINT RenderThread(LPVOID param)
{
GLclampf red = 0.0F, green = 0.0F, blue = 0.0F;
HWND hWnd = NULL;
HDC hdc = NULL;
HGLRC hrc = NULL;
// Flag thread as running
thread_running = TRUE;
// Register our window class
RegisterWindowClass ();
// Create the window
hWnd = CreateViewWindow (200, 200, TRUE);
// Get device context
hdc = GetDC(hWnd);
// Define required pixel format
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int iPixelFormat;
// get the best available match of pixel format for the device context
iPixelFormat = ChoosePixelFormat(hdc, &pfd);
// make that the pixel format of the device context
SetPixelFormat(hdc, iPixelFormat, &pfd);
// Create the OpenGL context
hrc = wglCreateContext(hdc);
// Make OpenGL context current
wglMakeCurrent(hdc, hrc);
// while the thread is active do something simple
while ( !stop_thread )
{
// Clear background and swap - simple OpenGL rendering
glClearColor(red, green, blue, 0.0F);
glClear(GL_COLOR_BUFFER_BIT);
red += 0.005F;
green += 0.0005F;
blue += 0.000005F;
if (red > 1.0F) red = 0.0F;
if (green > 1.0F) green = 0.0F;
if (blue > 1.0F) blue = 0.0F;
glFlush();
SwapBuffers(hdc);
}
glFinish();
// Delete the OpenGL context
wglDeleteContext(hrc);
// Release the device context
ReleaseDC(hWnd, hdc);
// Destroy the window
DestroyWindow(hWnd);
thread_running = FALSE;
return 0;
}
// Register a window class for channels which require one
BOOL RegisterWindowClass (void)
{
WNDCLASS wc;
/* === Register a class for the top level window === */
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = NULL;
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = szViewClassName;
return (RegisterClass(&wc) == 0);
}
// Create and show a window for OpenGL rendering
HWND CreateViewWindow ( int width, int height, BOOL bShow)
{
DWORD dwStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE | WS_POPUP;
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST,
szViewClassName,
szViewWindowName,
dwStyle,
0,
0,
width,
height,
(HWND)NULL,
(HMENU)NULL,
GetModuleHandle(NULL),
NULL);
if ((hWnd != NULL) && (bShow))
{
ShowWindow (hWnd, SW_SHOW);
}
return hWnd;
}