From a77cc0a559d9cc35e00f3d17c4d8056e1ddc5e82 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Mon, 12 Oct 2020 17:30:10 +0100 Subject: [PATCH] Optimise the 3DS port (now runs okay on Old3DSs) This commit customises the software renderer specifically for the 3DS, taking into account its rotated framebuffer and BGR format. With this, the game appears to run full-speed on Old3DSs. Of course, the level transition effect still makes the console chug, but that thing was always a bit of a bottleneck. --- src/Backends/Platform/3DS.cpp | 2 +- src/Backends/Rendering/Software.cpp | 95 +++++++++++++++++++ .../Rendering/Window/Software/3DS.cpp | 24 +---- 3 files changed, 101 insertions(+), 20 deletions(-) diff --git a/src/Backends/Platform/3DS.cpp b/src/Backends/Platform/3DS.cpp index 5be63d58..a7620ddb 100644 --- a/src/Backends/Platform/3DS.cpp +++ b/src/Backends/Platform/3DS.cpp @@ -22,7 +22,7 @@ bool Backend_Init(void (*drag_and_drop_callback)(const char *path), void (*windo if (R_SUCCEEDED(romfsInit())) { - osSetSpeedupEnable(true); // Enable New3DS speedup, since this doesn't run very well on Old3DSs yet +// osSetSpeedupEnable(true); // Enable New3DS speedup, since this doesn't run very well on Old3DSs yet return true; } diff --git a/src/Backends/Rendering/Software.cpp b/src/Backends/Rendering/Software.cpp index 6eef25a9..48c855b1 100644 --- a/src/Backends/Rendering/Software.cpp +++ b/src/Backends/Rendering/Software.cpp @@ -37,8 +37,13 @@ RenderBackend_Surface* RenderBackend_Init(const char *window_title, size_t scree if (WindowBackend_Software_CreateWindow(window_title, screen_width, screen_height, fullscreen)) { framebuffer.pixels = WindowBackend_Software_GetFramebuffer(&framebuffer.pitch); + #ifdef _3DS + framebuffer.width = screen_height; + framebuffer.height = screen_width; + #else framebuffer.width = screen_width; framebuffer.height = screen_height; + #endif return &framebuffer; } @@ -80,9 +85,15 @@ RenderBackend_Surface* RenderBackend_CreateSurface(size_t width, size_t height, return NULL; } +#ifdef _3DS + surface->width = height; + surface->height = width; + surface->pitch = height * 3; +#else surface->width = width; surface->height = height; surface->pitch = width * 3; +#endif return surface; } @@ -107,18 +118,48 @@ void RenderBackend_RestoreSurface(RenderBackend_Surface *surface) void RenderBackend_UploadSurface(RenderBackend_Surface *surface, const unsigned char *pixels, size_t width, size_t height) { +#ifdef _3DS + // Rotate 90 degrees clockwise, and convert from RGB to BGR + const unsigned char *source_pointer = pixels; + + for (size_t y = 0; y < height; ++y) + { + for (size_t x = 0; x < width; ++x) + { + unsigned char *destination_pixel = &surface->pixels[x * surface->pitch + (surface->width - y - 1) * 3]; + + destination_pixel[2] = *source_pointer++; + destination_pixel[1] = *source_pointer++; + destination_pixel[0] = *source_pointer++; + } + } +#else for (size_t y = 0; y < height; ++y) memcpy(&surface->pixels[y * surface->pitch], &pixels[y * width * 3], width * 3); +#endif } ATTRIBUTE_HOT void RenderBackend_Blit(RenderBackend_Surface *source_surface, const RenderBackend_Rect *rect, RenderBackend_Surface *destination_surface, long x, long y, bool colour_key) { RenderBackend_Rect rect_clamped; +#ifdef _3DS + // Rotate + rect_clamped.left = source_surface->width - rect->bottom; + rect_clamped.top = rect->left; + rect_clamped.right = source_surface->width - rect->top; + rect_clamped.bottom = rect->right; + + const long new_x = (destination_surface->width - y) - (rect_clamped.right - rect_clamped.left); + const long new_y = x; + x = new_x; + y = new_y; +#else rect_clamped.left = rect->left; rect_clamped.top = rect->top; rect_clamped.right = rect->right; rect_clamped.bottom = rect->bottom; +#endif // Clamp the rect and coordinates so we don't write outside the pixel buffer long overflow; @@ -194,10 +235,18 @@ ATTRIBUTE_HOT void RenderBackend_ColourFill(RenderBackend_Surface *surface, cons { RenderBackend_Rect rect_clamped; +#ifdef _3DS + // Rotate + rect_clamped.left = surface->width - rect->bottom; + rect_clamped.top = rect->left; + rect_clamped.right = surface->width - rect->top; + rect_clamped.bottom = rect->right; +#else rect_clamped.left = rect->left; rect_clamped.top = rect->top; rect_clamped.right = rect->right; rect_clamped.bottom = rect->bottom; +#endif // Clamp the rect so it doesn't write outside the pixel buffer long overflow; @@ -230,9 +279,15 @@ ATTRIBUTE_HOT void RenderBackend_ColourFill(RenderBackend_Surface *surface, cons for (long i = 0; i < rect_clamped.right - rect_clamped.left; ++i) { + #ifdef _3DS + *destination_pointer++ = blue; + *destination_pointer++ = green; + *destination_pointer++ = red; + #else *destination_pointer++ = red; *destination_pointer++ = green; *destination_pointer++ = blue; + #endif } } } @@ -247,8 +302,13 @@ RenderBackend_GlyphAtlas* RenderBackend_CreateGlyphAtlas(size_t width, size_t he if (atlas->pixels != NULL) { + #ifdef _3DS + atlas->width = height; + atlas->height = width; + #else atlas->width = width; atlas->height = height; + #endif return atlas; } @@ -267,8 +327,19 @@ void RenderBackend_DestroyGlyphAtlas(RenderBackend_GlyphAtlas *atlas) void RenderBackend_UploadGlyph(RenderBackend_GlyphAtlas *atlas, size_t x, size_t y, const unsigned char *pixels, size_t width, size_t height, size_t pitch) { +#ifdef _3DS + // Rotate + for (size_t h = 0; h < height; ++h) + { + const unsigned char *source_pointer = &pixels[h * pitch]; + + for (size_t w = 0; w < width; ++w) + atlas->pixels[(x + w) * atlas->width + (atlas->width - (y + h) - 1)] = *source_pointer++; + } +#else for (size_t i = 0; i < height; ++i) memcpy(&atlas->pixels[(y + i) * atlas->width + x], &pixels[i * pitch], width); +#endif } void RenderBackend_PrepareToDrawGlyphs(RenderBackend_GlyphAtlas *atlas, RenderBackend_Surface *destination_surface, unsigned char red, unsigned char green, unsigned char blue) @@ -276,13 +347,37 @@ void RenderBackend_PrepareToDrawGlyphs(RenderBackend_GlyphAtlas *atlas, RenderBa glyph_atlas = atlas; glyph_destination_surface = destination_surface; +#ifdef _3DS + glyph_colour_channels[0] = blue; + glyph_colour_channels[1] = green; + glyph_colour_channels[2] = red; +#else glyph_colour_channels[0] = red; glyph_colour_channels[1] = green; glyph_colour_channels[2] = blue; +#endif } void RenderBackend_DrawGlyph(long x, long y, size_t glyph_x, size_t glyph_y, size_t glyph_width, size_t glyph_height) { +#ifdef _3DS + // Rotate + const size_t new_glyph_width = glyph_height; + const size_t new_glyph_height = glyph_width; + glyph_width = new_glyph_width; + glyph_height = new_glyph_height; + + const long new_x = (glyph_destination_surface->width - y) - glyph_width; + const long new_y = x; + x = new_x; + y = new_y; + + const long new_glyph_x = (glyph_atlas->width - glyph_y) - glyph_width; + const long new_glyph_y = glyph_x; + glyph_x = new_glyph_x; + glyph_y = new_glyph_y; +#endif + size_t surface_x; size_t surface_y; diff --git a/src/Backends/Rendering/Window/Software/3DS.cpp b/src/Backends/Rendering/Window/Software/3DS.cpp index cd9ab546..ed288c6e 100644 --- a/src/Backends/Rendering/Window/Software/3DS.cpp +++ b/src/Backends/Rendering/Window/Software/3DS.cpp @@ -2,6 +2,7 @@ #include #include +#include #include <3ds.h> @@ -23,9 +24,9 @@ bool WindowBackend_Software_CreateWindow(const char *window_title, size_t screen if (framebuffer != NULL) { - framebuffer_pitch = screen_width * 3; - framebuffer_width = screen_width; - framebuffer_height = screen_height; + framebuffer_pitch = screen_height * 3; + framebuffer_width = screen_height; + framebuffer_height = screen_width; return true; } @@ -47,22 +48,7 @@ unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch) void WindowBackend_Software_Display(void) { - // Absolutely disgusting - unsigned char *actual_framebuffer = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); - - unsigned char *framebuffer_pointer = framebuffer; - - const size_t offset = ((400 - framebuffer_width) / 2) * 240; - - for (unsigned int h = framebuffer_height - 1; h-- != 0;) - { - for (unsigned int w = 0; w < framebuffer_width * 240; w += 240) - { - actual_framebuffer[(offset + w + h) * 3 + 2] = *framebuffer_pointer++; - actual_framebuffer[(offset + w + h) * 3 + 1] = *framebuffer_pointer++; - actual_framebuffer[(offset + w + h) * 3 + 0] = *framebuffer_pointer++; - } - } + memcpy(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL) + (400 - framebuffer_height) * 240 * 3 / 2, framebuffer, framebuffer_pitch * framebuffer_height); gfxFlushBuffers(); gfxScreenSwapBuffers(GFX_TOP, false);