From 9b8e5a705e72736051c70020b81fbd16bc80cb88 Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Thu, 11 Apr 2024 23:44:35 +0100 Subject: [PATCH] 3DS: Improve framebuffer support --- src/video/n3ds/SDL_n3dsframebuffer.c | 64 ++++++++++++++++++++----- src/video/n3ds/SDL_n3dsvideo.c | 70 +++++++++++++++++++++++++++- src/video/n3ds/SDL_n3dsvideo.h | 2 - 3 files changed, 121 insertions(+), 15 deletions(-) diff --git a/src/video/n3ds/SDL_n3dsframebuffer.c b/src/video/n3ds/SDL_n3dsframebuffer.c index 734ea1302..02408e9de 100644 --- a/src/video/n3ds/SDL_n3dsframebuffer.c +++ b/src/video/n3ds/SDL_n3dsframebuffer.c @@ -34,7 +34,9 @@ typedef struct int width, height; } Dimensions; -static void CopyFramebuffertoN3DS(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim); +static void CopyFramebuffertoN3DS_16(u16 *dest, const Dimensions dest_dim, const u16 *source, const Dimensions source_dim); +static void CopyFramebuffertoN3DS_24(u8 *dest, const Dimensions dest_dim, const u8 *source, const Dimensions source_dim); +static void CopyFramebuffertoN3DS_32(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim); static int GetDestOffset(int x, int y, int dest_width); static int GetSourceOffset(int x, int y, int source_width); static void FlushN3DSBuffer(const void *buffer, u32 bufsize, gfxScreen_t screen); @@ -43,17 +45,21 @@ static void FlushN3DSBuffer(const void *buffer, u32 bufsize, gfxScreen_t screen) int SDL_N3DS_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormatEnum *format, void **pixels, int *pitch) { SDL_Surface *framebuffer; + const SDL_DisplayMode *mode; int w, h; + SDL_N3DS_DestroyWindowFramebuffer(_this, window); + + mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(window)); SDL_GetWindowSizeInPixels(window, &w, &h); - framebuffer = SDL_CreateSurface(w, h, FRAMEBUFFER_FORMAT); + framebuffer = SDL_CreateSurface(w, h, mode->format); if (!framebuffer) { return -1; } SDL_SetSurfaceProperty(SDL_GetWindowProperties(window), N3DS_SURFACE, framebuffer); - *format = FRAMEBUFFER_FORMAT; + *format = mode->format; *pixels = framebuffer->pixels; *pitch = framebuffer->pitch; return 0; @@ -64,7 +70,7 @@ int SDL_N3DS_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_WindowData *drv_data = window->driverdata; SDL_Surface *surface; u16 width, height; - u32 *framebuffer; + void *framebuffer; u32 bufsize; surface = (SDL_Surface *)SDL_GetProperty(SDL_GetWindowProperties(window), N3DS_SURFACE, NULL); @@ -73,26 +79,60 @@ int SDL_N3DS_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, } /* Get the N3DS internal framebuffer and its size */ - framebuffer = (u32 *)gfxGetFramebuffer(drv_data->screen, GFX_LEFT, &width, &height); + framebuffer = gfxGetFramebuffer(drv_data->screen, GFX_LEFT, &width, &height); bufsize = width * height * 4; - CopyFramebuffertoN3DS(framebuffer, (Dimensions){ width, height }, - surface->pixels, (Dimensions){ surface->w, surface->h }); + if (surface->format->bytes_per_pixel == 2) + CopyFramebuffertoN3DS_16(framebuffer, (Dimensions){ width, height }, + surface->pixels, (Dimensions){ surface->w, surface->h }); + else if (surface->format->bytes_per_pixel == 3) + CopyFramebuffertoN3DS_24(framebuffer, (Dimensions){ width, height }, + surface->pixels, (Dimensions){ surface->w, surface->h }); + else + CopyFramebuffertoN3DS_32(framebuffer, (Dimensions){ width, height }, + surface->pixels, (Dimensions){ surface->w, surface->h }); FlushN3DSBuffer(framebuffer, bufsize, drv_data->screen); return 0; } -static void CopyFramebuffertoN3DS(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim) +static void CopyFramebuffertoN3DS_16(u16 *dest, const Dimensions dest_dim, const u16 *source, const Dimensions source_dim) { int rows = SDL_min(dest_dim.width, source_dim.height); int cols = SDL_min(dest_dim.height, source_dim.width); for (int y = 0; y < rows; ++y) { for (int x = 0; x < cols; ++x) { - SDL_memcpy( - dest + GetDestOffset(x, y, dest_dim.width), - source + GetSourceOffset(x, y, source_dim.width), - 4); + const u16 *s = source + GetSourceOffset(x, y, source_dim.width); + u16 *d = dest + GetDestOffset(x, y, dest_dim.width); + *d = *s; + } + } +} + +static void CopyFramebuffertoN3DS_24(u8 *dest, const Dimensions dest_dim, const u8 *source, const Dimensions source_dim) +{ + int rows = SDL_min(dest_dim.width, source_dim.height); + int cols = SDL_min(dest_dim.height, source_dim.width); + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < cols; ++x) { + const u8 *s = source + GetSourceOffset(x, y, source_dim.width) * 3; + u8 *d = dest + GetDestOffset(x, y, dest_dim.width) * 3; + d[0] = s[0]; + d[1] = s[1]; + d[2] = s[2]; + } + } +} + +static void CopyFramebuffertoN3DS_32(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim) +{ + int rows = SDL_min(dest_dim.width, source_dim.height); + int cols = SDL_min(dest_dim.height, source_dim.width); + for (int y = 0; y < rows; ++y) { + for (int x = 0; x < cols; ++x) { + const u32 *s = source + GetSourceOffset(x, y, source_dim.width); + u32 *d = dest + GetDestOffset(x, y, dest_dim.width); + *d = *s; } } } diff --git a/src/video/n3ds/SDL_n3dsvideo.c b/src/video/n3ds/SDL_n3dsvideo.c index 3f75111fe..9b37953a1 100644 --- a/src/video/n3ds/SDL_n3dsvideo.c +++ b/src/video/n3ds/SDL_n3dsvideo.c @@ -35,6 +35,8 @@ static int AddN3DSDisplay(gfxScreen_t screen); static int N3DS_VideoInit(SDL_VideoDevice *_this); static void N3DS_VideoQuit(SDL_VideoDevice *_this); +static int N3DS_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display); +static int N3DS_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode); static int N3DS_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect); static int N3DS_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props); static void N3DS_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); @@ -44,6 +46,23 @@ struct SDL_DisplayData gfxScreen_t screen; }; +struct SDL_DisplayModeData +{ + GSPGPU_FramebufferFormat fmt; +}; + +static const struct +{ + SDL_PixelFormatEnum pixfmt; + GSPGPU_FramebufferFormat gspfmt; +} format_map[] = { + { SDL_PIXELFORMAT_RGBA8888, GSP_RGBA8_OES }, + { SDL_PIXELFORMAT_BGR24, GSP_BGR8_OES }, + { SDL_PIXELFORMAT_RGB565, GSP_RGB565_OES }, + { SDL_PIXELFORMAT_RGBA5551, GSP_RGB5_A1_OES }, + { SDL_PIXELFORMAT_RGBA4444, GSP_RGBA4_OES } +}; + /* N3DS driver bootstrap functions */ static void N3DS_DeleteDevice(SDL_VideoDevice *device) @@ -76,6 +95,8 @@ static SDL_VideoDevice *N3DS_CreateDevice(void) device->VideoInit = N3DS_VideoInit; device->VideoQuit = N3DS_VideoQuit; + device->GetDisplayModes = N3DS_GetDisplayModes; + device->SetDisplayMode = N3DS_SetDisplayMode; device->GetDisplayBounds = N3DS_GetDisplayBounds; device->CreateSDLWindow = N3DS_CreateWindow; @@ -93,6 +114,8 @@ static SDL_VideoDevice *N3DS_CreateDevice(void) device->free = N3DS_DeleteDevice; + device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY; + return device; } @@ -117,6 +140,7 @@ static int N3DS_VideoInit(SDL_VideoDevice *_this) static int AddN3DSDisplay(gfxScreen_t screen) { SDL_DisplayMode mode; + SDL_DisplayModeData *modedata; SDL_VideoDisplay display; SDL_DisplayData *display_driver_data = SDL_calloc(1, sizeof(SDL_DisplayData)); if (!display_driver_data) { @@ -128,10 +152,17 @@ static int AddN3DSDisplay(gfxScreen_t screen) display_driver_data->screen = screen; + modedata = SDL_malloc(sizeof(SDL_DisplayModeData)); + if (!modedata) { + return -1; + } + mode.w = (screen == GFX_TOP) ? GSP_SCREEN_HEIGHT_TOP : GSP_SCREEN_HEIGHT_BOTTOM; mode.h = GSP_SCREEN_WIDTH; mode.refresh_rate = 60.0f; - mode.format = FRAMEBUFFER_FORMAT; + mode.format = SDL_PIXELFORMAT_RGBA8888; + mode.driverdata = modedata; + modedata->fmt = GSP_RGBA8_OES; display.name = (screen == GFX_TOP) ? "N3DS top screen" : "N3DS bottom screen"; display.desktop_mode = mode; @@ -149,6 +180,43 @@ static void N3DS_VideoQuit(SDL_VideoDevice *_this) gfxExit(); } +static int N3DS_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) +{ + SDL_DisplayData *displaydata = display->driverdata; + SDL_DisplayModeData *modedata; + SDL_DisplayMode mode; + int i; + + for (i = 0; i < SDL_arraysize(format_map); i++) { + modedata = SDL_malloc(sizeof(SDL_DisplayModeData)); + if (!modedata) + continue; + + SDL_zero(mode); + mode.w = (displaydata->screen == GFX_TOP) ? GSP_SCREEN_HEIGHT_TOP : GSP_SCREEN_HEIGHT_BOTTOM; + mode.h = GSP_SCREEN_WIDTH; + mode.refresh_rate = 60; + mode.format = format_map[i].pixfmt; + mode.driverdata = modedata; + modedata->fmt = format_map[i].gspfmt; + + if (!SDL_AddFullscreenDisplayMode(display, &mode)) { + SDL_free(modedata); + } + } + + return 0; +} + +static int N3DS_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode) +{ + SDL_DisplayData *displaydata = display->driverdata; + SDL_DisplayModeData *modedata = mode->driverdata; + + gfxSetScreenFormat(displaydata->screen, modedata->fmt); + return 0; +} + static int N3DS_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect) { SDL_DisplayData *driver_data = display->driverdata; diff --git a/src/video/n3ds/SDL_n3dsvideo.h b/src/video/n3ds/SDL_n3dsvideo.h index 5ed72aaed..d2197632a 100644 --- a/src/video/n3ds/SDL_n3dsvideo.h +++ b/src/video/n3ds/SDL_n3dsvideo.h @@ -38,6 +38,4 @@ struct SDL_WindowData gfxScreen_t screen; /**< Keeps track of which N3DS screen is targeted */ }; -#define FRAMEBUFFER_FORMAT SDL_PIXELFORMAT_RGBA8888 - #endif /* SDL_n3dsvideo_h_ */