DGA video driver is now thread-safe
authorSam Lantinga <slouken@lokigames.com>
Thu, 12 Jul 2001 20:42:22 +0000
changeset 101825b2fa28e2e
parent 100 a1c973c35fef
child 102 9162d62280b5
DGA video driver is now thread-safe
Improved DGA hardware acceleration code
src/video/dga/SDL_dgaevents.c
src/video/dga/SDL_dgavideo.c
src/video/dga/SDL_dgavideo.h
     1.1 --- a/src/video/dga/SDL_dgaevents.c	Wed Jul 11 20:18:52 2001 +0000
     1.2 +++ b/src/video/dga/SDL_dgaevents.c	Thu Jul 12 20:42:22 2001 +0000
     1.3 @@ -97,9 +97,11 @@
     1.4  void DGA_PumpEvents(_THIS)
     1.5  {
     1.6  	/* Keep processing pending events */
     1.7 +	LOCK_DISPLAY();
     1.8  	while ( X11_Pending(DGA_Display) ) {
     1.9  		DGA_DispatchEvent(this);
    1.10  	}
    1.11 +	UNLOCK_DISPLAY();
    1.12  }
    1.13  
    1.14  void DGA_InitOSKeymap(_THIS)
     2.1 --- a/src/video/dga/SDL_dgavideo.c	Wed Jul 11 20:18:52 2001 +0000
     2.2 +++ b/src/video/dga/SDL_dgavideo.c	Thu Jul 12 20:42:22 2001 +0000
     2.3 @@ -58,7 +58,7 @@
     2.4  static void DGA_VideoQuit(_THIS);
     2.5  
     2.6  /* Hardware surface functions */
     2.7 -static int DGA_InitHWSurfaces(_THIS, Uint8 *base, int size);
     2.8 +static int DGA_InitHWSurfaces(_THIS, SDL_Surface *screen, Uint8 *base, int size);
     2.9  static void DGA_FreeHWSurfaces(_THIS);
    2.10  static int DGA_AllocHWSurface(_THIS, SDL_Surface *surface);
    2.11  static int DGA_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color);
    2.12 @@ -409,6 +409,11 @@
    2.13  		return(-1);
    2.14  	}
    2.15  
    2.16 +#ifdef LOCK_DGA_DISPLAY
    2.17 +	/* Create the event lock so we're thread-safe.. :-/ */
    2.18 +	event_lock = SDL_CreateMutex();
    2.19 +#endif /* LOCK_DGA_DISPLAY */
    2.20 +
    2.21  	/* We're done! */
    2.22  	return(0);
    2.23  }
    2.24 @@ -535,7 +540,7 @@
    2.25  	if ( surfaces_len < 0 ) {
    2.26  		surfaces_len = 0;
    2.27  	}
    2.28 -	DGA_InitHWSurfaces(this, surfaces_mem, surfaces_len);
    2.29 +	DGA_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
    2.30  
    2.31  	/* Set the update rectangle function */
    2.32  	this->UpdateRects = DGA_DirectUpdate;
    2.33 @@ -581,15 +586,36 @@
    2.34  }
    2.35  #endif
    2.36  
    2.37 -static int DGA_InitHWSurfaces(_THIS, Uint8 *base, int size)
    2.38 +static int DGA_InitHWSurfaces(_THIS, SDL_Surface *screen, Uint8 *base, int size)
    2.39  {
    2.40 -	surfaces.prev = NULL;
    2.41 -	surfaces.used = 0;
    2.42 -	surfaces.base = base;
    2.43 -	surfaces.size = size;
    2.44 -	surfaces.next = NULL;
    2.45 +	vidmem_bucket *bucket;
    2.46 +
    2.47  	surfaces_memtotal = size;
    2.48  	surfaces_memleft = size;
    2.49 +
    2.50 +	if ( surfaces_memleft > 0 ) {
    2.51 +		bucket = (vidmem_bucket *)malloc(sizeof(*bucket));
    2.52 +		if ( bucket == NULL ) {
    2.53 +			SDL_OutOfMemory();
    2.54 +			return(-1);
    2.55 +		}
    2.56 +		bucket->prev = &surfaces;
    2.57 +		bucket->used = 0;
    2.58 +		bucket->dirty = 0;
    2.59 +		bucket->base = base;
    2.60 +		bucket->size = size;
    2.61 +		bucket->next = NULL;
    2.62 +	} else {
    2.63 +		bucket = NULL;
    2.64 +	}
    2.65 +
    2.66 +	surfaces.prev = NULL;
    2.67 +	surfaces.used = 1;
    2.68 +	surfaces.dirty = 0;
    2.69 +	surfaces.base = screen->pixels;
    2.70 +	surfaces.size = (unsigned int)((long)base - (long)surfaces.base);
    2.71 +	surfaces.next = bucket;
    2.72 +	screen->hwdata = (struct private_hwdata *)&surfaces;
    2.73  	return(0);
    2.74  }
    2.75  static void DGA_FreeHWSurfaces(_THIS)
    2.76 @@ -605,11 +631,35 @@
    2.77  	surfaces.next = NULL;
    2.78  }
    2.79  
    2.80 +static __inline__ void DGA_AddDirtySurface(SDL_Surface *surface)
    2.81 +{
    2.82 +	((vidmem_bucket *)surface->hwdata)->dirty = 1;
    2.83 +}
    2.84 +
    2.85 +static __inline__ int DGA_IsSurfaceDirty(SDL_Surface *surface)
    2.86 +{
    2.87 +	return ((vidmem_bucket *)surface->hwdata)->dirty;
    2.88 +}
    2.89 +
    2.90 +static __inline__ void DGA_WaitDirtySurfaces(_THIS)
    2.91 +{
    2.92 +	vidmem_bucket *bucket;
    2.93 +
    2.94 +	/* Wait for graphic operations to complete */
    2.95 +	XDGASync(DGA_Display, DGA_Screen);
    2.96 +
    2.97 +	/* Clear all surface dirty bits */
    2.98 +	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
    2.99 +		bucket->dirty = 0;
   2.100 +	}
   2.101 +}
   2.102 +
   2.103  static int DGA_AllocHWSurface(_THIS, SDL_Surface *surface)
   2.104  {
   2.105  	vidmem_bucket *bucket;
   2.106  	int size;
   2.107  	int extra;
   2.108 +	int retval = 0;
   2.109  
   2.110  /* Temporarily, we only allow surfaces the same width as display.
   2.111     Some blitters require the pitch between two hardware surfaces
   2.112 @@ -624,11 +674,13 @@
   2.113  #ifdef DGA_DEBUG
   2.114  	fprintf(stderr, "Allocating bucket of %d bytes\n", size);
   2.115  #endif
   2.116 +	LOCK_DISPLAY();
   2.117  
   2.118  	/* Quick check for available mem */
   2.119  	if ( size > surfaces_memleft ) {
   2.120  		SDL_SetError("Not enough video memory");
   2.121 -		return(-1);
   2.122 +		retval = -1;
   2.123 +		goto done;
   2.124  	}
   2.125  
   2.126  	/* Search for an empty bucket big enough */
   2.127 @@ -639,7 +691,8 @@
   2.128  	}
   2.129  	if ( bucket == NULL ) {
   2.130  		SDL_SetError("Video memory too fragmented");
   2.131 -		return(-1);
   2.132 +		retval = -1;
   2.133 +		goto done;
   2.134  	}
   2.135  
   2.136  	/* Create a new bucket for left-over memory */
   2.137 @@ -653,7 +706,8 @@
   2.138  		newbucket = (vidmem_bucket *)malloc(sizeof(*newbucket));
   2.139  		if ( newbucket == NULL ) {
   2.140  			SDL_OutOfMemory();
   2.141 -			return(-1);
   2.142 +			retval = -1;
   2.143 +			goto done;
   2.144  		}
   2.145  		newbucket->prev = bucket;
   2.146  		newbucket->used = 0;
   2.147 @@ -669,24 +723,24 @@
   2.148  	/* Set the current bucket values and return it! */
   2.149  	bucket->used = 1;
   2.150  	bucket->size = size;
   2.151 +	bucket->dirty = 0;
   2.152  #ifdef DGA_DEBUG
   2.153  	fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
   2.154  #endif
   2.155  	surfaces_memleft -= size;
   2.156  	surface->flags |= SDL_HWSURFACE;
   2.157  	surface->pixels = bucket->base;
   2.158 -	return(0);
   2.159 +	surface->hwdata = (struct private_hwdata *)bucket;
   2.160 +done:
   2.161 +	UNLOCK_DISPLAY();
   2.162 +	return(retval);
   2.163  }
   2.164  static void DGA_FreeHWSurface(_THIS, SDL_Surface *surface)
   2.165  {
   2.166  	vidmem_bucket *bucket, *freeable;
   2.167  
   2.168  	/* Look for the bucket in the current list */
   2.169 -	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   2.170 -		if ( bucket->base == (Uint8 *)surface->pixels ) {
   2.171 -			break;
   2.172 -		}
   2.173 -	}
   2.174 +	bucket = (vidmem_bucket *)surface->hwdata;
   2.175  	if ( (bucket == NULL) || ! bucket->used ) {
   2.176  		return;
   2.177  	}
   2.178 @@ -724,6 +778,7 @@
   2.179  		free(freeable);
   2.180  	}
   2.181  	surface->pixels = NULL;
   2.182 +	surface->hwdata = NULL;
   2.183  }
   2.184  
   2.185  static __inline__ void dst_to_xy(_THIS, SDL_Surface *dst, int *x, int *y)
   2.186 @@ -742,6 +797,7 @@
   2.187  	unsigned int w, h;
   2.188  
   2.189  	/* Don't fill the visible part of the screen, wait until flipped */
   2.190 +	LOCK_DISPLAY();
   2.191  	if ( was_flipped && (dst == this->screen) ) {
   2.192  		while ( XDGAGetViewportStatus(DGA_Display, DGA_Screen) )
   2.193  			/* Keep waiting for the hardware ... */ ;
   2.194 @@ -756,8 +812,9 @@
   2.195    printf("Hardware accelerated rectangle fill: %dx%d at %d,%d\n", w, h, x, y);
   2.196  #endif
   2.197  	XDGAFillRectangle(DGA_Display, DGA_Screen, x, y, w, h, color);
   2.198 -	sync_needed++;
   2.199  	XFlush(DGA_Display);
   2.200 +	DGA_AddDirtySurface(dst);
   2.201 +	UNLOCK_DISPLAY();
   2.202  	return(0);
   2.203  }
   2.204  
   2.205 @@ -771,6 +828,7 @@
   2.206  
   2.207  	this = current_video;
   2.208  	/* Don't blit to the visible part of the screen, wait until flipped */
   2.209 +	LOCK_DISPLAY();
   2.210  	if ( was_flipped && (dst == this->screen) ) {
   2.211  		while ( XDGAGetViewportStatus(DGA_Display, DGA_Screen) )
   2.212  			/* Keep waiting for the hardware ... */ ;
   2.213 @@ -794,8 +852,10 @@
   2.214  		XDGACopyArea(DGA_Display, DGA_Screen,
   2.215  			srcx, srcy, w, h, dstx, dsty);
   2.216  	}
   2.217 -	sync_needed++;
   2.218  	XFlush(DGA_Display);
   2.219 +	DGA_AddDirtySurface(src);
   2.220 +	DGA_AddDirtySurface(dst);
   2.221 +	UNLOCK_DISPLAY();
   2.222  	return(0);
   2.223  }
   2.224  
   2.225 @@ -826,12 +886,8 @@
   2.226  	return(accelerated);
   2.227  }
   2.228  
   2.229 -static __inline__ void DGA_WaitHardware(_THIS)
   2.230 +static __inline__ void DGA_WaitFlip(_THIS)
   2.231  {
   2.232 -	if ( sync_needed ) {
   2.233 -		XDGASync(DGA_Display, DGA_Screen);
   2.234 -		sync_needed = 0;
   2.235 -	}
   2.236  	if ( was_flipped ) {
   2.237  		while ( XDGAGetViewportStatus(DGA_Display, DGA_Screen) )
   2.238  			/* Keep waiting for the hardware ... */ ;
   2.239 @@ -841,15 +897,26 @@
   2.240  
   2.241  static int DGA_LockHWSurface(_THIS, SDL_Surface *surface)
   2.242  {
   2.243 -	if ( surface == SDL_VideoSurface ) {
   2.244 +	if ( surface == this->screen ) {
   2.245  		SDL_mutexP(hw_lock);
   2.246 -		DGA_WaitHardware(this);
   2.247 +		LOCK_DISPLAY();
   2.248 +		if ( DGA_IsSurfaceDirty(surface) ) {
   2.249 +			DGA_WaitDirtySurfaces(this);
   2.250 +		}
   2.251 +		DGA_WaitFlip(this);
   2.252 +		UNLOCK_DISPLAY();
   2.253 +	} else {
   2.254 +		if ( DGA_IsSurfaceDirty(surface) ) {
   2.255 +			LOCK_DISPLAY();
   2.256 +			DGA_WaitDirtySurfaces(this);
   2.257 +			UNLOCK_DISPLAY();
   2.258 +		}
   2.259  	}
   2.260  	return(0);
   2.261  }
   2.262  static void DGA_UnlockHWSurface(_THIS, SDL_Surface *surface)
   2.263  {
   2.264 -	if ( surface == SDL_VideoSurface ) {
   2.265 +	if ( surface == this->screen ) {
   2.266  		SDL_mutexV(hw_lock);
   2.267  	}
   2.268  }
   2.269 @@ -857,10 +924,15 @@
   2.270  static int DGA_FlipHWSurface(_THIS, SDL_Surface *surface)
   2.271  {
   2.272  	/* Wait for vertical retrace and then flip display */
   2.273 -	DGA_WaitHardware(this);
   2.274 +	LOCK_DISPLAY();
   2.275 +	if ( DGA_IsSurfaceDirty(this->screen) ) {
   2.276 +		DGA_WaitDirtySurfaces(this);
   2.277 +	}
   2.278 +	DGA_WaitFlip(this);
   2.279  	XDGASetViewport(DGA_Display, DGA_Screen,
   2.280  	                0, flip_yoffset[flip_page], XDGAFlipRetrace);
   2.281  	XFlush(DGA_Display);
   2.282 +	UNLOCK_DISPLAY();
   2.283  	was_flipped = 1;
   2.284  	flip_page = !flip_page;
   2.285  
   2.286 @@ -891,8 +963,10 @@
   2.287  		xcmap[i].blue  = (colors[i].b<<8)|colors[i].b;
   2.288  		xcmap[i].flags = (DoRed|DoGreen|DoBlue);
   2.289  	}
   2.290 +	LOCK_DISPLAY();
   2.291  	XStoreColors(DGA_Display, DGA_colormap, xcmap, ncolors);
   2.292  	XSync(DGA_Display, False);
   2.293 +	UNLOCK_DISPLAY();
   2.294  
   2.295  	/* That was easy. :) */
   2.296  	return(1);
   2.297 @@ -923,8 +997,10 @@
   2.298  		xcmap[i].blue  = ramp[2*256+c];
   2.299  		xcmap[i].flags = (DoRed|DoGreen|DoBlue);
   2.300  	}
   2.301 +	LOCK_DISPLAY();
   2.302  	XStoreColors(DGA_Display, DGA_colormap, xcmap, ncolors);
   2.303  	XSync(DGA_Display, False);
   2.304 +	UNLOCK_DISPLAY();
   2.305  	return(0);
   2.306  }
   2.307  
   2.308 @@ -952,6 +1028,13 @@
   2.309  			SDL_DestroyMutex(hw_lock);
   2.310  			hw_lock = NULL;
   2.311  		}
   2.312 +#ifdef LOCK_DGA_DISPLAY
   2.313 +		if ( event_lock != NULL ) {
   2.314 +			SDL_DestroyMutex(event_lock);
   2.315 +			event_lock = NULL;
   2.316 +		}
   2.317 +#endif /* LOCK_DGA_DISPLAY */
   2.318 +
   2.319  
   2.320  		/* Clean up defined video modes */
   2.321  		for ( i=0; i<NUM_MODELISTS; ++i ) {
     3.1 --- a/src/video/dga/SDL_dgavideo.h	Wed Jul 11 20:18:52 2001 +0000
     3.2 +++ b/src/video/dga/SDL_dgavideo.h	Thu Jul 12 20:42:22 2001 +0000
     3.3 @@ -37,11 +37,22 @@
     3.4  /* Hidden "this" pointer for the video functions */
     3.5  #define _THIS	SDL_VideoDevice *this
     3.6  
     3.7 +/* Define this if you need the DGA driver to be thread-safe */
     3.8 +#define LOCK_DGA_DISPLAY
     3.9 +#ifdef LOCK_DGA_DISPLAY
    3.10 +#define LOCK_DISPLAY()		SDL_mutexP(event_lock)
    3.11 +#define UNLOCK_DISPLAY()	SDL_mutexV(event_lock)
    3.12 +#else
    3.13 +#define LOCK_DISPLAY()
    3.14 +#define UNLOCK_DISPLAY()
    3.15 +#endif
    3.16 +
    3.17  
    3.18  /* This is the structure we use to keep track of video memory */
    3.19  typedef struct vidmem_bucket {
    3.20  	struct vidmem_bucket *prev;
    3.21 -	unsigned int used;
    3.22 +	int used;
    3.23 +	int dirty;
    3.24  	Uint8 *base;
    3.25  	unsigned int size;
    3.26  	struct vidmem_bucket *next;
    3.27 @@ -82,6 +93,9 @@
    3.28  
    3.29  	/* Used to handle DGA events */
    3.30  	int event_base;
    3.31 +#ifdef LOCK_DGA_DISPLAY
    3.32 +	SDL_mutex *event_lock;
    3.33 +#endif
    3.34  };
    3.35  /* Old variable names */
    3.36  #define DGA_Display		(this->hidden->DGA_Display)
    3.37 @@ -102,5 +116,6 @@
    3.38  #define surfaces_memleft	(this->hidden->surfaces_memleft)
    3.39  #define hw_lock			(this->hidden->hw_lock)
    3.40  #define DGA_event_base		(this->hidden->event_base)
    3.41 +#define event_lock		(this->hidden->event_lock)
    3.42  
    3.43  #endif /* _SDL_dgavideo_h */