From 79bd9f07c6363669450857ee75bc8487a87af3fe Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 13 Jul 2001 10:19:51 +0000 Subject: [PATCH] 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 | 25 +++--- src/video/fbcon/SDL_fbmatrox.c | 80 ++++++++----------- src/video/fbcon/SDL_fbvideo.c | 141 +++++++++++++++++++++++---------- src/video/fbcon/SDL_fbvideo.h | 46 +++++++++-- 4 files changed, 179 insertions(+), 113 deletions(-) diff --git a/src/video/fbcon/SDL_fb3dfx.c b/src/video/fbcon/SDL_fb3dfx.c index 9071ea753..353bada2d 100644 --- a/src/video/fbcon/SDL_fb3dfx.c +++ b/src/video/fbcon/SDL_fb3dfx.c @@ -32,18 +32,6 @@ static char rcsid = #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 @@ static void WaitVBL(_THIS) 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 @@ static int FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) 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 @@ static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, 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 @@ void FB_3DfxAccel(_THIS, __u32 card) { /* 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); diff --git a/src/video/fbcon/SDL_fbmatrox.c b/src/video/fbcon/SDL_fbmatrox.c index 67e547054..92f766875 100644 --- a/src/video/fbcon/SDL_fbmatrox.c +++ b/src/video/fbcon/SDL_fbmatrox.c @@ -32,18 +32,6 @@ static char rcsid = #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 @@ static void WaitVBL(_THIS) 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 @@ static int FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) } /* 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 @@ static int FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) /* 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 @@ static int FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) 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 @@ static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, 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 @@ static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, /* 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 @@ static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, } 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 @@ static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, } 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 @@ void FB_MatroxAccel(_THIS, __u32 card) { /* 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; diff --git a/src/video/fbcon/SDL_fbvideo.c b/src/video/fbcon/SDL_fbvideo.c index 04a8ed1a5..802a93a0a 100644 --- a/src/video/fbcon/SDL_fbvideo.c +++ b/src/video/fbcon/SDL_fbvideo.c @@ -133,13 +133,14 @@ static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors); 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 @@ static SDL_VideoDevice *FB_CreateDevice(int devindex) } 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 @@ static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, 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 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, 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 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, 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 @@ static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, 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 @@ void FB_DumpHWSurfaces(_THIS) } #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 @@ surface->pitch = SDL_VideoSurface->pitch; /* 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 @@ static void FB_FreeHWSurface(_THIS, SDL_Surface *surface) /* 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 @@ static void FB_WaitVBL(_THIS) 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"); diff --git a/src/video/fbcon/SDL_fbvideo.h b/src/video/fbcon/SDL_fbvideo.h index 8f2b65e0a..b14f53c56 100644 --- a/src/video/fbcon/SDL_fbvideo.h +++ b/src/video/fbcon/SDL_fbvideo.h @@ -43,18 +43,13 @@ static char rcsid = /* 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 @@ struct SDL_PrivateVideoData { 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 @@ struct SDL_PrivateVideoData { #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 @@ struct SDL_PrivateVideoData { 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 */