From 7da242dbd5200c27f22fc1fc2f3dffa84b899d03 Mon Sep 17 00:00:00 2001 From: Ove Kaaven Date: Sun, 7 Nov 1999 22:44:06 +0000 Subject: [PATCH] DirectX-XShm now waits for the X server to finish the previous frame before sending another frame down its pipe, avoiding the X server overload and resulting slowness that used to be. --- graphics/ddraw.c | 13 +++-- graphics/ddraw_private.h | 2 +- include/ts_xlib.h | 1 + include/x11drv.h | 3 ++ tsx11/X11_calls | 1 + tsx11/ts_xlib.c | 11 ++++ windows/x11drv/event.c | 114 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 140 insertions(+), 5 deletions(-) diff --git a/graphics/ddraw.c b/graphics/ddraw.c index da0bd41e3bb..8fee4092e70 100644 --- a/graphics/ddraw.c +++ b/graphics/ddraw.c @@ -708,7 +708,11 @@ static void Xlib_copy_surface_on_screen(IDirectDrawSurface4Impl* This) { This->s.palette); #ifdef HAVE_LIBXXSHM - if (This->s.ddraw->e.xlib.xshm_active) + if (This->s.ddraw->e.xlib.xshm_active) { + int compl = This->s.ddraw->e.xlib.xshm_compl; + if (compl) + X11DRV_EVENT_WaitShmCompletion( compl ); + This->s.ddraw->e.xlib.xshm_compl = X11DRV_EVENT_PrepareShmCompletion( This->s.ddraw->d.drawable ); TSXShmPutImage(display, This->s.ddraw->d.drawable, DefaultGCOfScreen(X11DRV_GetXScreen()), @@ -716,7 +720,8 @@ static void Xlib_copy_surface_on_screen(IDirectDrawSurface4Impl* This) { 0, 0, 0, 0, This->t.xlib.image->width, This->t.xlib.image->height, - False); + True); + } else #endif TSXPutImage( display, @@ -5027,8 +5032,10 @@ static HRESULT WINAPI Xlib_DirectDrawCreate( LPDIRECTDRAW *lplpDD, LPUNKNOWN pUn #ifdef HAVE_LIBXXSHM /* Test if XShm is available. */ - if (((*ilplpDD)->e.xlib.xshm_active = DDRAW_XSHM_Available())) + if (((*ilplpDD)->e.xlib.xshm_active = DDRAW_XSHM_Available())) { + (*ilplpDD)->e.xlib.xshm_compl = 0; TRACE("Using XShm extension.\n"); + } #endif return DD_OK; diff --git a/graphics/ddraw_private.h b/graphics/ddraw_private.h index e55fbb02db6..48e70e855c8 100644 --- a/graphics/ddraw_private.h +++ b/graphics/ddraw_private.h @@ -81,7 +81,7 @@ struct _dga_directdrawdata struct _xlib_directdrawdata { #ifdef HAVE_LIBXXSHM - int xshm_active; + int xshm_active, xshm_compl; #endif /* defined(HAVE_LIBXXSHM) */ /* are these needed for anything? (draw_surf is the active surface) diff --git a/include/ts_xlib.h b/include/ts_xlib.h index 67094554e10..9e4303db17e 100644 --- a/include/ts_xlib.h +++ b/include/ts_xlib.h @@ -53,6 +53,7 @@ extern int TSXChangeGC(Display*, GC, unsigned long, XGCValues*); extern int TSXChangeKeyboardControl(Display*, unsigned long, XKeyboardControl*); extern int TSXChangeProperty(Display*, Window, Atom, Atom, int, int, const unsigned char*, int); extern int TSXChangeWindowAttributes(Display*, Window, unsigned long, XSetWindowAttributes*); +extern int TSXCheckTypedEvent(Display*, int, XEvent*); extern int TSXCheckTypedWindowEvent(Display*, Window, int, XEvent*); extern int TSXCheckWindowEvent(Display*, Window, long, XEvent*); extern int TSXConvertSelection(Display*, Atom, Atom, Atom, Window, Time); diff --git a/include/x11drv.h b/include/x11drv.h index 1034f9cb309..0293b352f44 100644 --- a/include/x11drv.h +++ b/include/x11drv.h @@ -469,4 +469,7 @@ extern void X11DRV_WND_SetDrawable(struct tagWND *wndPtr, struct tagDC *dc, WORD extern BOOL X11DRV_WND_SetHostAttr(struct tagWND *wndPtr, INT haKey, INT value); extern BOOL X11DRV_WND_IsSelfClipping(struct tagWND *wndPtr); +extern int X11DRV_EVENT_PrepareShmCompletion( Drawable dw ); +extern void X11DRV_EVENT_WaitShmCompletion( int compl ); + #endif /* __WINE_X11DRV_H */ diff --git a/tsx11/X11_calls b/tsx11/X11_calls index 65c747b3e64..b872337be03 100644 --- a/tsx11/X11_calls +++ b/tsx11/X11_calls @@ -16,6 +16,7 @@ XChangeGC XChangeKeyboardControl XChangeProperty XChangeWindowAttributes +XCheckTypedEvent XCheckTypedWindowEvent XCheckWindowEvent XClipBox diff --git a/tsx11/ts_xlib.c b/tsx11/ts_xlib.c index c3364830de2..2ec5a35c239 100644 --- a/tsx11/ts_xlib.c +++ b/tsx11/ts_xlib.c @@ -422,6 +422,17 @@ int TSXChangeWindowAttributes(Display* a0, Window a1, unsigned long a2, XSetWin return r; } +int TSXCheckTypedEvent(Display* a0, int a1, XEvent* a2) +{ + int r; + TRACE("Call XCheckTypedEvent\n"); + EnterCriticalSection( &X11DRV_CritSection ); + r = XCheckTypedEvent(a0, a1, a2); + LeaveCriticalSection( &X11DRV_CritSection ); + TRACE("Ret XCheckTypedEvent\n"); + return r; +} + int TSXCheckTypedWindowEvent(Display* a0, Window a1, int a2, XEvent* a3) { int r; diff --git a/windows/x11drv/event.c b/windows/x11drv/event.c index 4977625a0e7..2c3f40fbddf 100644 --- a/windows/x11drv/event.c +++ b/windows/x11drv/event.c @@ -14,6 +14,9 @@ #include "ts_xlib.h" #include "ts_xresource.h" #include "ts_xutil.h" +#ifdef HAVE_LIBXXSHM +#include "ts_xshm.h" +#endif #include #include @@ -102,6 +105,11 @@ static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event ); static void EVENT_MapNotify( HWND pWnd, XMapEvent *event ); static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event ); +#ifdef HAVE_LIBXXSHM +static void EVENT_ShmCompletion( XShmCompletionEvent *event ); +static int ShmCompletionType; +#endif + /* Usable only with OLVWM - compile option perhaps? static void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event ); */ @@ -121,6 +129,10 @@ static BOOL in_transition = FALSE; /* This is not used as for today */ */ BOOL X11DRV_EVENT_Init(void) { +#ifdef HAVE_LIBXXSHM + ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion; +#endif + /* Install the X event processing callback */ SERVICE_AddObject( FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE ), @@ -150,7 +162,7 @@ static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg ) { XEvent event; - TRACE_(event)( "called.\n" ); + TRACE_(event)( "called (thread %lx).\n", GetCurrentThreadId() ); EnterCriticalSection( &X11DRV_CritSection ); while ( XPending( display ) ) @@ -211,6 +223,13 @@ static void EVENT_ProcessEvent( XEvent *event ) case ReparentNotify: return; } + +#ifdef HAVE_LIBXXSHM + if (event->type == ShmCompletionType) { + EVENT_ShmCompletion( (XShmCompletionEvent*)event ); + return; + } +#endif if ( TSXFindContext( display, event->xany.window, winContext, (char **)&hWnd ) != 0) { @@ -373,6 +392,7 @@ static void EVENT_ProcessEvent( XEvent *event ) event_names[event->type], hWnd ); break; } + TRACE_(event)( "returns.\n" ); } /*********************************************************************** @@ -1780,4 +1800,96 @@ INPUT_TYPE X11DRV_EVENT_SetInputMehod(INPUT_TYPE type) return prev; } +#ifdef HAVE_LIBXXSHM + +/* +Normal XShm operation: + +X11 service thread app thread +------------- ----------------- ------------------------ + (idle) ddraw calls XShmPutImage +(copies data) (waiting for shm_event) +ShmCompletion -> (waiting for shm_event) +(idle) signal shm_event -> + (idle) returns to app + +However, this situation can occur for some reason: + +X11 service thread app thread +------------- ----------------- ------------------------ +Expose -> + WM_ERASEBKGND? -> + (waiting for app) ddraw calls XShmPutImage +(copies data) (waiting for app) (waiting for shm_event) +ShmCompletion (waiting for app) (waiting for shm_event) +(idle) DEADLOCK DEADLOCK + +which is why I also wait for shm_read and do XCheckTypedEvent() +calls in the wait loop. This results in: + +X11 service thread app thread +------------- ----------------- ------------------------ +ShmCompletion (waiting for app) waking up on shm_read +(idle) (waiting for app) XCheckTypedEvent() -> signal shm_event + (waiting for app) returns + (idle) +*/ + +/* FIXME: this is not pretty */ +static Drawable shm_draw = 0; +static HANDLE shm_event = 0, shm_read = 0; + +static void EVENT_ShmCompletion( XShmCompletionEvent *event ) +{ + TRACE_(event)("Got ShmCompletion for drawable %ld (time %ld)\n", event->drawable, GetTickCount() ); + if (event->drawable == shm_draw) { + HANDLE event = shm_event; + shm_draw = 0; + shm_event = 0; + SetEvent(event); + TRACE_(event)("Event object triggered\n" ); + } else ERR_(event)("Got ShmCompletion for unknown drawable %ld\n", event->drawable ); +} + +int X11DRV_EVENT_PrepareShmCompletion( Drawable dw ) +{ + if (shm_draw) { + ERR_(event)("Multiple ShmCompletion requests not implemented\n"); + return 0; + } + TRACE_(event)("Preparing ShmCompletion (%d) wait for drawable %ld (time %ld)\n", ShmCompletionType, dw, GetTickCount() ); + shm_draw = dw; + if (!shm_event) + /* use manual reset just in case */ + shm_event = ConvertToGlobalHandle( CreateEventA( NULL, TRUE, FALSE, NULL ) ); + if (!shm_read) + shm_read = ConvertToGlobalHandle( FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE ) ); + return shm_event; +} + +void X11DRV_EVENT_WaitShmCompletion( int compl ) +{ + if (!compl) return; + TRACE_(event)("Waiting for ShmCompletion (%d) (thread %lx) (time %ld)\n", ShmCompletionType, GetCurrentThreadId(), GetTickCount() ); + /* already triggered? */ + if ( WaitForSingleObject( compl, 0 ) != WAIT_OBJECT_0 ) { + /* nope, may need to poll X event queue, in case the service thread is blocked */ + XEvent event; + HANDLE hnd[2]; + + hnd[0] = compl; + hnd[1] = shm_read; + do { + /* check X event queue */ + if (TSXCheckTypedEvent( display, ShmCompletionType, &event)) { + EVENT_ProcessEvent( &event ); + } + } while ( WaitForMultipleObjects(2, hnd, FALSE, INFINITE) > WAIT_OBJECT_0 ); + } + ResetEvent(compl); /* manual reset */ + TRACE_(event)("Wait complete (time %ld)\n", GetTickCount() ); +} + +#endif + #endif /* !defined(X_DISPLAY_MISSING) */