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.
diff --git a/graphics/ddraw.c b/graphics/ddraw.c
index da0bd41..8fee409 100644
--- a/graphics/ddraw.c
+++ b/graphics/ddraw.c
@@ -708,7 +708,11 @@
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 @@
0, 0, 0, 0,
This->t.xlib.image->width,
This->t.xlib.image->height,
- False);
+ True);
+ }
else
#endif
TSXPutImage( display,
@@ -5027,8 +5032,10 @@
#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 e55fbb0..48e70e8 100644
--- a/graphics/ddraw_private.h
+++ b/graphics/ddraw_private.h
@@ -81,7 +81,7 @@
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 6709455..9e4303d 100644
--- a/include/ts_xlib.h
+++ b/include/ts_xlib.h
@@ -53,6 +53,7 @@
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 1034f9c..0293b35 100644
--- a/include/x11drv.h
+++ b/include/x11drv.h
@@ -469,4 +469,7 @@
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 65c747b..b872337 100644
--- a/tsx11/X11_calls
+++ b/tsx11/X11_calls
@@ -16,6 +16,7 @@
XChangeKeyboardControl
XChangeProperty
XChangeWindowAttributes
+XCheckTypedEvent
XCheckTypedWindowEvent
XCheckWindowEvent
XClipBox
diff --git a/tsx11/ts_xlib.c b/tsx11/ts_xlib.c
index c336483..2ec5a35 100644
--- a/tsx11/ts_xlib.c
+++ b/tsx11/ts_xlib.c
@@ -422,6 +422,17 @@
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 4977625..2c3f40f 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 <assert.h>
#include <string.h>
@@ -102,6 +105,11 @@
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 @@
*/
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 @@
{
XEvent event;
- TRACE_(event)( "called.\n" );
+ TRACE_(event)( "called (thread %lx).\n", GetCurrentThreadId() );
EnterCriticalSection( &X11DRV_CritSection );
while ( XPending( display ) )
@@ -211,6 +223,13 @@
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 @@
event_names[event->type], hWnd );
break;
}
+ TRACE_(event)( "returns.\n" );
}
/***********************************************************************
@@ -1780,4 +1800,96 @@
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) */