Commit b86787e5 authored by Roderick Colenbrander's avatar Roderick Colenbrander Committed by Alexandre Julliard

wgl: Improve wglDeleteContext threading behavior.

parent bfc4c710
...@@ -499,6 +499,61 @@ static void test_acceleration(HDC hdc) ...@@ -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) static void test_make_current_read(HDC hdc)
{ {
int res; int res;
...@@ -802,6 +857,7 @@ START_TEST(opengl) ...@@ -802,6 +857,7 @@ START_TEST(opengl)
return; return;
} }
test_deletecontext(hdc);
test_makecurrent(hdc); test_makecurrent(hdc);
test_setpixelformat(hdc); test_setpixelformat(hdc);
test_sharelists(hdc); test_sharelists(hdc);
......
...@@ -113,6 +113,7 @@ typedef struct wine_glcontext { ...@@ -113,6 +113,7 @@ typedef struct wine_glcontext {
BOOL do_escape; BOOL do_escape;
BOOL has_been_current; BOOL has_been_current;
BOOL sharing; BOOL sharing;
DWORD tid;
BOOL gl3_context; BOOL gl3_context;
XVisualInfo *vis; XVisualInfo *vis;
WineGLPixelFormat *fmt; WineGLPixelFormat *fmt;
...@@ -1763,29 +1764,33 @@ HGLRC CDECL X11DRV_wglCreateContext(X11DRV_PDEVICE *physDev) ...@@ -1763,29 +1764,33 @@ HGLRC CDECL X11DRV_wglCreateContext(X11DRV_PDEVICE *physDev)
BOOL CDECL X11DRV_wglDeleteContext(HGLRC hglrc) BOOL CDECL X11DRV_wglDeleteContext(HGLRC hglrc)
{ {
Wine_GLContext *ctx = (Wine_GLContext *) hglrc; Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
BOOL ret = TRUE;
TRACE("(%p)\n", hglrc); TRACE("(%p)\n", hglrc);
if (!has_opengl()) return 0; if (!has_opengl()) return 0;
wine_tsx11_lock(); if (!is_valid_context(ctx))
/* 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
{ {
WARN("Error deleting context !\n"); WARN("Error deleting context !\n");
SetLastError(ERROR_INVALID_HANDLE); 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) { ...@@ -1857,16 +1862,26 @@ BOOL CDECL X11DRV_wglMakeCurrent(X11DRV_PDEVICE *physDev, HGLRC hglrc) {
if (!has_opengl()) return FALSE; if (!has_opengl()) return FALSE;
wine_tsx11_lock(); 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); ret = pglXMakeCurrent(gdi_display, None, NULL);
NtCurrentTeb()->glContext = 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", WARN( "mismatched pixel format hdc %p %u ctx %p %u\n",
hdc, physDev->current_pf, ctx, ctx->fmt->iPixelFormat ); hdc, physDev->current_pf, ctx, ctx->fmt->iPixelFormat );
SetLastError( ERROR_INVALID_PIXEL_FORMAT ); SetLastError( ERROR_INVALID_PIXEL_FORMAT );
ret = FALSE; ret = FALSE;
} else { }
else
{
Drawable drawable = get_glxdrawable(physDev); 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 */ /* The describe lines below are for debugging purposes only */
if (TRACE_ON(wgl)) { if (TRACE_ON(wgl)) {
...@@ -1881,6 +1896,7 @@ BOOL CDECL X11DRV_wglMakeCurrent(X11DRV_PDEVICE *physDev, HGLRC hglrc) { ...@@ -1881,6 +1896,7 @@ BOOL CDECL X11DRV_wglMakeCurrent(X11DRV_PDEVICE *physDev, HGLRC hglrc) {
if(ret) if(ret)
{ {
ctx->has_been_current = TRUE; ctx->has_been_current = TRUE;
ctx->tid = GetCurrentThreadId();
ctx->hdc = hdc; ctx->hdc = hdc;
ctx->read_hdc = hdc; ctx->read_hdc = hdc;
ctx->drawables[0] = drawable; ctx->drawables[0] = drawable;
...@@ -1913,18 +1929,28 @@ BOOL CDECL X11DRV_wglMakeContextCurrentARB(X11DRV_PDEVICE* pDrawDev, X11DRV_PDEV ...@@ -1913,18 +1929,28 @@ BOOL CDECL X11DRV_wglMakeContextCurrentARB(X11DRV_PDEVICE* pDrawDev, X11DRV_PDEV
if (!has_opengl()) return 0; if (!has_opengl()) return 0;
wine_tsx11_lock(); 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); ret = pglXMakeCurrent(gdi_display, None, NULL);
NtCurrentTeb()->glContext = NULL; NtCurrentTeb()->glContext = NULL;
} else { }
else
{
if (NULL == pglXMakeContextCurrent) { if (NULL == pglXMakeContextCurrent) {
ret = FALSE; ret = FALSE;
} else { } else {
Wine_GLContext *prev_ctx = NtCurrentTeb()->glContext;
Wine_GLContext *ctx = (Wine_GLContext *) hglrc; Wine_GLContext *ctx = (Wine_GLContext *) hglrc;
Drawable d_draw = get_glxdrawable(pDrawDev); Drawable d_draw = get_glxdrawable(pDrawDev);
Drawable d_read = get_glxdrawable(pReadDev); Drawable d_read = get_glxdrawable(pReadDev);
if (prev_ctx) prev_ctx->tid = 0;
ctx->has_been_current = TRUE; ctx->has_been_current = TRUE;
ctx->tid = GetCurrentThreadId();
ctx->hdc = pDrawDev->hdc; ctx->hdc = pDrawDev->hdc;
ctx->read_hdc = pReadDev->hdc; ctx->read_hdc = pReadDev->hdc;
ctx->drawables[0] = d_draw; ctx->drawables[0] = d_draw;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment