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
{
LONG ref; /* reference count */
enum dc_gl_type type; /* type of GL surface */
GLXDrawable drawable; /* drawable for rendering with GL */
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;
}
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' */
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;
EnterCriticalSection( &context_section );
LIST_FOR_EACH_ENTRY( ctx, &context_list, struct wgl_context, entry )
{
if (old == ctx->drawables[0]) {
ctx->drawables[0] = new;
if (old->drawable == ctx->drawables[0]) {
ctx->drawables[0] = new->drawable;
ctx->refresh_drawables = TRUE;
}
if (old == ctx->drawables[1]) {
ctx->drawables[1] = new;
if (old->drawable == ctx->drawables[1]) {
ctx->drawables[1] = new->drawable;
ctx->refresh_drawables = TRUE;
}
}
LeaveCriticalSection( &context_section );
}
/* Given the current context, make sure its drawable is sync'd */
static inline void sync_context(struct wgl_context *context)
{
EnterCriticalSection( &context_section );
if (context->refresh_drawables) {
if (glxRequireVersion(3))
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);
context->refresh_drawables = FALSE;
}
LeaveCriticalSection( &context_section );
}
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;
EnterCriticalSection( &context_section );
if (hwnd && !XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&gl )) return gl;
if (hdc && !XFindContext( gdi_display, (XID)hdc, gl_pbuffer_context, (char **)&gl )) return gl;
if (hwnd && !XFindContext( gdi_display, (XID)hwnd, gl_hwnd_context, (char **)&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 );
return NULL;
}
static void release_gl_drawable( struct gl_drawable *gl )
{
if (gl) LeaveCriticalSection( &context_section );
return gl;
}
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
*/
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;
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 );
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 */
{
@ -1425,9 +1442,21 @@ static BOOL create_gl_drawable( HWND hwnd, struct gl_drawable *gl )
}
}
if (gl->drawable)
gl->refresh_swap_interval = TRUE;
return gl->drawable != 0;
if (!gl->drawable)
{
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 )
{
struct gl_drawable *gl, *prev;
struct gl_drawable *gl;
if (!format->visual) return FALSE;
gl = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*gl) );
/* 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;
}
if (!(gl = create_gl_drawable( hwnd, format ))) return FALSE;
TRACE( "created GL drawable %lx for win %p %s\n",
gl->drawable, hwnd, debugstr_fbconfig( format->fbconfig ));
XFlush( gdi_display );
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 );
release_gl_drawable( gl );
__wine_set_pixel_format( hwnd, pixel_format_index( format ));
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 )
{
struct gl_drawable *gl;
GLXDrawable glxp;
Pixmap pix;
struct gl_drawable *old, *new;
XWindowChanges changes;
changes.width = min( max( 1, width ), 65535 );
changes.height = min( max( 1, height ), 65535 );
if (!(old = get_gl_drawable( hwnd, 0 ))) return;
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 (gl->type)
switch (old->type)
{
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;
case DC_GL_PIXMAP_WIN:
pix = XCreatePixmap(gdi_display, root_window, changes.width, changes.height,
gl->format->visual->depth);
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);
if (!(new = create_gl_drawable( hwnd, old->format ))) break;
mark_drawable_dirty( old, new );
XFlush( gdi_display );
XFreePixmap(gdi_display, gl->pixmap);
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;
TRACE( "Recreated GL drawable %lx to replace %lx\n", new->drawable, old->drawable );
release_gl_drawable( new );
break;
default:
break;
}
done:
release_gl_drawable( gl );
release_gl_drawable( old );
}
@ -1573,46 +1563,36 @@ done:
*/
void set_gl_drawable_parent( HWND hwnd, HWND parent )
{
struct gl_drawable *gl;
GLXDrawable old_drawable;
struct gl_drawable *old, *new;
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 (gl->type)
switch (old->type)
{
case DC_GL_WINDOW:
break;
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:
if (parent != GetDesktopWindow()) goto done;
pglXDestroyPixmap( gdi_display, gl->drawable );
XFreePixmap( gdi_display, gl->pixmap );
break;
if (parent == GetDesktopWindow()) break;
/* fall through */
default:
goto done;
}
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 );
release_gl_drawable( old );
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 ))
{
XDeleteContext( gdi_display, (XID)hwnd, gl_hwnd_context );
free_gl_drawable( gl );
release_gl_drawable( gl );
}
LeaveCriticalSection( &context_section );
}
@ -1827,7 +1807,9 @@ static struct wgl_context *glxdrv_wglCreateContext( HDC hdc )
ret->hdc = hdc;
ret->fmt = gl->format;
ret->ctx = create_glxcontext(gdi_display, ret, NULL);
EnterCriticalSection( &context_section );
list_add_head( &context_list, &ret->entry );
LeaveCriticalSection( &context_section );
}
release_gl_drawable( gl );
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,
debugstr_fbconfig( gl->format->fbconfig ));
EnterCriticalSection( &context_section );
ret = pglXMakeCurrent(gdi_display, gl->drawable, ctx->ctx);
if (ret)
{
@ -1905,8 +1888,10 @@ static BOOL glxdrv_wglMakeCurrent(HDC hdc, struct wgl_context *ctx)
ctx->drawables[0] = gl->drawable;
ctx->drawables[1] = gl->drawable;
ctx->refresh_drawables = FALSE;
LeaveCriticalSection( &context_section );
goto done;
}
LeaveCriticalSection( &context_section );
}
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 )))
{
read_gl = get_gl_drawable( WindowFromDC( read_hdc ), read_hdc );
EnterCriticalSection( &context_section );
ret = pglXMakeContextCurrent(gdi_display, draw_gl->drawable,
read_gl ? read_gl->drawable : 0, ctx->ctx);
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->refresh_drawables = FALSE;
NtCurrentTeb()->glContext = ctx;
LeaveCriticalSection( &context_section );
goto done;
}
LeaveCriticalSection( &context_section );
}
SetLastError( ERROR_INVALID_HANDLE );
done:
@ -2148,7 +2137,12 @@ static struct wgl_context *X11DRV_wglCreateContextAttribsARB( HDC hdc, struct wg
HeapFree( GetProcessHeap(), 0, ret );
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 );
@ -2394,10 +2388,11 @@ static HDC X11DRV_wglGetPbufferDCARB( struct wgl_pbuffer *object )
gl->type = DC_GL_PBUFFER;
gl->drawable = object->drawable;
gl->format = object->fmt;
gl->ref = 1;
EnterCriticalSection( &context_section );
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 );
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 ))
{
XDeleteContext( gdi_display, (XID)hdc, gl_pbuffer_context );
free_gl_drawable( gl );
release_gl_drawable( gl );
}
else hdc = 0;
@ -3100,6 +3095,7 @@ static BOOL X11DRV_wglSwapIntervalEXT(int interval)
return FALSE;
}
EnterCriticalSection( &context_section );
ret = set_swap_interval(gl->drawable, interval);
gl->refresh_swap_interval = FALSE;
if (ret)
@ -3107,6 +3103,7 @@ static BOOL X11DRV_wglSwapIntervalEXT(int interval)
else
SetLastError(ERROR_DC_NOT_FOUND);
LeaveCriticalSection( &context_section );
release_gl_drawable(gl);
return ret;
@ -3325,11 +3322,13 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
return FALSE;
}
EnterCriticalSection( &context_section );
if (gl->refresh_swap_interval)
{
set_swap_interval(gl->drawable, gl->swap_interval);
gl->refresh_swap_interval = FALSE;
}
LeaveCriticalSection( &context_section );
switch (gl->type)
{