Merged DGA video surface handling improvements, unified locking code.
authorSam Lantinga <slouken@lokigames.com>
Fri, 13 Jul 2001 10:19:51 +0000
changeset 106 63ec24e0575f
parent 105 2136ea8953f9
child 107 01fcac5d146e
Merged DGA video surface handling improvements, unified locking code. Fixed matrox blit bug where src Y less than dst Y Fixed hardware surface init when no resolution change
src/video/fbcon/SDL_fb3dfx.c
src/video/fbcon/SDL_fbmatrox.c
src/video/fbcon/SDL_fbvideo.c
src/video/fbcon/SDL_fbvideo.h
--- a/src/video/fbcon/SDL_fb3dfx.c	Fri Jul 13 10:15:52 2001 +0000
+++ b/src/video/fbcon/SDL_fb3dfx.c	Fri Jul 13 10:19:51 2001 +0000
@@ -32,18 +32,6 @@
 #include "3dfx_mmio.h"
 
 
-static int LockHWSurface(_THIS, SDL_Surface *surface)
-{
-	if ( surface == SDL_VideoSurface ) {
-		tdfx_waitidle();
-	}
-	return(0);
-}
-static void UnlockHWSurface(_THIS, SDL_Surface *surface)
-{
-	return;
-}
-
 /* Wait for vertical retrace */
 static void WaitVBL(_THIS)
 {
@@ -55,6 +43,10 @@
 	while( (tdfx_in32(TDFX_STATUS) & STATUS_RETRACE) == 0 )
 		; 
 }
+static void WaitIdle(_THIS)
+{
+	tdfx_waitidle();
+}
 
 /* Sets video mem colorkey and accelerated blit function */
 static int SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key)
@@ -86,6 +78,9 @@
 	tdfx_out32(COMMAND_2D, COMMAND_2D_FILLRECT);
 	tdfx_out32(DSTSIZE, rect->w | (rect->h << 16));
 	tdfx_out32(LAUNCH_2D, dstX | (dstY << 16));
+
+	FB_AddBusySurface(dst);
+
 	return(0);
 }
 
@@ -151,6 +146,9 @@
 	tdfx_out32(DSTXY, dstX | (dstY << 16));
 	tdfx_out32(LAUNCH_2D, srcX | (srcY << 16));
 
+	FB_AddBusySurface(src);
+	FB_AddBusySurface(dst);
+
 	return(0);
 }
 
@@ -185,9 +183,8 @@
 {
 	/* We have hardware accelerated surface functions */
 	this->CheckHWBlit = CheckHWBlit;
-	this->LockHWSurface = LockHWSurface;
-	this->UnlockHWSurface = UnlockHWSurface;
 	wait_vbl = WaitVBL;
+	wait_idle = WaitIdle;
 
 	/* Reset the 3Dfx controller */
 	tdfx_out32(BRESERROR0, 0);
--- a/src/video/fbcon/SDL_fbmatrox.c	Fri Jul 13 10:15:52 2001 +0000
+++ b/src/video/fbcon/SDL_fbmatrox.c	Fri Jul 13 10:19:51 2001 +0000
@@ -32,18 +32,6 @@
 #include "matrox_mmio.h"
 
 
-static int LockHWSurface(_THIS, SDL_Surface *surface)
-{
-	if ( surface == SDL_VideoSurface ) {
-		mga_waitidle();
-	}
-	return(0);
-}
-static void UnlockHWSurface(_THIS, SDL_Surface *surface)
-{
-	return;
-}
-
 /* Wait for vertical retrace - taken from the XFree86 Matrox driver */
 static void WaitVBL(_THIS)
 {
@@ -60,6 +48,10 @@
 	while ( mga_in32(0x1E20) < count )
 		;
 }
+static void WaitIdle(_THIS)
+{
+	mga_waitidle();
+}
 
 /* Sets video mem colorkey and accelerated blit function */
 static int SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key)
@@ -91,8 +83,7 @@
 	}
 
 	/* Set up the X/Y base coordinates */
-	dstX = 0;
-	dstY = ((char *)dst->pixels - mapped_mem) / SDL_VideoSurface->pitch;
+	FB_dst_to_xy(this, dst, &dstX, &dstY);
 
 	/* Adjust for the current rectangle */
 	dstX += rect->x;
@@ -104,19 +95,6 @@
 	/* Set up the Y boundaries */
 	ydstlen = (rect->h | (dstY << 16));
 
-#if 0	/* This old way doesn't work on the Matrox G450 */
-	/* Set up for color fill operation */
-	fillop = MGADWG_TRAP | MGADWG_SOLID |
-	         MGADWG_ARZERO | MGADWG_SGNZERO | MGADWG_SHIFTZERO |
-	         MGADWG_BFCOL | MGADWG_BLK;
-
-	/* Execute the operations! */
-	mga_wait(4);
-	mga_out32(MGAREG_FCOL, color);
-	mga_out32(MGAREG_FXBNDRY, fxbndry);
-	mga_out32(MGAREG_YDSTLEN, ydstlen);
-	mga_out32(MGAREG_DWGCTL + MGAREG_EXEC, fillop);
-#else
 	/* Set up for color fill operation */
 	fillop = MGADWG_TRAP | MGADWG_SOLID |
 	         MGADWG_ARZERO | MGADWG_SGNZERO | MGADWG_SHIFTZERO;
@@ -127,7 +105,8 @@
 	mga_out32(MGAREG_FCOL, color);
 	mga_out32(MGAREG_FXBNDRY, fxbndry);
 	mga_out32(MGAREG_YDSTLEN + MGAREG_EXEC, ydstlen);
-#endif
+
+	FB_AddBusySurface(dst);
 
 	return(0);
 }
@@ -136,12 +115,12 @@
                        SDL_Surface *dst, SDL_Rect *dstrect)
 {
 	SDL_VideoDevice *this;
-	int bpp;
+	int pitch, w, h;
 	int srcX, srcY;
 	int dstX, dstY;
 	Uint32 sign;
-	Uint32 sstart, sstop;
-	int sskip;
+	Uint32 start, stop;
+	int skip;
 	Uint32 blitop;
 
 	/* FIXME: For now, only blit to display surface */
@@ -151,16 +130,17 @@
 
 	/* Calculate source and destination base coordinates (in pixels) */
 	this = current_video;
-	srcX= 0;	/* FIXME: Calculate this from memory offset */
-	srcY = ((char *)src->pixels - mapped_mem) / SDL_VideoSurface->pitch;
-	dstX = 0;	/* FIXME: Calculate this from memory offset */
-	dstY = ((char *)dst->pixels - mapped_mem) / SDL_VideoSurface->pitch;
+	w = dstrect->w;
+	h = dstrect->h;
+	FB_dst_to_xy(this, src, &srcX, &srcY);
+	FB_dst_to_xy(this, dst, &dstX, &dstY);
 
 	/* Adjust for the current blit rectangles */
 	srcX += srcrect->x;
 	srcY += srcrect->y;
 	dstX += dstrect->x;
 	dstY += dstrect->y;
+	pitch = dst->pitch/dst->format->BytesPerPixel;
 
 	/* Set up the blit direction (sign) flags */
 	sign = 0;
@@ -169,19 +149,21 @@
 	}
 	if ( srcY < dstY ) {
 		sign |= 4;
+		srcY += (h - 1);
+		dstY += (h - 1);
 	}
 
 	/* Set up the blit source row start, end, and skip (in pixels) */
-	bpp = src->format->BytesPerPixel;
-	sstop = sstart = ((srcY * SDL_VideoSurface->pitch)/bpp) + srcX;
+	stop = start = (srcY * pitch) + srcX;
 	if ( srcX < dstX ) {
-		sstart += (dstrect->w - 1);
+		start += (w - 1);
 	} else {
-		sstop += (dstrect->w - 1);
+		stop  += (w - 1);
 	}
-	sskip = src->pitch/bpp;
 	if ( srcY < dstY ) {
-		sskip = -sskip;
+		skip = -pitch;
+	} else {
+		skip = pitch;
 	}
 
 	/* Set up the blit operation */
@@ -209,13 +191,16 @@
 	}
 	mga_wait(7);
 	mga_out32(MGAREG_SGN, sign);
-	mga_out32(MGAREG_AR3, sstart);
-	mga_out32(MGAREG_AR0, sstop);
-	mga_out32(MGAREG_AR5, sskip);
-	mga_out32(MGAREG_FXBNDRY, (dstX | ((dstX + dstrect->w-1) << 16)));
-	mga_out32(MGAREG_YDSTLEN, (dstY << 16) | dstrect->h);
+	mga_out32(MGAREG_AR3, start);
+	mga_out32(MGAREG_AR0, stop);
+	mga_out32(MGAREG_AR5, skip);
+	mga_out32(MGAREG_FXBNDRY, (dstX | ((dstX + w-1) << 16)));
+	mga_out32(MGAREG_YDSTLEN, (dstY << 16) | h);
 	mga_out32(MGAREG_DWGCTL + MGAREG_EXEC, blitop);
 
+	FB_AddBusySurface(src);
+	FB_AddBusySurface(dst);
+
 	return(0);
 }
 
@@ -250,9 +235,8 @@
 {
 	/* We have hardware accelerated surface functions */
 	this->CheckHWBlit = CheckHWBlit;
-	this->LockHWSurface = LockHWSurface;
-	this->UnlockHWSurface = UnlockHWSurface;
 	wait_vbl = WaitVBL;
+	wait_idle = WaitIdle;
 
 	/* The Matrox has an accelerated color fill */
 	this->info.blit_fill = 1;
--- a/src/video/fbcon/SDL_fbvideo.c	Fri Jul 13 10:15:52 2001 +0000
+++ b/src/video/fbcon/SDL_fbvideo.c	Fri Jul 13 10:19:51 2001 +0000
@@ -133,13 +133,14 @@
 static void FB_VideoQuit(_THIS);
 
 /* Hardware surface functions */
-static int FB_InitHWSurfaces(_THIS, char *base, int size);
+static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size);
 static void FB_FreeHWSurfaces(_THIS);
 static int FB_AllocHWSurface(_THIS, SDL_Surface *surface);
 static int FB_LockHWSurface(_THIS, SDL_Surface *surface);
 static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface);
 static void FB_FreeHWSurface(_THIS, SDL_Surface *surface);
 static void FB_WaitVBL(_THIS);
+static void FB_WaitIdle(_THIS);
 static int FB_FlipHWSurface(_THIS, SDL_Surface *surface);
 
 /* Internal palette functions */
@@ -191,6 +192,7 @@
 	}
 	memset(this->hidden, 0, (sizeof *this->hidden));
 	wait_vbl = FB_WaitVBL;
+	wait_idle = FB_WaitIdle;
 	mouse_fd = -1;
 	keyboard_fd = -1;
 
@@ -665,7 +667,7 @@
 	if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) {
 		return(NULL);
 	}
-    current->format->palette->ncolors = 16;
+	current->format->palette->ncolors = 16;
 
 	/* Get the fixed information about the console hardware.
 	   This is necessary since finfo.line_length changes.
@@ -759,6 +761,18 @@
 				return(NULL);
 			}
 		}
+	} else {
+		int maxheight;
+
+		/* Figure out how much video memory is available */
+		if ( flags & SDL_DOUBLEBUF ) {
+			maxheight = height*2;
+		} else {
+			maxheight = height;
+		}
+		if ( vinfo.yres_virtual > maxheight ) {
+			vinfo.yres_virtual = maxheight;
+		}
 	}
 	cache_vinfo = vinfo;
 #ifdef FBCON_DEBUG
@@ -803,6 +817,13 @@
 	current->pitch = finfo.line_length;
 	current->pixels = mapped_mem+mapped_offset;
 
+	/* Set up the information for hardware surfaces */
+	surfaces_mem = (char *)current->pixels +
+	                        vinfo.yres_virtual*current->pitch;
+	surfaces_len = (mapped_memlen-(surfaces_mem-mapped_mem));
+	FB_FreeHWSurfaces(this);
+	FB_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
+
 	/* Let the application know we have a hardware palette */
 	switch (finfo.visual) {
 	    case FB_VISUAL_PSEUDOCOLOR:
@@ -820,17 +841,12 @@
 			flip_address[0] = (char *)current->pixels;
 			flip_address[1] = (char *)current->pixels+
 			                          current->h*current->pitch;
+			this->screen = current;
 			FB_FlipHWSurface(this, current);
+			this->screen = NULL;
 		}
 	}
 
-	/* Set up the information for hardware surfaces */
-	surfaces_mem = (char *)current->pixels +
-	                        vinfo.yres_virtual*current->pitch;
-	surfaces_len = (mapped_memlen-(surfaces_mem-mapped_mem));
-	FB_FreeHWSurfaces(this);
-	FB_InitHWSurfaces(this, surfaces_mem, surfaces_len);
-
 	/* Set the update rectangle function */
 	this->UpdateRects = FB_DirectUpdate;
 
@@ -867,15 +883,36 @@
 }
 #endif
 
-static int FB_InitHWSurfaces(_THIS, char *base, int size)
+static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size)
 {
-	surfaces.prev = NULL;
-	surfaces.used = 0;
-	surfaces.base = base;
-	surfaces.size = size;
-	surfaces.next = NULL;
+	vidmem_bucket *bucket;
+
 	surfaces_memtotal = size;
 	surfaces_memleft = size;
+
+	if ( surfaces_memleft > 0 ) {
+		bucket = (vidmem_bucket *)malloc(sizeof(*bucket));
+		if ( bucket == NULL ) {
+			SDL_OutOfMemory();
+			return(-1);
+		}
+		bucket->prev = &surfaces;
+		bucket->used = 0;
+		bucket->dirty = 0;
+		bucket->base = base;
+		bucket->size = size;
+		bucket->next = NULL;
+	} else {
+		bucket = NULL;
+	}
+
+	surfaces.prev = NULL;
+	surfaces.used = 1;
+	surfaces.dirty = 0;
+	surfaces.base = screen->pixels;
+	surfaces.size = (unsigned int)((long)base - (long)surfaces.base);
+	surfaces.next = bucket;
+	screen->hwdata = (struct private_hwdata *)&surfaces;
 	return(0);
 }
 static void FB_FreeHWSurfaces(_THIS)
@@ -956,12 +993,14 @@
 	/* Set the current bucket values and return it! */
 	bucket->used = 1;
 	bucket->size = size;
+	bucket->dirty = 0;
 #ifdef FBCON_DEBUG
 	fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
 #endif
 	surfaces_memleft -= size;
 	surface->flags |= SDL_HWSURFACE;
 	surface->pixels = bucket->base;
+	surface->hwdata = (struct private_hwdata *)bucket;
 	return(0);
 }
 static void FB_FreeHWSurface(_THIS, SDL_Surface *surface)
@@ -970,58 +1009,64 @@
 
 	/* Look for the bucket in the current list */
 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
-		if ( bucket->base == (char *)surface->pixels ) {
+		if ( bucket == (vidmem_bucket *)surface->hwdata ) {
 			break;
 		}
 	}
-	if ( (bucket == NULL) || ! bucket->used ) {
-		return;
-	}
-
-	/* Add the memory back to the total */
-#ifdef FBCON_DEBUG
+	if ( bucket && bucket->used ) {
+		/* Add the memory back to the total */
+#ifdef DGA_DEBUG
 	printf("Freeing bucket of %d bytes\n", bucket->size);
 #endif
-	surfaces_memleft += bucket->size;
+		surfaces_memleft += bucket->size;
 
-	/* Can we merge the space with surrounding buckets? */
-	bucket->used = 0;
-	if ( bucket->next && ! bucket->next->used ) {
-#ifdef FBCON_DEBUG
+		/* Can we merge the space with surrounding buckets? */
+		bucket->used = 0;
+		if ( bucket->next && ! bucket->next->used ) {
+#ifdef DGA_DEBUG
 	printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size);
 #endif
-		freeable = bucket->next;
-		bucket->size += bucket->next->size;
-		bucket->next = bucket->next->next;
-		if ( bucket->next ) {
-			bucket->next->prev = bucket;
+			freeable = bucket->next;
+			bucket->size += bucket->next->size;
+			bucket->next = bucket->next->next;
+			if ( bucket->next ) {
+				bucket->next->prev = bucket;
+			}
+			free(freeable);
 		}
-		free(freeable);
-	}
-	if ( bucket->prev && ! bucket->prev->used ) {
-#ifdef FBCON_DEBUG
+		if ( bucket->prev && ! bucket->prev->used ) {
+#ifdef DGA_DEBUG
 	printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size);
 #endif
-		freeable = bucket;
-		bucket->prev->size += bucket->size;
-		bucket->prev->next = bucket->next;
-		if ( bucket->next ) {
-			bucket->next->prev = bucket->prev;
+			freeable = bucket;
+			bucket->prev->size += bucket->size;
+			bucket->prev->next = bucket->next;
+			if ( bucket->next ) {
+				bucket->next->prev = bucket->prev;
+			}
+			free(freeable);
 		}
-		free(freeable);
 	}
 	surface->pixels = NULL;
+	surface->hwdata = NULL;
 }
 static int FB_LockHWSurface(_THIS, SDL_Surface *surface)
 {
-	if ( surface == SDL_VideoSurface ) {
+	if ( surface == this->screen ) {
 		SDL_mutexP(hw_lock);
+		if ( FB_IsSurfaceBusy(surface) ) {
+			FB_WaitBusySurfaces(this);
+		}
+	} else {
+		if ( FB_IsSurfaceBusy(surface) ) {
+			FB_WaitBusySurfaces(this);
+		}
 	}
 	return(0);
 }
 static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
 {
-	if ( surface == SDL_VideoSurface ) {
+	if ( surface == this->screen ) {
 		SDL_mutexV(hw_lock);
 	}
 }
@@ -1034,10 +1079,18 @@
 	return;
 }
 
+static void FB_WaitIdle(_THIS)
+{
+	return;
+}
+
 static int FB_FlipHWSurface(_THIS, SDL_Surface *surface)
 {
 	/* Wait for vertical retrace and then flip display */
 	cache_vinfo.yoffset = flip_page*surface->h;
+	if ( FB_IsSurfaceBusy(this->screen) ) {
+		FB_WaitBusySurfaces(this);
+	}
 	wait_vbl(this);
 	if ( ioctl(console_fd, FBIOPAN_DISPLAY, &cache_vinfo) < 0 ) {
 		SDL_SetError("ioctl(FBIOPAN_DISPLAY) failed");
--- a/src/video/fbcon/SDL_fbvideo.h	Fri Jul 13 10:15:52 2001 +0000
+++ b/src/video/fbcon/SDL_fbvideo.h	Fri Jul 13 10:19:51 2001 +0000
@@ -43,18 +43,13 @@
 /* This is the structure we use to keep track of video memory */
 typedef struct vidmem_bucket {
 	struct vidmem_bucket *prev;
-	unsigned int used;
+	int used;
+	int dirty;
 	char *base;
 	unsigned int size;
 	struct vidmem_bucket *next;
 } vidmem_bucket;
 
-/* Information about the location of the surface in hardware memory */
-struct private_hwdata {
-	int x;
-	int y;
-};
-
 /* Private display data */
 struct SDL_PrivateVideoData {
 	int console_fd;
@@ -90,6 +85,7 @@
 	SDL_mutex *hw_lock;
 
 	void (*wait_vbl)(_THIS);
+	void (*wait_idle)(_THIS);
 };
 /* Old variable names */
 #define console_fd		(this->hidden->console_fd)
@@ -117,6 +113,7 @@
 #define surfaces_memleft	(this->hidden->surfaces_memleft)
 #define hw_lock			(this->hidden->hw_lock)
 #define wait_vbl		(this->hidden->wait_vbl)
+#define wait_idle		(this->hidden->wait_idle)
 
 /* Accelerator types that are supported by the driver, but are not
    necessarily in the kernel headers on the system we compile on.
@@ -132,4 +129,39 @@
 extern void FB_SavePaletteTo(_THIS, int palette_len, __u16 *area);
 extern void FB_RestorePaletteFrom(_THIS, int palette_len, __u16 *area);
 
+/* These are utility functions for working with video surfaces */
+
+static __inline__ void FB_AddBusySurface(SDL_Surface *surface)
+{
+	((vidmem_bucket *)surface->hwdata)->dirty = 1;
+}
+
+static __inline__ int FB_IsSurfaceBusy(SDL_Surface *surface)
+{
+	return ((vidmem_bucket *)surface->hwdata)->dirty;
+}
+
+static __inline__ void FB_WaitBusySurfaces(_THIS)
+{
+	vidmem_bucket *bucket;
+
+	/* Wait for graphic operations to complete */
+	wait_idle(this);
+
+	/* Clear all surface dirty bits */
+	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
+		bucket->dirty = 0;
+	}
+}
+
+static __inline__ void FB_dst_to_xy(_THIS, SDL_Surface *dst, int *x, int *y)
+{
+	*x = (long)((char *)dst->pixels - mapped_mem)%this->screen->pitch;
+	*y = (long)((char *)dst->pixels - mapped_mem)/this->screen->pitch;
+	if ( dst == this->screen ) {
+		*x += this->offset_x;
+		*y += this->offset_y;
+	}
+}
+
 #endif /* _SDL_fbvideo_h */