wgl: Improve wglDeleteContext threading behavior.

This commit is contained in:
Roderick Colenbrander 2010-04-26 15:49:56 +02:00 committed by Alexandre Julliard
parent bfc4c71049
commit b86787e57c
2 changed files with 100 additions and 18 deletions

View File

@ -499,6 +499,61 @@ static void test_acceleration(HDC hdc)
}
}
struct wgl_thread_param
{
HANDLE test_finished;
HGLRC hglrc;
BOOL hglrc_deleted;
};
static DWORD WINAPI wgl_thread(void *param)
{
struct wgl_thread_param *p = param;
p->hglrc_deleted = wglDeleteContext(p->hglrc);
SetEvent(p->test_finished);
return 0;
}
static void test_deletecontext(HDC hdc)
{
struct wgl_thread_param thread_params;
HGLRC hglrc = wglCreateContext(hdc);
HANDLE thread_handle;
DWORD res, tid;
if(!hglrc)
{
skip("wglCreateContext failed!\n");
return;
}
res = wglMakeCurrent(hdc, hglrc);
if(!res)
{
skip("wglMakeCurrent failed!\n");
return;
}
/* WGL doesn't allow you to delete a context from a different thread than the one in which it is current.
* This differs from GLX which does allow it but it delays actual deletion until the context becomes not current.
*/
thread_params.hglrc = hglrc;
thread_params.test_finished = CreateEvent(NULL, FALSE, FALSE, NULL);
thread_handle = CreateThread(NULL, 0, wgl_thread, &thread_params, 0, &tid);
ok(!!thread_handle, "Failed to create thread, last error %#x.\n", GetLastError());
if(thread_handle)
{
WaitForSingleObject(thread_handle, INFINITE);
ok(thread_params.hglrc_deleted == FALSE, "Attempt to delete WGL context from another thread passed but should fail!\n");
}
CloseHandle(thread_params.test_finished);
res = wglDeleteContext(hglrc);
ok(res == TRUE, "wglDeleteContext failed\n");
}
static void test_make_current_read(HDC hdc)
{
int res;
@ -802,6 +857,7 @@ START_TEST(opengl)
return;
}
test_deletecontext(hdc);
test_makecurrent(hdc);
test_setpixelformat(hdc);
test_sharelists(hdc);

View File

@ -113,6 +113,7 @@ typedef struct wine_glcontext {
BOOL do_escape;
BOOL has_been_current;
BOOL sharing;
DWORD tid;
BOOL gl3_context;
XVisualInfo *vis;
WineGLPixelFormat *fmt;
@ -1763,29 +1764,33 @@ HGLRC CDECL X11DRV_wglCreateContext(X11DRV_PDEVICE *physDev)
BOOL CDECL X11DRV_wglDeleteContext(HGLRC hglrc)
{
Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
BOOL ret = TRUE;
TRACE("(%p)\n", hglrc);
if (!has_opengl()) return 0;
wine_tsx11_lock();
/* A game (Half Life not to name it) deletes twice the same context,
* so make sure it is valid first */
if (is_valid_context( ctx ))
{
if (ctx->ctx) pglXDestroyContext(gdi_display, ctx->ctx);
free_context(ctx);
}
else
if (!is_valid_context(ctx))
{
WARN("Error deleting context !\n");
SetLastError(ERROR_INVALID_HANDLE);
ret = FALSE;
return FALSE;
}
wine_tsx11_unlock();
return ret;
/* WGL doesn't allow deletion of a context which is current in another thread */
if (ctx->tid != 0 && ctx->tid != GetCurrentThreadId())
{
TRACE("Cannot delete context=%p because it is current in another thread.\n", ctx);
return FALSE;
}
if (ctx->ctx)
{
wine_tsx11_lock();
pglXDestroyContext(gdi_display, ctx->ctx);
wine_tsx11_unlock();
}
return TRUE;
}
/**
@ -1857,16 +1862,26 @@ BOOL CDECL X11DRV_wglMakeCurrent(X11DRV_PDEVICE *physDev, HGLRC hglrc) {
if (!has_opengl()) return FALSE;
wine_tsx11_lock();
if (hglrc == NULL) {
if (hglrc == NULL)
{
Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
if (prev_ctx) prev_ctx->tid = 0;
ret = pglXMakeCurrent(gdi_display, None, NULL);
NtCurrentTeb()->glContext = NULL;
} else if (ctx->fmt->iPixelFormat != physDev->current_pf) {
}
else if (ctx->fmt->iPixelFormat != physDev->current_pf)
{
WARN( "mismatched pixel format hdc %p %u ctx %p %u\n",
hdc, physDev->current_pf, ctx, ctx->fmt->iPixelFormat );
SetLastError( ERROR_INVALID_PIXEL_FORMAT );
ret = FALSE;
} else {
}
else
{
Drawable drawable = get_glxdrawable(physDev);
Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
if (prev_ctx) prev_ctx->tid = 0;
/* The describe lines below are for debugging purposes only */
if (TRACE_ON(wgl)) {
@ -1881,6 +1896,7 @@ BOOL CDECL X11DRV_wglMakeCurrent(X11DRV_PDEVICE *physDev, HGLRC hglrc) {
if(ret)
{
ctx->has_been_current = TRUE;
ctx->tid = GetCurrentThreadId();
ctx->hdc = hdc;
ctx->read_hdc = hdc;
ctx->drawables[0] = drawable;
@ -1913,18 +1929,28 @@ BOOL CDECL X11DRV_wglMakeContextCurrentARB(X11DRV_PDEVICE* pDrawDev, X11DRV_PDEV
if (!has_opengl()) return 0;
wine_tsx11_lock();
if (hglrc == NULL) {
if (hglrc == NULL)
{
Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
if (prev_ctx) prev_ctx->tid = 0;
ret = pglXMakeCurrent(gdi_display, None, NULL);
NtCurrentTeb()->glContext = NULL;
} else {
}
else
{
if (NULL == pglXMakeContextCurrent) {
ret = FALSE;
} else {
Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
Drawable d_draw = get_glxdrawable(pDrawDev);
Drawable d_read = get_glxdrawable(pReadDev);
if (prev_ctx) prev_ctx->tid = 0;
ctx->has_been_current = TRUE;
ctx->tid = GetCurrentThreadId();
ctx->hdc = pDrawDev->hdc;
ctx->read_hdc = pReadDev->hdc;
ctx->drawables[0] = d_draw;