From b030d20fc462c7778739b67d063bed92dcd644a4 Mon Sep 17 00:00:00 2001 From: Ove Kaaven Date: Sun, 23 Jan 2000 02:28:48 +0000 Subject: [PATCH] Supercharged XShm implementation for DirectDraw. --- graphics/ddraw.c | 25 +++++- include/x11drv.h | 2 + windows/x11drv/event.c | 175 ++++++++++++++++++++++++++++++++--------- 3 files changed, 160 insertions(+), 42 deletions(-) diff --git a/graphics/ddraw.c b/graphics/ddraw.c index 4c88786b4c8..804df3f0286 100644 --- a/graphics/ddraw.c +++ b/graphics/ddraw.c @@ -737,6 +737,18 @@ static HRESULT WINAPI IDirectDrawSurface4Impl_Lock( } else { assert(This->s.surface_desc.u1.lpSurface); } + + /* wait for any previous operations to complete */ +#ifdef HAVE_LIBXXSHM + if (This->t.xlib.image && (SDDSCAPS(This) & DDSCAPS_VISIBLE) && + This->s.ddraw->e.xlib.xshm_active) { +/* + int compl = InterlockedExchange( &This->s.ddraw->e.xlib.xshm_compl, 0 ); + if (compl) X11DRV_EVENT_WaitShmCompletion( compl ); +*/ + X11DRV_EVENT_WaitShmCompletions( This->s.ddraw->d.drawable ); + } +#endif return DD_OK; } @@ -761,10 +773,13 @@ static void Xlib_copy_surface_on_screen(IDirectDrawSurface4Impl* This) { #ifdef HAVE_LIBXXSHM 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 ); +/* + X11DRV_EVENT_WaitReplaceShmCompletion( &This->s.ddraw->e.xlib.xshm_compl, This->s.ddraw->d.drawable ); +*/ + /* let WaitShmCompletions track 'em for now */ + /* (you may want to track it again whenever you implement DX7's partial surface locking, + where threads have concurrent access) */ + X11DRV_EVENT_PrepareShmCompletion( This->s.ddraw->d.drawable ); TSXShmPutImage(display, This->s.ddraw->d.drawable, DefaultGCOfScreen(X11DRV_GetXScreen()), @@ -773,6 +788,8 @@ static void Xlib_copy_surface_on_screen(IDirectDrawSurface4Impl* This) { This->t.xlib.image->width, This->t.xlib.image->height, True); + /* make sure the image is transferred ASAP */ + TSXFlush(display); } else #endif diff --git a/include/x11drv.h b/include/x11drv.h index 33810ea0b6f..2644ea023c7 100644 --- a/include/x11drv.h +++ b/include/x11drv.h @@ -479,5 +479,7 @@ extern void X11DRV_WND_DockWindow(struct tagWND *wndPtr); extern int X11DRV_EVENT_PrepareShmCompletion( Drawable dw ); extern void X11DRV_EVENT_WaitShmCompletion( int compl ); +extern void X11DRV_EVENT_WaitShmCompletions( Drawable dw ); +extern void X11DRV_EVENT_WaitReplaceShmCompletion( int *compl, Drawable dw ); #endif /* __WINE_X11DRV_H */ diff --git a/windows/x11drv/event.c b/windows/x11drv/event.c index 590ee1d11d5..a54fb6ac8d1 100644 --- a/windows/x11drv/event.c +++ b/windows/x11drv/event.c @@ -112,7 +112,7 @@ static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event ); #ifdef HAVE_LIBXXSHM static void EVENT_ShmCompletion( XShmCompletionEvent *event ); -static int ShmCompletionType; +static int ShmAvailable, ShmCompletionType; extern int XShmGetEventBase( Display * );/* Missing prototype for function in libXext. */ #endif @@ -151,7 +151,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; + ShmAvailable = XShmQueryExtension( display ); + if (ShmAvailable) { + ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion; + } #endif /* Install the X event processing callback */ @@ -246,7 +249,7 @@ static void EVENT_ProcessEvent( XEvent *event ) } #ifdef HAVE_LIBXXSHM - if (event->type == ShmCompletionType) { + if (ShmAvailable && (event->type == ShmCompletionType)) { EVENT_ShmCompletion( (XShmCompletionEvent*)event ); return; } @@ -2002,59 +2005,155 @@ ShmCompletion (waiting for app) waking up on shm_read (waiting for app) returns (idle) */ + +typedef struct { + Drawable draw; + LONG state, waiter; + HANDLE sema; +} shm_qs; /* FIXME: this is not pretty */ -static Drawable shm_draw = 0; -static HANDLE shm_event = 0, shm_read = 0; +static HANDLE shm_read = 0; + +#define SHM_MAX_Q 4 +static volatile shm_qs shm_q[SHM_MAX_Q]; static void EVENT_ShmCompletion( XShmCompletionEvent *event ) { + int n; + TRACE("Got ShmCompletion for drawable %ld (time %ld)\n", event->drawable, GetTickCount() ); - if (event->drawable == shm_draw) { - HANDLE event = shm_event; - shm_draw = 0; - SetEvent(event); - TRACE("Event object triggered\n" ); - } else ERR("Got ShmCompletion for unknown drawable %ld\n", event->drawable ); + + for (n=0; ndrawable) && (shm_q[n].state == 0)) { + HANDLE sema = shm_q[n].sema; + if (!InterlockedCompareExchange((PVOID*)&shm_q[n].state, (PVOID)1, (PVOID)0)) { + ReleaseSemaphore(sema, 1, NULL); + TRACE("Signaling ShmCompletion (#%d) (semaphore %x)\n", n, sema); + } + return; + } + + ERR("Got ShmCompletion for unknown drawable %ld\n", event->drawable ); } int X11DRV_EVENT_PrepareShmCompletion( Drawable dw ) { - if (shm_draw) { - ERR("Multiple ShmCompletion requests not implemented\n"); - return 0; - } - TRACE("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 ) ); + int n; + if (!shm_read) shm_read = ConvertToGlobalHandle( FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE ) ); - return shm_event; + + for (n=0; n=SHM_MAX_Q) { + ERR("Maximum number of outstanding ShmCompletions exceeded!\n"); + return 0; + } + + shm_q[n].state = 0; + if (!shm_q[n].sema) { + shm_q[n].sema = ConvertToGlobalHandle( CreateSemaphoreA( NULL, 0, 256, NULL ) ); + TRACE("Allocated ShmCompletion slots have been increased to %d, new semaphore is %x\n", n+1, shm_q[n].sema); + } + + TRACE("Prepared ShmCompletion (#%d) wait for drawable %ld (thread %lx) (time %ld)\n", n, dw, GetCurrentThreadId(), GetTickCount() ); + return n+1; +} + +static void X11DRV_EVENT_WaitReplaceShmCompletionInternal( int *compl, Drawable dw, int creat ) +{ + int n = *compl; + LONG nn, st; + HANDLE sema; + + if ((!n) || (creat && (!shm_q[n-1].draw))) { + nn = X11DRV_EVENT_PrepareShmCompletion(dw); + if (!(n=(LONG)InterlockedCompareExchange((PVOID*)compl, (PVOID)nn, (PVOID)n))) + return; + /* race for compl lost, clear slot */ + shm_q[nn-1].draw = 0; + return; + } + + if (dw && (shm_q[n-1].draw != dw)) { + /* this shouldn't happen with the current ddraw implementation */ + FIXME("ShmCompletion replace with different drawable!\n"); + return; + } + + sema = shm_q[n-1].sema; + if (!sema) { + /* nothing to wait on (PrepareShmCompletion not done yet?), so probably nothing to wait for */ + return; + } + + nn = InterlockedExchangeAdd((PLONG)&shm_q[n-1].waiter, 1); + if ((!shm_q[n-1].draw) || (shm_q[n-1].state == 2)) { + /* too late, the wait was just cleared (wait complete) */ + TRACE("Wait skip for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema); + } else { + TRACE("Waiting for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema); + if (nn) { + /* another thread is already waiting, let the primary waiter do the dirty work + * (to avoid TSX critical section contention - that could get really slow) */ + WaitForSingleObject( sema, INFINITE ); + } else + /* we're primary waiter - first check if it's already triggered */ + if ( WaitForSingleObject( sema, 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] = sema; + 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 ); + } + TRACE("Wait complete (thread %lx) (time %ld)\n", GetCurrentThreadId(), GetTickCount() ); + + /* clear wait */ + st = InterlockedExchange(&shm_q[n-1].state, 2); + if (st != 2) { + /* first waiter to return, release all other waiters */ + nn = shm_q[n-1].waiter; + TRACE("Signaling %ld additional ShmCompletion (#%d) waiter(s), semaphore %x\n", nn-1, n-1, sema); + ReleaseSemaphore(sema, nn-1, NULL); + } + } + nn = InterlockedDecrement((LPLONG)&shm_q[n-1].waiter); + if (!nn) { + /* last waiter to return, replace drawable and prepare new wait */ + shm_q[n-1].draw = dw; + shm_q[n-1].state = 0; + } +} + +void X11DRV_EVENT_WaitReplaceShmCompletion( int *compl, Drawable dw ) +{ + X11DRV_EVENT_WaitReplaceShmCompletionInternal( compl, dw, 1 ); } void X11DRV_EVENT_WaitShmCompletion( int compl ) { if (!compl) return; - TRACE("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]; + X11DRV_EVENT_WaitReplaceShmCompletionInternal( &compl, 0, 0 ); +} - 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("Wait complete (time %ld)\n", GetTickCount() ); +void X11DRV_EVENT_WaitShmCompletions( Drawable dw ) +{ + int n; + + for (n=0; n