From 59a041681996289d5da77721167b503fe9c5d5a4 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 | 88 ++++++++++++++++++---------- src/video/n3ds/SDL_n3dsvideo.c | 66 +++++++++++++++++++-- src/video/n3ds/SDL_n3dsvideo.h | 2 - 3 files changed, 118 insertions(+), 38 deletions(-) diff --git a/src/video/n3ds/SDL_n3dsframebuffer.c b/src/video/n3ds/SDL_n3dsframebuffer.c index 784c2df44b..c0524525fd 100644 --- a/src/video/n3ds/SDL_n3dsframebuffer.c +++ b/src/video/n3ds/SDL_n3dsframebuffer.c @@ -33,9 +33,9 @@ typedef struct int width, height; } Dimensions; -SDL_FORCE_INLINE void FreePreviousWindowFramebuffer(SDL_Window *window); -SDL_FORCE_INLINE SDL_Surface *CreateNewWindowFramebuffer(SDL_Window *window); -SDL_FORCE_INLINE void CopyFramebuffertoN3DS(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim); +SDL_FORCE_INLINE void CopyFramebuffertoN3DS_16(u16 *dest, const Dimensions dest_dim, const u16 *source, const Dimensions source_dim); +SDL_FORCE_INLINE void CopyFramebuffertoN3DS_24(u8 *dest, const Dimensions dest_dim, const u8 *source, const Dimensions source_dim); +SDL_FORCE_INLINE void CopyFramebuffertoN3DS_32(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim); SDL_FORCE_INLINE int GetDestOffset(int x, int y, int dest_width); SDL_FORCE_INLINE int GetSourceOffset(int x, int y, int source_width); SDL_FORCE_INLINE void FlushN3DSBuffer(const void *buffer, u32 bufsize, gfxScreen_t screen); @@ -43,44 +43,32 @@ SDL_FORCE_INLINE void FlushN3DSBuffer(const void *buffer, u32 bufsize, gfxScreen int SDL_N3DS_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void **pixels, int *pitch) { SDL_Surface *framebuffer; + SDL_DisplayMode mode; + int w, h; - FreePreviousWindowFramebuffer(window); - framebuffer = CreateNewWindowFramebuffer(window); + SDL_N3DS_DestroyWindowFramebuffer(_this, window); + + SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(window), &mode); + SDL_GetWindowSizeInPixels(window, &w, &h); + framebuffer = SDL_CreateRGBSurfaceWithFormat(0, w, h, SDL_BYTESPERPIXEL(mode.format), mode.format); if (!framebuffer) { return SDL_OutOfMemory(); } SDL_SetWindowData(window, N3DS_SURFACE, framebuffer); - *format = FRAMEBUFFER_FORMAT; + *format = mode.format; *pixels = framebuffer->pixels; *pitch = framebuffer->pitch; return 0; } -SDL_FORCE_INLINE void -FreePreviousWindowFramebuffer(SDL_Window *window) -{ - SDL_Surface *surface = (SDL_Surface *)SDL_GetWindowData(window, N3DS_SURFACE); - SDL_FreeSurface(surface); -} - -SDL_FORCE_INLINE SDL_Surface * -CreateNewWindowFramebuffer(SDL_Window *window) -{ - int w, h, bpp; - Uint32 Rmask, Gmask, Bmask, Amask; - SDL_PixelFormatEnumToMasks(FRAMEBUFFER_FORMAT, &bpp, &Rmask, &Gmask, &Bmask, &Amask); - SDL_GetWindowSizeInPixels(window, &w, &h); - return SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask); -} - int SDL_N3DS_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects) { SDL_WindowData *drv_data = (SDL_WindowData *)window->driverdata; SDL_Surface *surface; u16 width, height; - u32 *framebuffer; + void *framebuffer; u32 bufsize; surface = (SDL_Surface *)SDL_GetWindowData(window, N3DS_SURFACE); @@ -89,27 +77,63 @@ int SDL_N3DS_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect * } /* 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->BytesPerPixel == 2) + CopyFramebuffertoN3DS_16(framebuffer, (Dimensions){ width, height }, + surface->pixels, (Dimensions){ surface->w, surface->h }); + else if (surface->format->BytesPerPixel == 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; } SDL_FORCE_INLINE void -CopyFramebuffertoN3DS(u32 *dest, const Dimensions dest_dim, const u32 *source, const Dimensions source_dim) +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; + } + } +} + +SDL_FORCE_INLINE 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]; + } + } +} + +SDL_FORCE_INLINE 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 70868da4bc..99fcff779b 100644 --- a/src/video/n3ds/SDL_n3dsvideo.c +++ b/src/video/n3ds/SDL_n3dsvideo.c @@ -36,6 +36,7 @@ SDL_FORCE_INLINE int AddN3DSDisplay(gfxScreen_t screen); static int N3DS_VideoInit(_THIS); static void N3DS_VideoQuit(_THIS); static void N3DS_GetDisplayModes(_THIS, SDL_VideoDisplay *display); +static int N3DS_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode); static int N3DS_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect); static int N3DS_CreateWindow(_THIS, SDL_Window *window); static void N3DS_DestroyWindow(_THIS, SDL_Window *window); @@ -45,6 +46,23 @@ typedef struct gfxScreen_t screen; } DisplayDriverData; +typedef struct +{ + GSPGPU_FramebufferFormat fmt; +} ModeDriverData; + +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) @@ -80,6 +98,7 @@ static SDL_VideoDevice *N3DS_CreateDevice(void) device->VideoQuit = N3DS_VideoQuit; device->GetDisplayModes = N3DS_GetDisplayModes; + device->SetDisplayMode = N3DS_SetDisplayMode; device->GetDisplayBounds = N3DS_GetDisplayBounds; device->CreateSDLWindow = N3DS_CreateWindow; @@ -97,6 +116,8 @@ static SDL_VideoDevice *N3DS_CreateDevice(void) device->free = N3DS_DeleteDevice; + device->quirk_flags = VIDEO_DEVICE_QUIRK_FULLSCREEN_ONLY; + return device; } @@ -122,6 +143,7 @@ SDL_FORCE_INLINE int AddN3DSDisplay(gfxScreen_t screen) { SDL_DisplayMode mode; + ModeDriverData *modedata; SDL_VideoDisplay display; DisplayDriverData *display_driver_data = SDL_calloc(1, sizeof(DisplayDriverData)); if (!display_driver_data) { @@ -133,11 +155,18 @@ AddN3DSDisplay(gfxScreen_t screen) display_driver_data->screen = screen; + modedata = SDL_malloc(sizeof(ModeDriverData)); + if (!modedata) { + SDL_OutOfMemory(); + return; + } + mode.w = (screen == GFX_TOP) ? GSP_SCREEN_HEIGHT_TOP : GSP_SCREEN_HEIGHT_BOTTOM; mode.h = GSP_SCREEN_WIDTH; mode.refresh_rate = 60; - mode.format = FRAMEBUFFER_FORMAT; - mode.driverdata = NULL; + 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; @@ -158,8 +187,37 @@ static void N3DS_VideoQuit(_THIS) static void N3DS_GetDisplayModes(_THIS, SDL_VideoDisplay *display) { - /* Each display only has a single mode */ - SDL_AddDisplayMode(display, &display->current_mode); + DisplayDriverData *displaydata = display->driverdata; + ModeDriverData *modedata; + SDL_DisplayMode mode; + int i; + + for (i = 0; i < SDL_arraysize(format_map); i++) { + modedata = SDL_malloc(sizeof(ModeDriverData)); + 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_AddDisplayMode(display, &mode)) { + SDL_free(modedata); + } + } +} + +static int N3DS_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode) +{ + DisplayDriverData *displaydata = display->driverdata; + ModeDriverData *modedata = mode->driverdata; + + gfxSetScreenFormat(displaydata->screen, modedata->fmt); + return 0; } static int N3DS_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect) diff --git a/src/video/n3ds/SDL_n3dsvideo.h b/src/video/n3ds/SDL_n3dsvideo.h index 696d40b562..45c815c8c6 100644 --- a/src/video/n3ds/SDL_n3dsvideo.h +++ b/src/video/n3ds/SDL_n3dsvideo.h @@ -38,8 +38,6 @@ typedef struct SDL_WindowData gfxScreen_t screen; /**< Keeps track of which N3DS screen is targetted */ } SDL_WindowData; -#define FRAMEBUFFER_FORMAT SDL_PIXELFORMAT_RGBA8888 - #endif /* SDL_n3dsvideo_h_ */ /* vi: set sts=4 ts=4 sw=4 expandtab: */