winex11: Implement refcounting of GL drawables.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2018-02-12 15:34:28 +01:00
parent eebdb457bf
commit 6dc30a2ed3
1 changed files with 127 additions and 128 deletions

View File

@ -253,6 +253,7 @@ enum dc_gl_type
struct gl_drawable struct gl_drawable
{ {
LONG ref; /* reference count */
enum dc_gl_type type; /* type of GL surface */ enum dc_gl_type type; /* type of GL surface */
GLXDrawable drawable; /* drawable for rendering with GL */ GLXDrawable drawable; /* drawable for rendering with GL */
Window window; /* window if drawable is a GLXWindow */ Window window; /* window if drawable is a GLXWindow */
@ -1215,27 +1216,57 @@ static const struct wgl_pixel_format *get_pixel_format(Display *display, int iPi
return NULL; return NULL;
} }
static struct gl_drawable *grab_gl_drawable( struct gl_drawable *gl )
{
InterlockedIncrement( &gl->ref );
return gl;
}
static void release_gl_drawable( struct gl_drawable *gl )
{
if (!gl) return;
if (InterlockedDecrement( &gl->ref )) return;
switch (gl->type)
{
case DC_GL_CHILD_WIN:
pglXDestroyWindow( gdi_display, gl->drawable );
XDestroyWindow( gdi_display, gl->window );
XFreeColormap( gdi_display, gl->colormap );
break;
case DC_GL_PIXMAP_WIN:
pglXDestroyPixmap( gdi_display, gl->drawable );
XFreePixmap( gdi_display, gl->pixmap );
break;
default:
break;
}
HeapFree( GetProcessHeap(), 0, gl );
}
/* Mark any allocated context using the glx drawable 'old' to use 'new' */ /* Mark any allocated context using the glx drawable 'old' to use 'new' */
static void mark_drawable_dirty(GLXDrawable old, GLXDrawable new) static void mark_drawable_dirty( struct gl_drawable *old, struct gl_drawable *new )
{ {
struct wgl_context *ctx; struct wgl_context *ctx;
EnterCriticalSection( &context_section );
LIST_FOR_EACH_ENTRY( ctx, &context_list, struct wgl_context, entry ) LIST_FOR_EACH_ENTRY( ctx, &context_list, struct wgl_context, entry )
{ {
if (old == ctx->drawables[0]) { if (old->drawable == ctx->drawables[0]) {
ctx->drawables[0] = new; ctx->drawables[0] = new->drawable;
ctx->refresh_drawables = TRUE; ctx->refresh_drawables = TRUE;
} }
if (old == ctx->drawables[1]) { if (old->drawable == ctx->drawables[1]) {
ctx->drawables[1] = new; ctx->drawables[1] = new->drawable;
ctx->refresh_drawables = TRUE; ctx->refresh_drawables = TRUE;
} }
} }
LeaveCriticalSection( &context_section );
} }
/* Given the current context, make sure its drawable is sync'd */ /* Given the current context, make sure its drawable is sync'd */
static inline void sync_context(struct wgl_context *context) static inline void sync_context(struct wgl_context *context)
{ {
EnterCriticalSection( &context_section );
if (context->refresh_drawables) { if (context->refresh_drawables) {
if (glxRequireVersion(3)) if (glxRequireVersion(3))
pglXMakeContextCurrent(gdi_display, context->drawables[0], pglXMakeContextCurrent(gdi_display, context->drawables[0],
@ -1244,6 +1275,7 @@ static inline void sync_context(struct wgl_context *context)
pglXMakeCurrent(gdi_display, context->drawables[0], context->ctx); pglXMakeCurrent(gdi_display, context->drawables[0], context->ctx);
context->refresh_drawables = FALSE; context->refresh_drawables = FALSE;
} }
LeaveCriticalSection( &context_section );
} }
static BOOL set_swap_interval(GLXDrawable drawable, int interval) static BOOL set_swap_interval(GLXDrawable drawable, int interval)
@ -1288,15 +1320,14 @@ static struct gl_drawable *get_gl_drawable( HWND hwnd, HDC hdc )
struct gl_drawable *gl; struct gl_drawable *gl;
EnterCriticalSection( &context_section ); EnterCriticalSection( &context_section );
if (hwnd && !XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&gl )) return gl; if (hwnd && !XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&gl ))
if (hdc && !XFindContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char **)&gl )) return gl; gl = grab_gl_drawable( gl );
else if (hdc && !XFindContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char **)&gl ))
gl = grab_gl_drawable( gl );
else
gl = NULL;
LeaveCriticalSection( &context_section ); LeaveCriticalSection( &context_section );
return NULL; return gl;
}
static void release_gl_drawable( struct gl_drawable *gl )
{
if (gl) LeaveCriticalSection( &context_section );
} }
static GLXContext create_glxcontext(Display *display, struct wgl_context *context, GLXContext shareList) static GLXContext create_glxcontext(Display *display, struct wgl_context *context, GLXContext shareList)
@ -1319,35 +1350,13 @@ static GLXContext create_glxcontext(Display *display, struct wgl_context *contex
} }
/***********************************************************************
* free_gl_drawable
*/
static void free_gl_drawable( struct gl_drawable *gl )
{
switch (gl->type)
{
case DC_GL_CHILD_WIN:
pglXDestroyWindow( gdi_display, gl->drawable );
XDestroyWindow( gdi_display, gl->window );
XFreeColormap( gdi_display, gl->colormap );
break;
case DC_GL_PIXMAP_WIN:
pglXDestroyPixmap( gdi_display, gl->drawable );
XFreePixmap( gdi_display, gl->pixmap );
break;
default:
break;
}
HeapFree( GetProcessHeap(), 0, gl );
}
/*********************************************************************** /***********************************************************************
* create_gl_drawable * create_gl_drawable
*/ */
static BOOL create_gl_drawable( HWND hwnd, struct gl_drawable *gl ) static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel_format *format )
{ {
XVisualInfo *visual = gl->format->visual; struct gl_drawable *gl, *prev;
XVisualInfo *visual = format->visual;
RECT rect; RECT rect;
int width, height; int width, height;
@ -1355,7 +1364,15 @@ static BOOL create_gl_drawable( HWND hwnd, struct gl_drawable *gl )
width = min( max( 1, rect.right ), 65535 ); width = min( max( 1, rect.right ), 65535 );
height = min( max( 1, rect.bottom ), 65535 ); height = min( max( 1, rect.bottom ), 65535 );
gl->drawable = 0; if (!(gl = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*gl) ))) return NULL;
/* Default GLX and WGL swap interval is 1, but in case of glXSwapIntervalSGI
* there is no way to query it, so we have to store it here.
*/
gl->swap_interval = 1;
gl->refresh_swap_interval = TRUE;
gl->format = format;
gl->ref = 1;
if (GetAncestor( hwnd, GA_PARENT ) == GetDesktopWindow()) /* top-level window */ if (GetAncestor( hwnd, GA_PARENT ) == GetDesktopWindow()) /* top-level window */
{ {
@ -1425,9 +1442,21 @@ static BOOL create_gl_drawable( HWND hwnd, struct gl_drawable *gl )
} }
} }
if (gl->drawable) if (!gl->drawable)
gl->refresh_swap_interval = TRUE; {
return gl->drawable != 0; HeapFree( GetProcessHeap(), 0, gl );
return NULL;
}
EnterCriticalSection( &context_section );
if (!XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&prev ))
{
gl->swap_interval = prev->swap_interval;
release_gl_drawable( prev );
}
XSaveContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char *)grab_gl_drawable(gl) );
LeaveCriticalSection( &context_section );
return gl;
} }
@ -1436,37 +1465,17 @@ static BOOL create_gl_drawable( HWND hwnd, struct gl_drawable *gl )
*/ */
static BOOL set_win_format( HWND hwnd, const struct wgl_pixel_format *format ) static BOOL set_win_format( HWND hwnd, const struct wgl_pixel_format *format )
{ {
struct gl_drawable *gl, *prev; struct gl_drawable *gl;
if (!format->visual) return FALSE; if (!format->visual) return FALSE;
gl = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*gl) ); if (!(gl = create_gl_drawable( hwnd, format ))) return FALSE;
/* Default GLX and WGL swap interval is 1, but in case of glXSwapIntervalSGI
* there is no way to query it, so we have to store it here.
*/
gl->swap_interval = 1;
gl->refresh_swap_interval = TRUE;
gl->format = format;
if (!create_gl_drawable( hwnd, gl ))
{
HeapFree( GetProcessHeap(), 0, gl );
return FALSE;
}
TRACE( "created GL drawable %lx for win %p %s\n", TRACE( "created GL drawable %lx for win %p %s\n",
gl->drawable, hwnd, debugstr_fbconfig( format->fbconfig )); gl->drawable, hwnd, debugstr_fbconfig( format->fbconfig ));
XFlush( gdi_display ); XFlush( gdi_display );
release_gl_drawable( gl );
EnterCriticalSection( &context_section );
if (!XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&prev ))
{
gl->swap_interval = prev->swap_interval;
free_gl_drawable( prev );
}
XSaveContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char *)gl );
LeaveCriticalSection( &context_section );
__wine_set_pixel_format( hwnd, pixel_format_index( format )); __wine_set_pixel_format( hwnd, pixel_format_index( format ));
return TRUE; return TRUE;
@ -1521,50 +1530,31 @@ static BOOL set_pixel_format(HDC hdc, int format, BOOL allow_change)
*/ */
void sync_gl_drawable( HWND hwnd, int width, int height ) void sync_gl_drawable( HWND hwnd, int width, int height )
{ {
struct gl_drawable *gl; struct gl_drawable *old, *new;
GLXDrawable glxp;
Pixmap pix;
XWindowChanges changes; XWindowChanges changes;
changes.width = min( max( 1, width ), 65535 ); if (!(old = get_gl_drawable( hwnd, 0 ))) return;
changes.height = min( max( 1, height ), 65535 );
if (!(gl = get_gl_drawable( hwnd, 0 ))) return; TRACE( "setting drawable %lx size %dx%d\n", old->drawable, width, height );
TRACE( "setting drawable %lx size %dx%d\n", gl->drawable, changes.width, changes.height ); switch (old->type)
switch (gl->type)
{ {
case DC_GL_CHILD_WIN: case DC_GL_CHILD_WIN:
XConfigureWindow( gdi_display, gl->window, CWWidth | CWHeight, &changes ); changes.width = min( max( 1, width ), 65535 );
changes.height = min( max( 1, height ), 65535 );
XConfigureWindow( gdi_display, old->window, CWWidth | CWHeight, &changes );
break; break;
case DC_GL_PIXMAP_WIN: case DC_GL_PIXMAP_WIN:
pix = XCreatePixmap(gdi_display, root_window, changes.width, changes.height, if (!(new = create_gl_drawable( hwnd, old->format ))) break;
gl->format->visual->depth); mark_drawable_dirty( old, new );
if (!pix) goto done;
glxp = pglXCreatePixmap(gdi_display, gl->format->fbconfig, pix, NULL );
if (!glxp)
{
XFreePixmap(gdi_display, pix);
goto done;
}
mark_drawable_dirty(gl->drawable, glxp);
XFlush( gdi_display ); XFlush( gdi_display );
TRACE( "Recreated GL drawable %lx to replace %lx\n", new->drawable, old->drawable );
XFreePixmap(gdi_display, gl->pixmap); release_gl_drawable( new );
pglXDestroyPixmap(gdi_display, gl->drawable);
TRACE( "Recreated GL drawable %lx to replace %lx\n", glxp, gl->drawable );
gl->pixmap = pix;
gl->drawable = glxp;
gl->pixmap_size.cx = width;
gl->pixmap_size.cy = height;
break; break;
default: default:
break; break;
} }
done: release_gl_drawable( old );
release_gl_drawable( gl );
} }
@ -1573,46 +1563,36 @@ done:
*/ */
void set_gl_drawable_parent( HWND hwnd, HWND parent ) void set_gl_drawable_parent( HWND hwnd, HWND parent )
{ {
struct gl_drawable *gl; struct gl_drawable *old, *new;
GLXDrawable old_drawable;
if (!(gl = get_gl_drawable( hwnd, 0 ))) return; if (!(old = get_gl_drawable( hwnd, 0 ))) return;
TRACE( "setting drawable %lx parent %p\n", gl->drawable, parent ); TRACE( "setting drawable %lx parent %p\n", old->drawable, parent );
old_drawable = gl->drawable; switch (old->type)
switch (gl->type)
{ {
case DC_GL_WINDOW: case DC_GL_WINDOW:
break; break;
case DC_GL_CHILD_WIN: case DC_GL_CHILD_WIN:
if (parent != GetDesktopWindow()) goto done;
pglXDestroyWindow( gdi_display, gl->drawable );
XDestroyWindow( gdi_display, gl->window );
XFreeColormap( gdi_display, gl->colormap );
break;
case DC_GL_PIXMAP_WIN: case DC_GL_PIXMAP_WIN:
if (parent != GetDesktopWindow()) goto done; if (parent == GetDesktopWindow()) break;
pglXDestroyPixmap( gdi_display, gl->drawable ); /* fall through */
XFreePixmap( gdi_display, gl->pixmap );
break;
default: default:
goto done; release_gl_drawable( old );
}
if (!create_gl_drawable( hwnd, gl ))
{
XDeleteContext( gdi_display, (XID)hwnd, gl_hwnd_context );
release_gl_drawable( gl );
HeapFree( GetProcessHeap(), 0, gl );
__wine_set_pixel_format( hwnd, 0 );
return; return;
} }
mark_drawable_dirty( old_drawable, gl->drawable );
done:
release_gl_drawable( gl );
if ((new = create_gl_drawable( hwnd, old->format )))
{
mark_drawable_dirty( old, new );
release_gl_drawable( new );
}
else
{
destroy_gl_drawable( hwnd );
__wine_set_pixel_format( hwnd, 0 );
}
release_gl_drawable( old );
} }
@ -1627,7 +1607,7 @@ void destroy_gl_drawable( HWND hwnd )
if (!XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&gl )) if (!XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&gl ))
{ {
XDeleteContext( gdi_display, (XID)hwnd, gl_hwnd_context ); XDeleteContext( gdi_display, (XID)hwnd, gl_hwnd_context );
free_gl_drawable( gl ); release_gl_drawable( gl );
} }
LeaveCriticalSection( &context_section ); LeaveCriticalSection( &context_section );
} }
@ -1827,7 +1807,9 @@ static struct wgl_context *glxdrv_wglCreateContext( HDC hdc )
ret->hdc = hdc; ret->hdc = hdc;
ret->fmt = gl->format; ret->fmt = gl->format;
ret->ctx = create_glxcontext(gdi_display, ret, NULL); ret->ctx = create_glxcontext(gdi_display, ret, NULL);
EnterCriticalSection( &context_section );
list_add_head( &context_list, &ret->entry ); list_add_head( &context_list, &ret->entry );
LeaveCriticalSection( &context_section );
} }
release_gl_drawable( gl ); release_gl_drawable( gl );
TRACE( "%p -> %p\n", hdc, ret ); TRACE( "%p -> %p\n", hdc, ret );
@ -1896,6 +1878,7 @@ static BOOL glxdrv_wglMakeCurrent(HDC hdc, struct wgl_context *ctx)
TRACE("hdc %p drawable %lx fmt %p ctx %p %s\n", hdc, gl->drawable, gl->format, ctx->ctx, TRACE("hdc %p drawable %lx fmt %p ctx %p %s\n", hdc, gl->drawable, gl->format, ctx->ctx,
debugstr_fbconfig( gl->format->fbconfig )); debugstr_fbconfig( gl->format->fbconfig ));
EnterCriticalSection( &context_section );
ret = pglXMakeCurrent(gdi_display, gl->drawable, ctx->ctx); ret = pglXMakeCurrent(gdi_display, gl->drawable, ctx->ctx);
if (ret) if (ret)
{ {
@ -1905,8 +1888,10 @@ static BOOL glxdrv_wglMakeCurrent(HDC hdc, struct wgl_context *ctx)
ctx->drawables[0] = gl->drawable; ctx->drawables[0] = gl->drawable;
ctx->drawables[1] = gl->drawable; ctx->drawables[1] = gl->drawable;
ctx->refresh_drawables = FALSE; ctx->refresh_drawables = FALSE;
LeaveCriticalSection( &context_section );
goto done; goto done;
} }
LeaveCriticalSection( &context_section );
} }
SetLastError( ERROR_INVALID_HANDLE ); SetLastError( ERROR_INVALID_HANDLE );
@ -1938,6 +1923,8 @@ static BOOL X11DRV_wglMakeContextCurrentARB( HDC draw_hdc, HDC read_hdc, struct
if ((draw_gl = get_gl_drawable( WindowFromDC( draw_hdc ), draw_hdc ))) if ((draw_gl = get_gl_drawable( WindowFromDC( draw_hdc ), draw_hdc )))
{ {
read_gl = get_gl_drawable( WindowFromDC( read_hdc ), read_hdc ); read_gl = get_gl_drawable( WindowFromDC( read_hdc ), read_hdc );
EnterCriticalSection( &context_section );
ret = pglXMakeContextCurrent(gdi_display, draw_gl->drawable, ret = pglXMakeContextCurrent(gdi_display, draw_gl->drawable,
read_gl ? read_gl->drawable : 0, ctx->ctx); read_gl ? read_gl->drawable : 0, ctx->ctx);
if (ret) if (ret)
@ -1948,8 +1935,10 @@ static BOOL X11DRV_wglMakeContextCurrentARB( HDC draw_hdc, HDC read_hdc, struct
ctx->drawables[1] = read_gl ? read_gl->drawable : 0; ctx->drawables[1] = read_gl ? read_gl->drawable : 0;
ctx->refresh_drawables = FALSE; ctx->refresh_drawables = FALSE;
NtCurrentTeb()->glContext = ctx; NtCurrentTeb()->glContext = ctx;
LeaveCriticalSection( &context_section );
goto done; goto done;
} }
LeaveCriticalSection( &context_section );
} }
SetLastError( ERROR_INVALID_HANDLE ); SetLastError( ERROR_INVALID_HANDLE );
done: done:
@ -2148,7 +2137,12 @@ static struct wgl_context *X11DRV_wglCreateContextAttribsARB( HDC hdc, struct wg
HeapFree( GetProcessHeap(), 0, ret ); HeapFree( GetProcessHeap(), 0, ret );
ret = NULL; ret = NULL;
} }
else list_add_head( &context_list, &ret->entry ); else
{
EnterCriticalSection( &context_section );
list_add_head( &context_list, &ret->entry );
LeaveCriticalSection( &context_section );
}
} }
release_gl_drawable( gl ); release_gl_drawable( gl );
@ -2394,10 +2388,11 @@ static HDC X11DRV_wglGetPbufferDCARB( struct wgl_pbuffer *object )
gl->type = DC_GL_PBUFFER; gl->type = DC_GL_PBUFFER;
gl->drawable = object->drawable; gl->drawable = object->drawable;
gl->format = object->fmt; gl->format = object->fmt;
gl->ref = 1;
EnterCriticalSection( &context_section ); EnterCriticalSection( &context_section );
if (!XFindContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char **)&prev )) if (!XFindContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char **)&prev ))
free_gl_drawable( prev ); release_gl_drawable( prev );
XSaveContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char *)gl ); XSaveContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char *)gl );
LeaveCriticalSection( &context_section ); LeaveCriticalSection( &context_section );
@ -2516,7 +2511,7 @@ static int X11DRV_wglReleasePbufferDCARB( struct wgl_pbuffer *object, HDC hdc )
if (!XFindContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char **)&gl )) if (!XFindContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char **)&gl ))
{ {
XDeleteContext( gdi_display, (XID)hdc, gl_pbuffer_context ); XDeleteContext( gdi_display, (XID)hdc, gl_pbuffer_context );
free_gl_drawable( gl ); release_gl_drawable( gl );
} }
else hdc = 0; else hdc = 0;
@ -3100,6 +3095,7 @@ static BOOL X11DRV_wglSwapIntervalEXT(int interval)
return FALSE; return FALSE;
} }
EnterCriticalSection( &context_section );
ret = set_swap_interval(gl->drawable, interval); ret = set_swap_interval(gl->drawable, interval);
gl->refresh_swap_interval = FALSE; gl->refresh_swap_interval = FALSE;
if (ret) if (ret)
@ -3107,6 +3103,7 @@ static BOOL X11DRV_wglSwapIntervalEXT(int interval)
else else
SetLastError(ERROR_DC_NOT_FOUND); SetLastError(ERROR_DC_NOT_FOUND);
LeaveCriticalSection( &context_section );
release_gl_drawable(gl); release_gl_drawable(gl);
return ret; return ret;
@ -3325,11 +3322,13 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
return FALSE; return FALSE;
} }
EnterCriticalSection( &context_section );
if (gl->refresh_swap_interval) if (gl->refresh_swap_interval)
{ {
set_swap_interval(gl->drawable, gl->swap_interval); set_swap_interval(gl->drawable, gl->swap_interval);
gl->refresh_swap_interval = FALSE; gl->refresh_swap_interval = FALSE;
} }
LeaveCriticalSection( &context_section );
switch (gl->type) switch (gl->type)
{ {