PDA

View Full Version : Multi-threading with Detonator3 Drivers



Rob Love
09-21-2000, 12:14 AM
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;
}

Nicolas Lelong
09-21-2000, 12:44 AM
Hi,

Just a thought, I don't know for sure.

I took a look at a GL init/end source I have here ; and I saw it has something you don't have in your end sequence ; perhaps, this causes the bug : it seems you don't tell GL to release the rendering context before you delete it... so it may mess up the internal state !...

You should try with :

// Release the OpenGL context
wglMakeCurrent(NULL,NULL);

// Delete the OpenGL context
wglDeleteContext(hrc);

Hope this helps.

Nico

Rob Love
09-21-2000, 01:19 AM
Thanks Nico, that is a line I'd missed but unfortunately even with that change the Detonator3 drivers still crash on the second time round.

Many Thanks
Rob

skw|d
09-21-2000, 01:36 AM
If it is just nVidia drivers that are causing problems you should contact them. Have you been able to get two applications to run at the same time using the GL hardware?

/skw|d

Nicolas Lelong
09-21-2000, 03:12 AM
Have you tried to execute many times exactly the same code in the 'main' thread of your app ?

I was just thinking that as you don't have 2 threads using OpenGL in distinct contexts, the behaviour of your code should be the same in whatever thread it is...

So, I believe it should crash as well in the main thread !?! If so, that could help debugging... Maybe http://www.opengl.org/discussion_boards/ubb/confused.gif

Rob Love
09-21-2000, 05:17 AM
Thanks again Nico.

It turns out that if I perform the follwoing in the parent process...

HWND hWnd = ::GetDesktopWindow();
HDC hdc = ::GetDC(hWnd);
SetPixelFormat(hdc, 1, NULL);
::ReleaseDC(hWnd, hdc);

Then all is well. Looks like the ICD has to be first called from the parent process and then it can be called from child threads with no problems. When nVIDIA get back to me I'll let them know

Thanks
Rob

John Schultz
09-23-2000, 12:34 PM
I have observed this bug as well. My solution was to create an OpenGL context in the main thread first, make it current, delete it, and then start the new thread which creates its own context.