From 8e6ada7bc33e3cc4e1c17821ea171bf0815a505d Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Tue, 1 Dec 2009 19:31:57 -0500 Subject: [PATCH] SDL GC hack --- configure.in | 17 ++ include/SDL_config.h.in | 1 + src/video/fbcon/SDL_fbgc.c | 471 +++++++++++++++++++++++++++++++++++++++++ src/video/fbcon/SDL_fbgc.h | 35 +++ src/video/fbcon/SDL_fbvideo.c | 10 + src/video/fbcon/SDL_fbvideo.h | 11 + 6 files changed, 545 insertions(+), 0 deletions(-) create mode 100644 src/video/fbcon/SDL_fbgc.c create mode 100644 src/video/fbcon/SDL_fbgc.h diff --git a/configure.in b/configure.in index a7e9b18..a8961ba 100644 --- a/configure.in +++ b/configure.in @@ -1227,6 +1227,22 @@ AC_HELP_STRING([--enable-video-fbcon], [use framebuffer console video driver [[d fi } +dnl See if we're running on Linux for the Nintendo GameCube/Wii +dnl FIXME, perform a real test here... +CheckGC() +{ + AC_ARG_ENABLE(video-gc, +AC_HELP_STRING([--enable-video-gc], [enable GameCube video support in FB [[default=no]]]), + , enable_video_gc=no) + if test x$enable_video = xyes -a x$enable_video_gc = xyes -a x$video_fbcon = xyes; then + video_gc=yes + AC_MSG_RESULT($video_gc) + if test x$video_gc = xyes; then + AC_DEFINE(SDL_VIDEO_DRIVER_GC) + fi + fi +} + dnl Find DirectFB CheckDirectFB() { @@ -2322,6 +2338,7 @@ case "$host" in CheckX11 CheckNANOX CheckFBCON + CheckGC CheckDirectFB CheckPS2GS CheckPS3 diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index 58593ca..e523e9b 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -262,6 +262,7 @@ #undef SDL_VIDEO_DRIVER_DUMMY #undef SDL_VIDEO_DRIVER_FBCON #undef SDL_VIDEO_DRIVER_GAPI +#undef SDL_VIDEO_DRIVER_GC #undef SDL_VIDEO_DRIVER_GEM #undef SDL_VIDEO_DRIVER_GGI #undef SDL_VIDEO_DRIVER_IPOD diff --git a/src/video/fbcon/SDL_fbgc.c b/src/video/fbcon/SDL_fbgc.c new file mode 100644 index 0000000..b3b72bb --- /dev/null +++ b/src/video/fbcon/SDL_fbgc.c @@ -0,0 +1,471 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifdef SDL_VIDEO_DRIVER_GC +#include +#include "SDL_video.h" +#include "../SDL_blit.h" +#include "SDL_fbgc.h" + +static Uint32 r_Yr[256]; +static Uint32 g_Yg_[256]; +static Uint32 b_Yb[256]; +static Uint32 r_Ur[256]; +static Uint32 g_Ug_[256]; +static Uint32 b_Ub[256]; +/* static Uint32 r_Vr[256]; // space and cache optimisation */ +#define r_Vr b_Ub +static Uint32 g_Vg_[256]; +static Uint32 b_Vb[256]; + +static Uint8 RGB16toY[1 << 16]; +static Uint8 RGB16toU[1 << 16]; +static Uint8 RGB16toV[1 << 16]; + +#ifndef FBIOFLIPHACK +#define FBIOFLIPHACK 0x4623 /* gc-linux */ +#endif + +#ifndef GC_BLACK +#define GC_BLACK 0x00800080 +#endif + +#ifdef GC_DEBUG +# define GC_DPRINTF(fmt, args...) \ + fprintf(stderr,"DDD|%s: " fmt, __FUNCTION__ , ## args) +#else +# define GC_DPRINTF(fmt, args...) +#endif + +SDL_bool GC_Test(_THIS) +{ + int fliptest; + if (ioctl(console_fd, FBIOFLIPHACK, &fliptest)) + return SDL_TRUE; + return SDL_FALSE; +} + +SDL_bool GC_Available(void) +{ + if (access("/sys/bus/of_platform/drivers/gcn-vifb", 0) == 0) + return SDL_TRUE; + + return SDL_FALSE; +} + +/* + * + * Color space handling. + */ + +#define RGB2YUV_SHIFT 16 +#define RGB2YUV_LUMA 16 +#define RGB2YUV_CHROMA 128 + +#define Yr ((int)( 0.299*(1< y) ? y : z)) + +static void GC_InitRGB2YUVTables(void) +{ + unsigned int i; + unsigned int r, g, b; + + for (i = 0; i < 256; i++) { + r_Yr[i] = Yr * i; + g_Yg_[i] = Yg * i + (RGB2YUV_LUMA << RGB2YUV_SHIFT); + b_Yb[i] = Yb * i; + r_Ur[i] = Ur * i; + g_Ug_[i] = Ug * i + (RGB2YUV_CHROMA << RGB2YUV_SHIFT); + b_Ub[i] = Ub * i; + r_Vr[i] = Vr * i; + g_Vg_[i] = Vg * i + (RGB2YUV_CHROMA << RGB2YUV_SHIFT); + b_Vb[i] = Vb * i; + } + + for (i = 0; i < 1 << 16; i++) { + /* RGB565 */ + r = ((i >> 8) & 0xf8); + g = ((i >> 3) & 0xfc); + b = ((i << 3) & 0xf8); + /* extend to 8bit */ + r |= (r >> 5); + g |= (g >> 6); + b |= (b >> 5); + + RGB16toY[i] = + clamp(16, 235, + (r_Yr[r] + g_Yg_[g] + b_Yb[b]) >> RGB2YUV_SHIFT); + RGB16toU[i] = + clamp(16, 240, + (r_Ur[r] + g_Ug_[g] + b_Ub[b]) >> RGB2YUV_SHIFT); + RGB16toV[i] = + clamp(16, 240, + (r_Vr[r] + g_Vg_[g] + b_Vb[b]) >> RGB2YUV_SHIFT); + } +} + +static inline Uint32 rgbrgb16toyuy2(Uint16 rgb1, Uint16 rgb2) +{ + register int Y1, Cb, Y2, Cr; + Uint16 rgb; + + /* fast path, thanks to bohdy */ + if (!(rgb1 | rgb2)) { + return GC_BLACK; /* black, black */ + } + + if (rgb1 == rgb2) { + /* fast path, thanks to isobel */ + Y1 = Y2 = RGB16toY[rgb1]; + Cb = RGB16toU[rgb1]; + Cr = RGB16toV[rgb1]; + } else { + Y1 = RGB16toY[rgb1]; + Y2 = RGB16toY[rgb2]; + + /* RGB565 average */ + rgb = ((rgb1 >> 1) & 0xFBEF) + ((rgb2 >> 1) & 0xFBEF) + + ((rgb1 & rgb2) & 0x0821); + + Cb = RGB16toU[rgb]; + Cr = RGB16toV[rgb]; + } + + return (((char)Y1) << 24) | (((char)Cb) << 16) | (((char)Y2) << 8) + | (((char)Cr) << 0); +} + +/* + * + * Blitters. + */ +static void GC_UpdateRectRGB16(_THIS, SDL_Rect * rect, int pitch) +{ + int width, height, left, i, mod, mod32; + Uint8 *src, *dst; + Uint32 *src32, *dst32; + Uint16 *rgb; + + /* XXX case width < 2 needs special treatment */ + + /* in pixel units */ + left = rect->x & ~1; /* 2 pixel align */ + width = (rect->w + 1) & ~1; /* 2 pixel align in excess */ + height = rect->h; + + /* in bytes, src and dest are 16bpp */ + src = shadow_mem + (rect->y * pitch) + left * 2; + dst = flip_address[back_page] + page_offset + + (rect->y * pitch) + left * 2; + mod = pitch - width * 2; + + src32 = (Uint32 *) src; + dst32 = (Uint32 *) dst; + mod32 = mod / 4; + + while (height--) { + i = width / 2; + + while (i--) { + rgb = (Uint16 *) src32; + *dst32++ = rgbrgb16toyuy2(rgb[0], rgb[1]); + src32++; + } + src32 += mod32; + dst32 += mod32; + } +} + +void GC_Init(_THIS, SDL_PixelFormat *vformat) +{ + GC_InitRGB2YUVTables(); + + /* 16 bits per pixel */ + vformat->BitsPerPixel = 16; + vformat->BytesPerPixel = 2; + /* RGB565 */ + vformat->Rmask = 0x0000f800; + vformat->Gmask = 0x000007e0; + vformat->Bmask = 0x0000001f; + + shadow_fb = 1; +} + +/* + * + * Video mode handling. + */ + +/* only 640x480 16bpp is currently supported */ +const static SDL_Rect RECT_640x480 = { 0, 0, 640, 480 }; +const static SDL_Rect *vid_modes[] = { + &RECT_640x480, + NULL +}; + +static SDL_Rect **GC_ListModes(_THIS, SDL_PixelFormat * format, Uint32 flags) +{ + switch (format->BitsPerPixel) { + case 16: + return (SDL_Rect **) vid_modes; + default: + return NULL; + } +} + +SDL_Surface *GC_SetVideoMode(_THIS, SDL_Surface * current, + int width, int height, int bpp, Uint32 flags) +{ + struct fb_fix_screeninfo finfo; + struct fb_var_screeninfo vinfo; + int i; + Uint32 Rmask; + Uint32 Gmask; + Uint32 Bmask; + Uint32 *p, *q; + Uint32 yres; + + GC_DPRINTF("Setting %dx%d %dbpp %smode\n", width, height, bpp, + (flags & SDL_DOUBLEBUF)?"(doublebuf) ":""); + + /* Set the terminal into graphics mode */ + if (FB_EnterGraphicsMode(this) < 0) { + return (NULL); + } + + /* Restore the original palette */ + //FB_RestorePalette(this); + + /* Set the video mode and get the final screen format */ + if (ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) { + SDL_SetError("Couldn't get console screen info"); + return (NULL); + } + + yres = vinfo.yres; + + /* hack to center 640x480 resolution on PAL cubes */ + if (vinfo.xres == 640 && vinfo.yres == 576) { + page_offset = ((576 - 480) / 2) * 640 * ((bpp + 7) / 8); + } else { + page_offset = 0; + } + + /* clear all video memory */ + p = (Uint32 *)mapped_mem; + q = (Uint32 *)(mapped_mem + mapped_memlen); + while (p < q) + *p++ = GC_BLACK; + + if ((vinfo.xres != width) || (vinfo.yres != height) || + (vinfo.bits_per_pixel != bpp)) { + vinfo.activate = FB_ACTIVATE_NOW; + vinfo.accel_flags = 0; + vinfo.bits_per_pixel = bpp; + vinfo.xres = width; + vinfo.xres_virtual = width; + /* do not modify yres*, we use a fake 640x480 mode in PAL */ + //vinfo.yres = height; + //vinfo.yres_virtual = 2*height; + vinfo.xoffset = 0; + vinfo.yoffset = 0; + vinfo.red.length = vinfo.red.offset = 0; + vinfo.green.length = vinfo.green.offset = 0; + vinfo.blue.length = vinfo.blue.offset = 0; + vinfo.transp.length = vinfo.transp.offset = 0; + + if (ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0) { + SDL_SetError("Couldn't set console screen info"); + return (NULL); + } + } else { + int maxheight; + + /* Figure out how much video memory is available */ + maxheight = 2*yres; + if (vinfo.yres_virtual > maxheight) { + vinfo.yres_virtual = maxheight; + } + } + cache_vinfo = vinfo; + + Rmask = 0; + for (i = 0; i < vinfo.red.length; ++i) { + Rmask <<= 1; + Rmask |= (0x00000001 << vinfo.red.offset); + } + Gmask = 0; + for (i = 0; i < vinfo.green.length; ++i) { + Gmask <<= 1; + Gmask |= (0x00000001 << vinfo.green.offset); + } + Bmask = 0; + for (i = 0; i < vinfo.blue.length; ++i) { + Bmask <<= 1; + Bmask |= (0x00000001 << vinfo.blue.offset); + } + if (!SDL_ReallocFormat(current, bpp, Rmask, Gmask, Bmask, 0)) { + return (NULL); + } + + /* Get the fixed information about the console hardware. + This is necessary since finfo.line_length changes. + */ + if (ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0) { + SDL_SetError("Couldn't get console hardware info"); + return (NULL); + } + + /* Save hardware palette, if needed */ + //FB_SavePalette(this, &finfo, &vinfo); + + /* Set up the new mode framebuffer */ + current->flags = SDL_FULLSCREEN; + current->w = width; + current->h = height; + current->pitch = width * ((bpp + 7) / 8); + current->pixels = shadow_mem; + + flip_address[0] = mapped_mem; + flip_address[1] = mapped_mem + current->pitch * yres; + + back_page = 1; + if (flags & SDL_DOUBLEBUF) { + current->flags |= SDL_DOUBLEBUF; + flip_pending = 1; + } else { + flip_pending = 0; + /* make page 0 both the visible and back page */ + back_page = ioctl(console_fd, FBIOFLIPHACK, &back_page); + if (back_page < 0) + back_page = 0; + } + + /* Set the update rectangle function */ + switch (bpp) { + case 16: + GC_DPRINTF("Using 16bpp blitter\n"); + this->hidden->UpdateRect = GC_UpdateRectRGB16; + break; + default: + GC_DPRINTF("Using NO blitter\n"); + this->hidden->UpdateRect = NULL; + break; + } + + /* Handle OpenGL support */ +#ifdef HAVE_OPENGL + if (flags & SDL_OPENGL) { + if (GC_GL_CreateWindow(this, width, height) == 0) { + current->flags |= (SDL_OPENGL | SDL_FULLSCREEN); + } else { + current = NULL; + } + } +#endif /* HAVE_OPENGL */ + + /* We're done */ + return (current); +} + +static int GC_FlipHWSurface(_THIS, SDL_Surface * surface) +{ + if (flip_pending) { + /* SDL_UpdateRect was not called */ + SDL_UpdateRect(this->screen, 0, 0, 0, 0); + } + + /* flip video page as soon as possible */ + ioctl(console_fd, FBIOFLIPHACK, NULL); + flip_pending = 1; + + return (0); +} + +static void GC_WaitForFlipCompletion(_THIS) +{ + int visible_page; + int result; + + if (flip_pending) { + flip_pending = 0; + back_page ^= 1; + visible_page = back_page; + while (visible_page == back_page) { + /* wait until back_page is not visible */ + result = ioctl(console_fd, FBIOFLIPHACK, &back_page); + if (result < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + return; /* ioctl unsupported ... */ + } + visible_page = result; + } + /* + * At this point the back_page is not visible. We can safely + * write to it without tearing. + */ + } +} + +static void GC_UpdateRects(_THIS, int numrects, SDL_Rect * rects) +{ + SDL_Surface *screen; + int pitch; + + /* external yuy2 fb is 16bpp */ + + screen = this->screen; + pitch = screen->pitch; /* this is the pitch of the shadow buffer */ + + if (this->hidden->UpdateRect) { + GC_WaitForFlipCompletion(this); + while (numrects--) { + if (rects->w <= 0 || rects->h <= 0) + continue; + this->hidden->UpdateRect(this, rects, pitch); + rects++; + } + } +} + +void GC_CreateDevice(SDL_VideoDevice *this) +{ + this->ListModes = GC_ListModes; + this->SetVideoMode = GC_SetVideoMode; + this->FlipHWSurface = GC_FlipHWSurface; + this->UpdateRects = GC_UpdateRects; +} + +#endif diff --git a/src/video/fbcon/SDL_fbgc.h b/src/video/fbcon/SDL_fbgc.h new file mode 100644 index 0000000..534a73e --- /dev/null +++ b/src/video/fbcon/SDL_fbgc.h @@ -0,0 +1,35 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + + +#ifdef SDL_VIDEO_DRIVER_GC +/* Gamecube/Wii hardware setup for the SDL framebuffer console driver */ + +#include "SDL_fbvideo.h" + +extern SDL_bool GC_Available(void); +extern void GC_CreateDevice(SDL_VideoDevice *this); + +extern SDL_bool GC_Test(_THIS); +extern void GC_Init(_THIS, SDL_PixelFormat *vformat); +#endif diff --git a/src/video/fbcon/SDL_fbvideo.c b/src/video/fbcon/SDL_fbvideo.c index 81a89da..328790e 100644 --- a/src/video/fbcon/SDL_fbvideo.c +++ b/src/video/fbcon/SDL_fbvideo.c @@ -272,6 +272,11 @@ static SDL_VideoDevice *FB_CreateDevice(int devindex) this->free = FB_DeleteDevice; +#ifdef SDL_VIDEO_DRIVER_GC + if (GC_Available(this)) + GC_CreateDevice(this); +#endif + return this; } @@ -784,6 +789,11 @@ static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat) } } +#ifdef SDL_VIDEO_DRIVER_GC + if (GC_Test(this)) + GC_Init(this, vformat); +#endif + if (shadow_fb) { shadow_mem = (char *)SDL_malloc(mapped_memlen); if (shadow_mem == NULL) { diff --git a/src/video/fbcon/SDL_fbvideo.h b/src/video/fbcon/SDL_fbvideo.h index 03b9e94..74d1460 100644 --- a/src/video/fbcon/SDL_fbvideo.h +++ b/src/video/fbcon/SDL_fbvideo.h @@ -106,6 +106,12 @@ struct SDL_PrivateVideoData { void (*wait_vbl)(_THIS); void (*wait_idle)(_THIS); +#ifdef SDL_VIDEO_DRIVER_GC + void (*UpdateRect) (_THIS, SDL_Rect * rect, int pitch); + int back_page; + int page_offset; + int flip_pending; +#endif }; /* Old variable names */ #define console_fd (this->hidden->console_fd) @@ -147,6 +153,11 @@ struct SDL_PrivateVideoData { #define screen_palette (this->hidden->screen_palette) #define wait_vbl (this->hidden->wait_vbl) #define wait_idle (this->hidden->wait_idle) +#ifdef SDL_VIDEO_DRIVER_GC +#define back_page (this->hidden->back_page) +#define page_offset (this->hidden->page_offset) +#define flip_pending (this->hidden->flip_pending) +#endif /* Accelerator types that are supported by the driver, but are not necessarily in the kernel headers on the system we compile on. -- 1.6.5