diff --git a/3ds/SDL2/3ds-initial-accelerated-renderer_2024-10-12.patch b/3ds/SDL2/3ds-initial-accelerated-renderer_2024-10-12.patch new file mode 100644 index 00000000..84761771 --- /dev/null +++ b/3ds/SDL2/3ds-initial-accelerated-renderer_2024-10-12.patch @@ -0,0 +1,1516 @@ +Source: https://github.com/libsdl-org/SDL/pull/9598 + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e45b53e20..a8cc01360 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -2877,6 +2877,9 @@ elseif(N3DS) + file(GLOB N3DS_MAIN_SOURCES ${SDL2_SOURCE_DIR}/src/main/n3ds/*.c) + set(SDLMAIN_SOURCES ${SDLMAIN_SOURCES} ${N3DS_MAIN_SOURCES}) + ++ file(GLOB N3DS_CORE_SOURCES ${SDL2_SOURCE_DIR}/src/core/n3ds/*.c) ++ list(APPEND SOURCE_FILES ${N3DS_CORE_SOURCES}) ++ + if(SDL_AUDIO) + set(SDL_AUDIO_DRIVER_N3DS 1) + file(GLOB N3DS_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/n3ds/*.c) +@@ -2930,9 +2933,12 @@ elseif(N3DS) + + if(SDL_VIDEO) + set(SDL_VIDEO_DRIVER_N3DS 1) +- file(GLOB N3DS_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/n3ds/*.c) ++ set(SDL_VIDEO_RENDER_N3DS 1) ++ file(GLOB N3DS_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/n3ds/*.c ${SDL2_SOURCE_DIR}/src/render/n3ds/*.c) + list(APPEND SOURCE_FILES ${N3DS_VIDEO_SOURCES}) ++ set(SDL_VIDEO_OPENGL 0) + set(HAVE_SDL_VIDEO TRUE) ++ list(APPEND EXTRA_LIBS citro3d) + endif() + + if(SDL_LOCALE) +diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake +index 64b8413c8..b52eae34f 100644 +--- a/include/SDL_config.h.cmake ++++ b/include/SDL_config.h.cmake +@@ -465,6 +465,7 @@ + #cmakedefine SDL_VIDEO_RENDER_OGL_ES2 @SDL_VIDEO_RENDER_OGL_ES2@ + #cmakedefine SDL_VIDEO_RENDER_DIRECTFB @SDL_VIDEO_RENDER_DIRECTFB@ + #cmakedefine SDL_VIDEO_RENDER_METAL @SDL_VIDEO_RENDER_METAL@ ++#cmakedefine SDL_VIDEO_RENDER_N3DS @SDL_VIDEO_RENDER_N3DS@ + #cmakedefine SDL_VIDEO_RENDER_VITA_GXM @SDL_VIDEO_RENDER_VITA_GXM@ + #cmakedefine SDL_VIDEO_RENDER_PS2 @SDL_VIDEO_RENDER_PS2@ + #cmakedefine SDL_VIDEO_RENDER_PSP @SDL_VIDEO_RENDER_PSP@ +diff --git a/src/SDL_internal.h b/src/SDL_internal.h +index aa6391493..2e58fe18a 100644 +--- a/src/SDL_internal.h ++++ b/src/SDL_internal.h +@@ -160,6 +160,9 @@ + #ifndef SDL_VIDEO_RENDER_VITA_GXM + #define SDL_VIDEO_RENDER_VITA_GXM 0 + #endif ++#ifndef SDL_VIDEO_RENDER_N3DS ++#define SDL_VIDEO_RENDER_N3DS 0 ++#endif + #else /* define all as 0 */ + #undef SDL_VIDEO_RENDER_SW + #define SDL_VIDEO_RENDER_SW 0 +@@ -185,6 +188,8 @@ + #define SDL_VIDEO_RENDER_PSP 0 + #undef SDL_VIDEO_RENDER_VITA_GXM + #define SDL_VIDEO_RENDER_VITA_GXM 0 ++#undef SDL_VIDEO_RENDER_N3DS ++#define SDL_VIDEO_RENDER_N3DS 0 + #endif /* SDL_RENDER_DISABLED */ + + #define SDL_HAS_RENDER_DRIVER \ +@@ -199,7 +204,8 @@ + SDL_VIDEO_RENDER_DIRECTFB | \ + SDL_VIDEO_RENDER_PS2 | \ + SDL_VIDEO_RENDER_PSP | \ +- SDL_VIDEO_RENDER_VITA_GXM) ++ SDL_VIDEO_RENDER_VITA_GXM | \ ++ SDL_VIDEO_RENDER_N3DS) + + #if !defined(SDL_RENDER_DISABLED) && !SDL_HAS_RENDER_DRIVER + #error SDL_RENDER enabled without any backend drivers. +diff --git a/src/core/n3ds/SDL_n3ds.c b/src/core/n3ds/SDL_n3ds.c +new file mode 100644 +index 000000000..cc0cc53b4 +--- /dev/null ++++ b/src/core/n3ds/SDL_n3ds.c +@@ -0,0 +1,53 @@ ++/* ++ Simple DirectMedia Layer ++ Copyright (C) 1997-2022 Sam Lantinga ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++*/ ++#include <3ds.h> ++#include "../../SDL_internal.h" ++ ++#include "3ds/allocator/linear.h" ++#include "SDL_stdinc.h" ++ ++#ifdef __3DS__ ++ ++void* N3DS_linearRealloc(void* mem, size_t size) { ++ /* FIXME: Remove this once libctru implements linearRealloc(). */ ++ if (mem == NULL) { ++ return linearAlloc(size); ++ } else if (linearGetSize(mem) <= size) { ++ return mem; ++ } else { ++ void *newMem = linearAlloc(size); ++ if (newMem != NULL) { ++ SDL_memcpy(newMem, mem, size); ++ linearFree(mem); ++ return newMem; ++ } else { ++ return NULL; ++ } ++ } ++} ++ ++void N3DS_linearFree(void* mem) { ++ linearFree(mem); ++} ++ ++#endif /* __3DS__ */ ++ ++/* vi: set ts=4 sw=4 expandtab: */ +diff --git a/src/core/n3ds/SDL_n3ds.h b/src/core/n3ds/SDL_n3ds.h +new file mode 100644 +index 000000000..15c34975e +--- /dev/null ++++ b/src/core/n3ds/SDL_n3ds.h +@@ -0,0 +1,41 @@ ++/* ++ Simple DirectMedia Layer ++ Copyright (C) 1997-2022 Sam Lantinga ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++*/ ++#include "../../SDL_internal.h" ++#include "SDL_system.h" ++ ++/* Set up for C function definitions, even when using C++ */ ++#ifdef __cplusplus ++/* *INDENT-OFF* */ ++extern "C" { ++/* *INDENT-ON* */ ++#endif ++ ++extern void* N3DS_linearRealloc(void* mem, size_t size); ++extern void N3DS_linearFree(void* mem); ++ ++/* Ends C function definitions when using C++ */ ++#ifdef __cplusplus ++/* *INDENT-OFF* */ ++} ++/* *INDENT-ON* */ ++#endif ++ ++/* vi: set ts=4 sw=4 expandtab: */ +diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c +index cc3d16959..88de5ad05 100644 +--- a/src/render/SDL_render.c ++++ b/src/render/SDL_render.c +@@ -33,6 +33,10 @@ + #include "../core/android/SDL_android.h" + #endif + ++#if defined(__3DS__) ++# include "../core/n3ds/SDL_n3ds.h" ++#endif ++ + /* as a courtesy to iOS apps, we don't try to draw when in the background, as + that will crash the app. However, these apps _should_ have used + SDL_AddEventWatch to catch SDL_APP_WILLENTERBACKGROUND events and stopped +@@ -122,6 +126,9 @@ static const SDL_RenderDriver *render_drivers[] = { + #if SDL_VIDEO_RENDER_DIRECTFB + &DirectFB_RenderDriver, + #endif ++#if SDL_VIDEO_RENDER_N3DS ++ &N3DS_RenderDriver, ++#endif + #if SDL_VIDEO_RENDER_PS2 + &PS2_RenderDriver, + #endif +@@ -306,7 +313,12 @@ void *SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, + newsize *= 2; + } + ++#ifdef __3DS__ ++ /* The 3DS GPU expects vertex data to be linear in physical memory. */ ++ ptr = N3DS_linearRealloc(renderer->vertex_data, newsize); ++#else + ptr = SDL_realloc(renderer->vertex_data, newsize); ++#endif + + if (!ptr) { + SDL_OutOfMemory(); +@@ -906,7 +918,7 @@ static SDL_INLINE void VerifyDrawQueueFunctions(const SDL_Renderer *renderer) + have to check that they aren't NULL over and over. */ + SDL_assert(renderer->QueueSetViewport != NULL); + SDL_assert(renderer->QueueSetDrawColor != NULL); +- SDL_assert(renderer->QueueDrawPoints != NULL); ++ SDL_assert(renderer->QueueDrawPoints != NULL || renderer->QueueGeometry != NULL); + SDL_assert(renderer->QueueDrawLines != NULL || renderer->QueueGeometry != NULL); + SDL_assert(renderer->QueueFillRects != NULL || renderer->QueueGeometry != NULL); + SDL_assert(renderer->QueueCopy != NULL || renderer->QueueGeometry != NULL); +@@ -2750,7 +2762,7 @@ int SDL_RenderDrawPoints(SDL_Renderer *renderer, + } + #endif + +- if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { ++ if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f || renderer->point_method == SDL_RENDERPOINTMETHOD_GEOMETRY) { + retval = RenderDrawPointsWithRects(renderer, points, count); + } else { + fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); +@@ -2821,7 +2833,7 @@ int SDL_RenderDrawPointsF(SDL_Renderer *renderer, + } + #endif + +- if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { ++ if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f || renderer->point_method == SDL_RENDERPOINTMETHOD_GEOMETRY) { + retval = RenderDrawPointsWithRectsF(renderer, points, count); + } else { + retval = QueueCmdDrawPoints(renderer, points, count); +@@ -2929,7 +2941,7 @@ static int RenderDrawLineBresenham(SDL_Renderer *renderer, int x1, int y1, int x + } + } + +- if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { ++ if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f || renderer->point_method == SDL_RENDERPOINTMETHOD_GEOMETRY) { + retval = RenderDrawPointsWithRectsF(renderer, points, numpixels); + } else { + retval = QueueCmdDrawPoints(renderer, points, numpixels); +@@ -4380,7 +4392,11 @@ void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer) + cmd = next; + } + ++#ifdef __3DS__ ++ N3DS_linearFree(renderer->vertex_data); ++#else + SDL_free(renderer->vertex_data); ++#endif + + /* Free existing textures for this renderer */ + while (renderer->textures) { +diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h +index bab41e772..d5da90f20 100644 +--- a/src/render/SDL_sysrender.h ++++ b/src/render/SDL_sysrender.h +@@ -133,6 +133,12 @@ typedef struct SDL_VertexSolid + SDL_Color color; + } SDL_VertexSolid; + ++typedef enum ++{ ++ SDL_RENDERPOINTMETHOD_POINTS, ++ SDL_RENDERPOINTMETHOD_GEOMETRY, ++} SDL_RenderPointMethod; ++ + typedef enum + { + SDL_RENDERLINEMETHOD_POINTS, +@@ -246,6 +252,9 @@ struct SDL_Renderer + /* Whether or not to scale relative mouse motion */ + SDL_bool relative_scaling; + ++ /* The method of drawing points */ ++ SDL_RenderPointMethod point_method; ++ + /* The method of drawing lines */ + SDL_RenderLineMethod line_method; + +@@ -305,6 +314,7 @@ extern SDL_RenderDriver GLES2_RenderDriver; + extern SDL_RenderDriver GLES_RenderDriver; + extern SDL_RenderDriver DirectFB_RenderDriver; + extern SDL_RenderDriver METAL_RenderDriver; ++extern SDL_RenderDriver N3DS_RenderDriver; + extern SDL_RenderDriver PS2_RenderDriver; + extern SDL_RenderDriver PSP_RenderDriver; + extern SDL_RenderDriver SW_RenderDriver; +diff --git a/src/render/n3ds/SDL_render_n3ds.c b/src/render/n3ds/SDL_render_n3ds.c +new file mode 100644 +index 000000000..d015b8b13 +--- /dev/null ++++ b/src/render/n3ds/SDL_render_n3ds.c +@@ -0,0 +1,1088 @@ ++/* ++ Simple DirectMedia Layer ++ Copyright (C) 1997-2022 Sam Lantinga ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++*/ ++#include "../../SDL_internal.h" ++ ++#if SDL_VIDEO_RENDER_N3DS ++#include "SDL_error.h" ++#include "SDL_hints.h" ++#include "SDL_pixels.h" ++#include "SDL_render.h" ++#include "SDL_stdinc.h" ++#include "SDL_video.h" ++#include "../SDL_sysrender.h" ++ ++#include ++#include <3ds.h> ++#include ++ ++#include "SDL_render_n3ds_shaders.h" ++ ++/** ++ * N3DS renderer implementation, derived from the PSP implementation. ++ * ++ * TODO: ++ * - Native SDL_PIXELFORMAT_RGB888 support (breaks with SDL_TEXTUREACCESS_STREAMING) ++ * - Fix GX Display Transfer usage for offloading texture swizzling to hardware ++ */ ++ ++static u8 SwizzleLUT[64] = ++{ ++ 0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15, ++ 0x02, 0x03, 0x06, 0x07, 0x12, 0x13, 0x16, 0x17, ++ 0x08, 0x09, 0x0c, 0x0d, 0x18, 0x19, 0x1c, 0x1d, ++ 0x0a, 0x0b, 0x0e, 0x0f, 0x1a, 0x1b, 0x1e, 0x1f, ++ 0x20, 0x21, 0x24, 0x25, 0x30, 0x31, 0x34, 0x35, ++ 0x22, 0x23, 0x26, 0x27, 0x32, 0x33, 0x36, 0x37, ++ 0x28, 0x29, 0x2c, 0x2d, 0x38, 0x39, 0x3c, 0x3d, ++ 0x2a, 0x2b, 0x2e, 0x2f, 0x3a, 0x3b, 0x3e, 0x3f ++}; ++ ++static int ++PixelFormatToN3DSGPU(Uint32 format) ++{ ++ switch (format) { ++ case SDL_PIXELFORMAT_RGBA8888: ++ return GPU_RGBA8; ++ case SDL_PIXELFORMAT_RGB888: ++ return GPU_RGB8; ++ case SDL_PIXELFORMAT_RGBA5551: ++ return GPU_RGBA5551; ++ case SDL_PIXELFORMAT_RGB565: ++ return GPU_RGB565; ++ case SDL_PIXELFORMAT_RGBA4444: ++ return GPU_RGBA4; ++ default: ++ return GPU_RGBA8; ++ } ++} ++ ++#define COL8888(r,g,b,a) ((a) | ((b)<<8) | ((g)<<16) | ((r)<<24)) ++ ++typedef struct ++{ ++ C3D_Tex texture; ++ C3D_RenderTarget* renderTarget; ++ C3D_Mtx renderProjMtx; ++ unsigned int width; /**< Image width. */ ++ unsigned int height; /**< Image height. */ ++ unsigned int pitch; ++ unsigned int size; ++ /** ++ * The 3DS GPU requires all textures to be *swizzled* before use. ++ * ++ * For textures considered STREAMING, we keep an unswizzled buffer in memory ++ * at all times. For textures considered STATIC or TARGET, we generate an ++ * unswizzled memory buffer on demand - this saves memory usage, but slows ++ * down updates. ++ * ++ * To save on memory usage, we align the unswizzled buffer's width/height ++ * to a multiple of 8, as opposed to the next power of two. The 3DS GPU can ++ * deal with that. ++ */ ++ void* unswizzledBuffer; ++ unsigned int unswizzledWidth; ++ unsigned int unswizzledHeight; ++ unsigned int unswizzledPitch; ++ unsigned int unswizzledSize; ++} N3DS_TextureData; ++ ++typedef struct ++{ ++ SDL_BlendMode mode; ++ SDL_Texture* texture; ++} N3DS_BlendState; ++ ++typedef struct ++{ ++ C3D_RenderTarget* renderTarget; ++ C3D_Mtx renderProjMtx; ++ SDL_Texture* boundTarget; /**< currently bound rendertarget */ ++ SDL_bool initialized; /**< is driver initialized */ ++ SDL_bool displayListAvail; /**< is the display list already initialized for this frame */ ++ unsigned int psm; /**< format of the display buffers */ ++ unsigned int bpp; /**< bits per pixel of the main display */ ++ ++ SDL_bool vsync; /**< wether we do vsync */ ++ N3DS_BlendState blendState; /**< current blend mode */ ++ ++ C3D_TexEnv envTex; ++ C3D_TexEnv envNoTex; ++ ++ DVLB_s *dvlb; ++ shaderProgram_s shaderProgram; ++ int projMtxShaderLoc; ++} N3DS_RenderData; ++ ++typedef struct ++{ ++ float x, y; ++ SDL_Color col; ++ float u, v; ++} VertVCT; ++ ++#define PI 3.14159265358979f ++ ++#define radToDeg(x) ((x)*180.f/PI) ++#define degToRad(x) ((x)*PI/180.f) ++ ++static inline void ++Swap(float *a, float *b) ++{ ++ float n=*a; ++ *a = *b; ++ *b = n; ++} ++ ++static inline int ++InVram(void* data) ++{ ++ return data < (void*)0x20000000; ++} ++ ++static int ++TextureNextPow2(unsigned int w) ++{ ++ if (w < 8) ++ return 8; ++ ++ w -= 1; ++ w |= w >> 1; ++ w |= w >> 2; ++ w |= w >> 4; ++ w |= w >> 8; ++ return w + 1; ++} ++ ++static int ++TextureAlign8(unsigned int w) { ++ return (w + 7) & (~7); ++} ++ ++static void ++TextureActivate(SDL_Texture *texture) ++{ ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ C3D_TexBind(0, &N3DS_texture->texture); ++} ++ ++static void ++N3DS_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) ++{ ++} ++ ++static int ++N3DS_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) ++{ ++ N3DS_TextureData* N3DS_texture = (N3DS_TextureData*) SDL_calloc(1, sizeof(*N3DS_texture)); ++ bool initialized = SDL_FALSE; ++ ++ if (!N3DS_texture) ++ return SDL_OutOfMemory(); ++ ++ N3DS_texture->width = texture->w; ++ N3DS_texture->height = texture->h; ++ ++#if 0 ++ initialized = C3D_TexInitVRAM(&N3DS_texture->texture, ++ TextureNextPow2(texture->w), ++ TextureNextPow2(texture->h), ++ PixelFormatToN3DSGPU(texture->format)); ++#endif ++ ++ if (!initialized) { ++ initialized = C3D_TexInit(&N3DS_texture->texture, ++ TextureNextPow2(texture->w), ++ TextureNextPow2(texture->h), ++ PixelFormatToN3DSGPU(texture->format)); ++ } ++ ++ if (!initialized) { ++ SDL_free(N3DS_texture); ++ return SDL_OutOfMemory(); ++ } ++ ++ N3DS_texture->pitch = N3DS_texture->texture.width * SDL_BYTESPERPIXEL(texture->format); ++ N3DS_texture->size = N3DS_texture->texture.height * N3DS_texture->pitch; ++ ++ N3DS_texture->unswizzledWidth = TextureAlign8(texture->w); ++ N3DS_texture->unswizzledHeight = TextureAlign8(texture->h); ++ N3DS_texture->unswizzledPitch = N3DS_texture->unswizzledWidth * SDL_BYTESPERPIXEL(texture->format); ++ N3DS_texture->unswizzledSize = N3DS_texture->unswizzledHeight * N3DS_texture->unswizzledPitch; ++ ++ if (texture->access == SDL_TEXTUREACCESS_TARGET) { ++ N3DS_texture->renderTarget = C3D_RenderTargetCreateFromTex(&N3DS_texture->texture, GPU_TEXFACE_2D, 0, GPU_RB_DEPTH16); ++ ++ if (N3DS_texture->renderTarget == NULL) { ++ C3D_TexDelete(&N3DS_texture->texture); ++ SDL_free(N3DS_texture); ++ return SDL_OutOfMemory(); ++ } ++ ++ Mtx_Ortho(&N3DS_texture->renderProjMtx, 0.0, N3DS_texture->texture.width, 0.0, N3DS_texture->texture.height, -1.0, 1.0, true); ++ } else if (texture->access == SDL_TEXTUREACCESS_STREAMING) { ++ N3DS_texture->unswizzledBuffer = linearAlloc(N3DS_texture->unswizzledSize); ++ } ++ ++ texture->driverdata = N3DS_texture; ++ ++ return 0; ++} ++ ++static int ++N3DS_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, ++ const SDL_Rect * rect, void **pixels, int *pitch); ++ ++static void ++N3DS_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); ++ ++static int ++N3DS_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, ++ const SDL_Rect * rect, const void *pixels, int pitch) ++{ ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ ++ const Uint8 *src; ++ Uint8 *dst; ++ int row, length, dpitch; ++ src = pixels; ++ ++ if (texture->access != SDL_TEXTUREACCESS_STREAMING) { ++ N3DS_texture->unswizzledBuffer = linearAlloc(N3DS_texture->unswizzledSize); ++ if (N3DS_texture->unswizzledBuffer == NULL) { ++ return SDL_OutOfMemory(); ++ } ++ } ++ ++ N3DS_LockTexture(renderer, texture, rect, (void **)&dst, &dpitch); ++ length = rect->w * SDL_BYTESPERPIXEL(texture->format); ++ if (length == pitch && length == dpitch) { ++ SDL_memcpy(dst, src, length*rect->h); ++ } else { ++ for (row = 0; row < rect->h; ++row) { ++ SDL_memcpy(dst, src, length); ++ src += pitch; ++ dst += dpitch; ++ } ++ } ++ N3DS_UnlockTexture(renderer, texture); ++ ++ if (texture->access != SDL_TEXTUREACCESS_STREAMING) { ++ linearFree(N3DS_texture->unswizzledBuffer); ++ N3DS_texture->unswizzledBuffer = NULL; ++ } ++ ++ return 0; ++} ++ ++static int ++N3DS_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, ++ const SDL_Rect * rect, void **pixels, int *pitch) ++{ ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ ++ *pixels = ++ (void *) ((Uint8 *) N3DS_texture->unswizzledBuffer + rect->y * N3DS_texture->unswizzledPitch + ++ rect->x * SDL_BYTESPERPIXEL(texture->format)); ++ *pitch = N3DS_texture->unswizzledPitch; ++ ++ return 0; ++} ++ ++static void ++N3DS_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) ++{ ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ ++ /* We do whole texture updates, at least for now */ ++ ++ /* ++ GSPGPU_FlushDataCache(N3DS_texture->unswizzledBuffer, N3DS_texture->unswizzledSize); ++ C3D_SyncDisplayTransfer( ++ N3DS_texture->unswizzledBuffer, ++ GX_BUFFER_DIM(N3DS_texture->unswizzledWidth, N3DS_texture->unswizzledHeight), ++ N3DS_texture->texture.data, ++ GX_BUFFER_DIM(N3DS_texture->texture.width, N3DS_texture->texture.height), ++ GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | ++ GX_TRANSFER_IN_FORMAT(N3DS_texture->texture.fmt) | GX_TRANSFER_OUT_FORMAT(N3DS_texture->texture.fmt) | ++ GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO) ++ ); ++ */ ++ ++ if (SDL_BYTESPERPIXEL(texture->format) == 4) { ++ uint32_t *dst = N3DS_texture->texture.data; ++ for (int y = 0; y < N3DS_texture->unswizzledHeight; y++) { ++ uint32_t *src = (uint32_t*) (N3DS_texture->unswizzledBuffer + (y * N3DS_texture->unswizzledPitch)); ++ for (int x = 0; x < N3DS_texture->unswizzledWidth; x++, src++) { ++ dst[SwizzleLUT[(x & 7) + ((y & 7) << 3)] + ((x & (~7)) << 3) + ((y & (~7)) * N3DS_texture->texture.width)] = *src; ++ } ++ } ++ } else if (SDL_BYTESPERPIXEL(texture->format) == 2) { ++ uint16_t *dst = N3DS_texture->texture.data; ++ for (int y = 0; y < N3DS_texture->unswizzledHeight; y++) { ++ uint16_t *src = (uint16_t*) (N3DS_texture->unswizzledBuffer + (y * N3DS_texture->unswizzledPitch)); ++ for (int x = 0; x < N3DS_texture->unswizzledWidth; x++, src++) { ++ dst[SwizzleLUT[(x & 7) + ((y & 7) << 3)] + ((x & (~7)) << 3) + ((y & (~7)) * N3DS_texture->texture.width)] = *src; ++ } ++ } ++ } ++ ++ C3D_TexFlush(&N3DS_texture->texture); ++} ++ ++static void ++N3DS_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode) ++{ ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ GPU_TEXTURE_FILTER_PARAM filter = (scaleMode == SDL_ScaleModeNearest) ? GPU_NEAREST : GPU_LINEAR; ++ ++ C3D_TexSetFilter(&N3DS_texture->texture, filter, filter); ++} ++ ++static int ++N3DS_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) ++{ ++ N3DS_RenderData *data = renderer->driverdata; ++ data->boundTarget = texture; ++ ++ if (texture == NULL) { ++ if (!C3D_FrameDrawOn(data->renderTarget)) { ++ return SDL_Unsupported(); ++ } ++ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, data->projMtxShaderLoc, &data->renderProjMtx); ++ } else { ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ if (N3DS_texture->renderTarget != NULL) { ++ if (!C3D_FrameDrawOn(N3DS_texture->renderTarget)) { ++ return SDL_Unsupported(); ++ } ++ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, data->projMtxShaderLoc, &N3DS_texture->renderProjMtx); ++ } else { ++ return SDL_Unsupported(); ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++N3DS_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) ++{ ++ return 0; /* nothing to do in this backend. */ ++} ++ ++static int ++N3DS_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, ++ const float *xy, int xy_stride, const SDL_Color *color, int color_stride, const float *uv, int uv_stride, ++ int num_vertices, const void *indices, int num_indices, int size_indices, ++ float scale_x, float scale_y) ++{ ++ int i; ++ int count = indices ? num_indices : num_vertices; ++ VertVCT *verts; ++ ++ cmd->data.draw.count = count; ++ size_indices = indices ? size_indices : 0; ++ ++ verts = (VertVCT *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertVCT), 0, &cmd->data.draw.first); ++ if (!verts) { ++ return -1; ++ } ++ ++ if (texture == NULL) { ++ for (i = 0; i < count; i++) { ++ int j; ++ float *xy_; ++ SDL_Color col_; ++ if (size_indices == 4) { ++ j = ((const Uint32 *)indices)[i]; ++ } else if (size_indices == 2) { ++ j = ((const Uint16 *)indices)[i]; ++ } else if (size_indices == 1) { ++ j = ((const Uint8 *)indices)[i]; ++ } else { ++ j = i; ++ } ++ ++ xy_ = (float *)((char*)xy + j * xy_stride); ++ col_ = *(SDL_Color *)((char*)color + j * color_stride); ++ ++ verts->x = xy_[0] * scale_x; ++ verts->y = xy_[1] * scale_y; ++ ++ verts->col = col_; ++ ++ verts++; ++ } ++ } else { ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ for (i = 0; i < count; i++) { ++ int j; ++ float *xy_; ++ SDL_Color col_; ++ float *uv_; ++ ++ if (size_indices == 4) { ++ j = ((const Uint32 *)indices)[i]; ++ } else if (size_indices == 2) { ++ j = ((const Uint16 *)indices)[i]; ++ } else if (size_indices == 1) { ++ j = ((const Uint8 *)indices)[i]; ++ } else { ++ j = i; ++ } ++ ++ xy_ = (float *)((char*)xy + j * xy_stride); ++ col_ = *(SDL_Color *)((char*)color + j * color_stride); ++ uv_ = (float *)((char*)uv + j * uv_stride); ++ ++ verts->x = xy_[0] * scale_x; ++ verts->y = xy_[1] * scale_y; ++ ++ verts->col = col_; ++ ++ verts->u = uv_[0] * N3DS_texture->texture.width; ++ verts->v = uv_[1] * N3DS_texture->texture.height; ++ ++ verts++; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++N3DS_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) ++{ ++ SDL_Color color = *((SDL_Color*) &(cmd->data.draw.r)); ++ VertVCT *verts = (VertVCT *) SDL_AllocateRenderVertices(renderer, count * 6 * sizeof (VertVCT), 0, &cmd->data.draw.first); ++ int i; ++ ++ if (!verts) { ++ return -1; ++ } ++ ++ cmd->data.draw.count = count * 6; ++ ++ for (i = 0; i < count; i++, rects++) { ++ verts->x = rects->x; ++ verts->y = rects->y; ++ verts->col = color; ++ verts++; ++ ++ verts->x = rects->x; ++ verts->y = rects->y + rects->h; ++ verts->col = color; ++ verts++; ++ ++ verts->x = rects->x + rects->w; ++ verts->y = rects->y; ++ verts->col = color; ++ verts++; ++ ++ verts->x = rects->x + rects->w; ++ verts->y = rects->y; ++ verts->col = color; ++ verts++; ++ ++ verts->x = rects->x; ++ verts->y = rects->y + rects->h; ++ verts->col = color; ++ verts++; ++ ++ verts->x = rects->x + rects->w; ++ verts->y = rects->y + rects->h; ++ verts->col = color; ++ verts++; ++ } ++ ++ return 0; ++} ++ ++static int ++N3DS_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, ++ const SDL_Rect * srcrect, const SDL_FRect * dstrect) ++{ ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ ++ SDL_Color color = *((SDL_Color*) &(cmd->data.draw.r)); ++ VertVCT *verts; ++ const float x = dstrect->x; ++ const float y = dstrect->y; ++ const float width = dstrect->w; ++ const float height = dstrect->h; ++ ++ const float u0 = (float) srcrect->x / N3DS_texture->texture.width; ++ const float v0 = 1.0f - (float) srcrect->y / N3DS_texture->texture.height; ++ const float u1 = (float) (srcrect->x + srcrect->w) / N3DS_texture->texture.width; ++ const float v1 = 1.0f - (float) (srcrect->y + srcrect->h) / N3DS_texture->texture.height; ++ ++ verts = (VertVCT *) SDL_AllocateRenderVertices(renderer, 6 * sizeof (VertVCT), 0, &cmd->data.draw.first); ++ if (!verts) { ++ return -1; ++ } ++ ++ cmd->data.draw.count = 6; ++ ++ verts->u = u0; ++ verts->v = v0; ++ verts->x = x; ++ verts->y = y; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u0; ++ verts->v = v1; ++ verts->x = x; ++ verts->y = y + height; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u1; ++ verts->v = v0; ++ verts->x = x + width; ++ verts->y = y; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u1; ++ verts->v = v0; ++ verts->x = x + width; ++ verts->y = y; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u0; ++ verts->v = v1; ++ verts->x = x; ++ verts->y = y + height; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u1; ++ verts->v = v1; ++ verts->x = x + width; ++ verts->y = y + height; ++ verts->col = color; ++ ++ return 0; ++} ++ ++static int ++N3DS_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, ++ const SDL_Rect * srcrect, const SDL_FRect * dstrect, ++ const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y) ++{ ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ ++ SDL_Color color = *((SDL_Color*) &(cmd->data.draw.r)); ++ VertVCT *verts = (VertVCT *) SDL_AllocateRenderVertices(renderer, 6 * sizeof (VertVCT), 0, &cmd->data.draw.first); ++ const float centerx = center->x; ++ const float centery = center->y; ++ const float x = dstrect->x + centerx; ++ const float y = dstrect->y + centery; ++ const float width = dstrect->w - centerx; ++ const float height = dstrect->h - centery; ++ float s, c; ++ float cw1, sw1, ch1, sh1, cw2, sw2, ch2, sh2; ++ ++ float u0 = (float) srcrect->x / N3DS_texture->texture.width; ++ float v0 = 1.0f - (float) srcrect->y / N3DS_texture->texture.height; ++ float u1 = (float) (srcrect->x + srcrect->w) / N3DS_texture->texture.width; ++ float v1 = 1.0f - (float) (srcrect->y + srcrect->h) / N3DS_texture->texture.height; ++ ++ if (!verts) { ++ return -1; ++ } ++ ++ cmd->data.draw.count = 6; ++ ++ s = sinf(degToRad(360-angle)); ++ c = cosf(degToRad(360-angle)); ++ ++ cw1 = c * -centerx; ++ sw1 = s * -centerx; ++ ch1 = c * -centery; ++ sh1 = s * -centery; ++ cw2 = c * width; ++ sw2 = s * width; ++ ch2 = c * height; ++ sh2 = s * height; ++ ++ if (flip & SDL_FLIP_VERTICAL) { ++ Swap(&v0, &v1); ++ } ++ ++ if (flip & SDL_FLIP_HORIZONTAL) { ++ Swap(&u0, &u1); ++ } ++ ++ verts->u = u0; ++ verts->v = v0; ++ verts->x = x + cw1 + sh1; ++ verts->y = y - sw1 + ch1; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u1; ++ verts->v = v0; ++ verts->x = x + cw2 + sh1; ++ verts->y = y - sw2 + ch1; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u0; ++ verts->v = v1; ++ verts->x = x + cw1 + sh2; ++ verts->y = y - sw1 + ch2; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u0; ++ verts->v = v1; ++ verts->x = x + cw1 + sh2; ++ verts->y = y - sw1 + ch2; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u1; ++ verts->v = v0; ++ verts->x = x + cw2 + sh1; ++ verts->y = y - sw2 + ch1; ++ verts->col = color; ++ verts++; ++ ++ verts->u = u1; ++ verts->v = v1; ++ verts->x = x + cw2 + sh2; ++ verts->y = y - sw2 + ch2; ++ verts->col = color; ++ ++ if (scale_x != 1.0f || scale_y != 1.0f) { ++ verts->x *= scale_x; ++ verts->y *= scale_y; ++ verts--; ++ verts->x *= scale_x; ++ verts->y *= scale_y; ++ verts--; ++ verts->x *= scale_x; ++ verts->y *= scale_y; ++ verts--; ++ verts->x *= scale_x; ++ verts->y *= scale_y; ++ verts--; ++ verts->x *= scale_x; ++ verts->y *= scale_y; ++ verts--; ++ verts->x *= scale_x; ++ verts->y *= scale_y; ++ } ++ ++ return 0; ++} ++ ++static void ++ResetBlendState(N3DS_RenderData *data, N3DS_BlendState* state) { ++ state->mode = SDL_BLENDMODE_INVALID; ++ state->texture = NULL; ++ C3D_SetTexEnv(0, &data->envNoTex); ++} ++ ++static void StartDrawing(SDL_Renderer *renderer) ++{ ++ N3DS_RenderData *data = (N3DS_RenderData *)renderer->driverdata; ++ ++ // Check if we need to start the displaylist ++ if (!data->displayListAvail) { ++ C3D_FrameBegin(data->vsync ? C3D_FRAME_SYNCDRAW : 0); ++ N3DS_SetRenderTarget(renderer, data->boundTarget); ++ data->displayListAvail = SDL_TRUE; ++ } ++} ++ ++static void ++N3DS_SetBlendState(N3DS_RenderData* data, N3DS_BlendState* state) ++{ ++ N3DS_BlendState* current = &data->blendState; ++ ++ if (state->mode != current->mode) { ++ switch (state->mode) { ++ case SDL_BLENDMODE_NONE: ++ C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ZERO, GPU_ONE, GPU_ZERO); ++ break; ++ case SDL_BLENDMODE_BLEND: ++ C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); ++ break; ++ case SDL_BLENDMODE_ADD: ++ C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE, GPU_ZERO, GPU_ONE); ++ break; ++ case SDL_BLENDMODE_MOD: ++ C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_DST_COLOR, GPU_ZERO, GPU_ZERO, GPU_ONE); ++ break; ++ case SDL_BLENDMODE_MUL: ++ C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_DST_COLOR, GPU_ONE_MINUS_SRC_ALPHA, GPU_DST_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); ++ break; ++ case SDL_BLENDMODE_INVALID: ++ break; ++ } ++ } ++ ++ if (state->texture != current->texture) { ++ if (state->texture != NULL) { ++ TextureActivate(state->texture); ++ C3D_SetTexEnv(0, &data->envTex); ++ } else { ++ C3D_SetTexEnv(0, &data->envNoTex); ++ } ++ } ++ ++ *current = *state; ++} ++ ++static int ++N3DS_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) ++{ ++ N3DS_RenderData *data = (N3DS_RenderData *) renderer->driverdata; ++ ++ C3D_BufInfo *bufInfo = C3D_GetBufInfo(); ++ BufInfo_Init(bufInfo); ++ BufInfo_Add(bufInfo, vertices, sizeof(VertVCT), 3, 0x210); ++ ++ StartDrawing(renderer); ++ ++ while (cmd) { ++ switch (cmd->command) { ++ case SDL_RENDERCMD_SETVIEWPORT: { ++ SDL_Rect *viewport = &cmd->data.viewport.rect; ++ if (data->boundTarget) { ++ C3D_SetViewport(viewport->x, viewport->y, viewport->w, viewport->h); ++ } else { ++ // Handle the tilted render target of the 3DS display. ++ C3D_SetViewport( ++ data->renderTarget->frameBuf.width - viewport->h - viewport->y, ++ data->renderTarget->frameBuf.height - viewport->w - viewport->x, ++ viewport->h, viewport->w); ++ } ++ break; ++ } ++ ++ case SDL_RENDERCMD_SETDRAWCOLOR: { ++ break; ++ } ++ ++ case SDL_RENDERCMD_DRAW_POINTS: { ++ /* Output as geometry */ ++ break; ++ } ++ ++ case SDL_RENDERCMD_DRAW_LINES: { ++ /* Output as geometry */ ++ break; ++ } ++ ++ case SDL_RENDERCMD_SETCLIPRECT: { ++ const SDL_Rect *rect = &cmd->data.cliprect.rect; ++ if (cmd->data.cliprect.enabled) { ++ unsigned int x = SDL_max(0, rect->x), w = rect->w; ++ unsigned int y = SDL_max(0, rect->y), h = rect->h; ++ ++ if (data->boundTarget) { ++ C3D_SetScissor(GPU_SCISSOR_NORMAL, ++ SDL_min(data->renderTarget->frameBuf.width, x), ++ SDL_min(data->renderTarget->frameBuf.height, y), ++ SDL_min(data->renderTarget->frameBuf.width, x + w), ++ SDL_min(data->renderTarget->frameBuf.height, y + h)); ++ } else { ++ C3D_SetScissor(GPU_SCISSOR_NORMAL, ++ SDL_max(0, data->renderTarget->frameBuf.width - (rect->y + rect->h)), ++ SDL_max(0, data->renderTarget->frameBuf.height - (rect->x + rect->w)), ++ SDL_max(0, data->renderTarget->frameBuf.width - rect->y), ++ SDL_max(0, data->renderTarget->frameBuf.height - rect->x)); ++ } ++ } else { ++ C3D_SetScissor(GPU_SCISSOR_DISABLE, 0, 0, 0, 0); ++ } ++ break; ++ } ++ ++ case SDL_RENDERCMD_CLEAR: { ++ const Uint8 r = cmd->data.color.r; ++ const Uint8 g = cmd->data.color.g; ++ const Uint8 b = cmd->data.color.b; ++ const Uint8 a = cmd->data.color.a; ++ C3D_FrameBufClear( ++ C3D_GetFrameBuf(), ++ C3D_CLEAR_ALL, ++ COL8888(r, g, b, a), ++ 0 ++ ); ++ break; ++ } ++ ++ case SDL_RENDERCMD_FILL_RECTS: { ++ const size_t first = cmd->data.draw.first / sizeof(VertVCT); ++ const size_t count = cmd->data.draw.count; ++ N3DS_BlendState state = { ++ .texture = NULL, ++ .mode = cmd->data.draw.blend ++ }; ++ N3DS_SetBlendState(data, &state); ++ C3D_DrawArrays(GPU_TRIANGLES, first, count); ++ break; ++ } ++ ++ case SDL_RENDERCMD_COPY: ++ case SDL_RENDERCMD_COPY_EX: { ++ const size_t first = cmd->data.draw.first / sizeof(VertVCT); ++ const size_t count = cmd->data.draw.count; ++ N3DS_BlendState state = { ++ .texture = cmd->data.draw.texture, ++ .mode = cmd->data.draw.blend ++ }; ++ N3DS_SetBlendState(data, &state); ++ C3D_DrawArrays(GPU_TRIANGLES, first, count); ++ break; ++ } ++ ++ case SDL_RENDERCMD_GEOMETRY: { ++ const size_t first = cmd->data.draw.first / sizeof(VertVCT); ++ const size_t count = cmd->data.draw.count; ++ N3DS_BlendState state = { ++ .texture = cmd->data.draw.texture, ++ .mode = cmd->data.draw.blend ++ }; ++ N3DS_SetBlendState(data, &state); ++ C3D_DrawArrays(GPU_TRIANGLES, first, count); ++ break; ++ } ++ ++ case SDL_RENDERCMD_NO_OP: ++ break; ++ } ++ ++ cmd = cmd->next; ++ } ++ ++ return 0; ++} ++ ++static int ++N3DS_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, ++ Uint32 pixel_format, void * pixels, int pitch) ++{ ++ return SDL_Unsupported(); ++} ++ ++static int ++N3DS_RenderPresent(SDL_Renderer * renderer) ++{ ++ N3DS_RenderData *data = (N3DS_RenderData *) renderer->driverdata; ++ ++ if (data->displayListAvail) { ++ C3D_FrameEnd(0); ++ data->displayListAvail = SDL_FALSE; ++ } ++ ++ return 0; ++} ++ ++static void ++N3DS_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture) ++{ ++ N3DS_RenderData *renderdata = (N3DS_RenderData *) renderer->driverdata; ++ N3DS_TextureData *N3DS_texture = (N3DS_TextureData *) texture->driverdata; ++ ++ if (renderdata == 0) ++ return; ++ ++ if (N3DS_texture == 0) ++ return; ++ ++ if (N3DS_texture->renderTarget != NULL) { ++ C3D_RenderTargetDelete(N3DS_texture->renderTarget); ++ } ++ ++ if (N3DS_texture->unswizzledBuffer != NULL) { ++ linearFree(N3DS_texture->unswizzledBuffer); ++ } ++ ++ C3D_TexDelete(&N3DS_texture->texture); ++ SDL_free(N3DS_texture); ++ texture->driverdata = NULL; ++} ++ ++static void ++N3DS_DestroyRenderer(SDL_Renderer * renderer) ++{ ++ N3DS_RenderData *data = (N3DS_RenderData *) renderer->driverdata; ++ if (data) { ++ if (!data->initialized) ++ return; ++ ++ C3D_RenderTargetDelete(data->renderTarget); ++ ++ shaderProgramFree(&data->shaderProgram); ++ DVLB_Free(data->dvlb); ++ ++ C3D_Fini(); ++ ++ data->initialized = SDL_FALSE; ++ SDL_free(data); ++ } ++} ++ ++static int ++N3DS_SetVSync(SDL_Renderer * renderer, const int vsync) ++{ ++ N3DS_RenderData *data = renderer->driverdata; ++ data->vsync = vsync; ++ return 0; ++} ++ ++int ++N3DS_CreateRenderer(SDL_Renderer * renderer, SDL_Window * window, Uint32 flags) ++{ ++ N3DS_RenderData *data; ++ int width, height, pixelFormat; ++ C3D_AttrInfo *attrInfo; ++ bool windowIsBottom; ++ ++ data = (N3DS_RenderData *) SDL_calloc(1, sizeof(*data)); ++ if (!data) { ++ N3DS_DestroyRenderer(renderer); ++ return SDL_OutOfMemory(); ++ } ++ ++ renderer->WindowEvent = N3DS_WindowEvent; ++ renderer->CreateTexture = N3DS_CreateTexture; ++ renderer->UpdateTexture = N3DS_UpdateTexture; ++ renderer->LockTexture = N3DS_LockTexture; ++ renderer->UnlockTexture = N3DS_UnlockTexture; ++ renderer->SetTextureScaleMode = N3DS_SetTextureScaleMode; ++ renderer->SetRenderTarget = N3DS_SetRenderTarget; ++ renderer->QueueSetViewport = N3DS_QueueSetViewport; ++ renderer->QueueSetDrawColor = N3DS_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ ++ renderer->QueueGeometry = N3DS_QueueGeometry; ++ renderer->QueueFillRects = N3DS_QueueFillRects; ++ renderer->QueueCopy = N3DS_QueueCopy; ++ renderer->QueueCopyEx = N3DS_QueueCopyEx; ++ renderer->RunCommandQueue = N3DS_RunCommandQueue; ++ renderer->RenderReadPixels = N3DS_RenderReadPixels; ++ renderer->RenderPresent = N3DS_RenderPresent; ++ renderer->DestroyTexture = N3DS_DestroyTexture; ++ renderer->DestroyRenderer = N3DS_DestroyRenderer; ++ renderer->SetVSync = N3DS_SetVSync; ++ renderer->info = N3DS_RenderDriver.info; ++ renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); ++ renderer->driverdata = data; ++ renderer->window = window; ++ renderer->point_method = SDL_RENDERPOINTMETHOD_GEOMETRY; ++ renderer->line_method = SDL_RENDERLINEMETHOD_GEOMETRY; ++ ++ if (data->initialized != SDL_FALSE) ++ return 0; ++ data->initialized = SDL_TRUE; ++ ++ if (flags & SDL_RENDERER_PRESENTVSYNC) { ++ data->vsync = SDL_TRUE; ++ } else { ++ data->vsync = SDL_FALSE; ++ } ++ ++ C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); ++ ++ /* Load shader */ ++ ++ data->dvlb = DVLB_ParseFile((uint32_t*) n3ds_shader_v, sizeof(n3ds_shader_v)); ++ shaderProgramInit(&data->shaderProgram); ++ shaderProgramSetVsh(&data->shaderProgram, &data->dvlb->DVLE[0]); ++ data->projMtxShaderLoc = shaderInstanceGetUniformLocation(data->shaderProgram.vertexShader, "projection"); ++ ++ /* Create render targets */ ++ ++ SDL_GetWindowSizeInPixels(window, &width, &height); ++ pixelFormat = PixelFormatToN3DSGPU(SDL_GetWindowPixelFormat(window)); ++ /* FIXME: We might need a more resilient way of detecting the window<->screen mapping in the future. */ ++ windowIsBottom = (width == 320); ++ ++ data->renderTarget = C3D_RenderTargetCreate(height, width, pixelFormat, GPU_RB_DEPTH16); ++ data->boundTarget = NULL; ++ ++ C3D_RenderTargetClear(data->renderTarget, C3D_CLEAR_ALL, 0, 0); ++ C3D_RenderTargetSetOutput(data->renderTarget, ++ windowIsBottom ? GFX_BOTTOM : GFX_TOP, GFX_LEFT, ++ GX_TRANSFER_IN_FORMAT(pixelFormat) | GX_TRANSFER_OUT_FORMAT(GPU_RB_RGBA8)); ++ Mtx_OrthoTilt(&data->renderProjMtx, 0.0, width, height, 0.0, -1.0, 1.0, true); ++ ++ C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL); ++ C3D_CullFace(GPU_CULL_NONE); ++ ++ /* Scissoring */ ++ C3D_SetScissor(GPU_SCISSOR_NORMAL, 0, 0, width, height); ++ ++ /* Bind shader */ ++ C3D_BindProgram(&data->shaderProgram); ++ ++ attrInfo = C3D_GetAttrInfo(); ++ AttrInfo_Init(attrInfo); ++ AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 2); ++ AttrInfo_AddLoader(attrInfo, 1, GPU_UNSIGNED_BYTE, 4); ++ AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 2); ++ C3D_SetAttrInfo(attrInfo); ++ ++ /* Texture environments */ ++ C3D_TexEnvInit(&data->envTex); ++ C3D_TexEnvSrc(&data->envTex, C3D_Both, GPU_TEXTURE0, (GPU_TEVSRC) 0, (GPU_TEVSRC) 0); ++ C3D_TexEnvOpRgb(&data->envTex, (GPU_TEVOP_RGB) 0, (GPU_TEVOP_RGB) 0, (GPU_TEVOP_RGB) 0); ++ C3D_TexEnvOpAlpha(&data->envTex, (GPU_TEVOP_A) 0, (GPU_TEVOP_A) 0, (GPU_TEVOP_A) 0); ++ C3D_TexEnvFunc(&data->envTex, C3D_Both, GPU_MODULATE); ++ ++ C3D_TexEnvInit(&data->envNoTex); ++ C3D_TexEnvSrc(&data->envNoTex, C3D_Both, GPU_PRIMARY_COLOR, (GPU_TEVSRC) 0, (GPU_TEVSRC) 0); ++ C3D_TexEnvOpRgb(&data->envNoTex, (GPU_TEVOP_RGB) 0, (GPU_TEVOP_RGB) 0, (GPU_TEVOP_RGB) 0); ++ C3D_TexEnvOpAlpha(&data->envNoTex, (GPU_TEVOP_A) 0, (GPU_TEVOP_A) 0, (GPU_TEVOP_A) 0); ++ C3D_TexEnvFunc(&data->envNoTex, C3D_Both, GPU_REPLACE); ++ ++ ResetBlendState(data, &data->blendState); ++ ++ return 0; ++} ++ ++SDL_RenderDriver N3DS_RenderDriver = { ++ .CreateRenderer = N3DS_CreateRenderer, ++ .info = { ++ .name = "N3DS", ++ .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, ++ .num_texture_formats = 4, ++ .texture_formats = { ++ [0] = SDL_PIXELFORMAT_RGBA8888, // GPU_RGBA8 ++ [1] = SDL_PIXELFORMAT_RGBA5551, // GPU_RGBA5551 ++ [2] = SDL_PIXELFORMAT_RGB565, // GPU_RGB565 ++ [3] = SDL_PIXELFORMAT_RGBA4444 // GPU_RGBA4 ++ }, ++ .max_texture_width = 1024, ++ .max_texture_height = 1024, ++ } ++}; ++ ++#endif /* SDL_VIDEO_RENDER_N3DS */ ++ ++/* vi: set ts=4 sw=4 expandtab: */ ++ +diff --git a/src/render/n3ds/SDL_render_n3ds_shaders.h b/src/render/n3ds/SDL_render_n3ds_shaders.h +new file mode 100644 +index 000000000..ce380d652 +--- /dev/null ++++ b/src/render/n3ds/SDL_render_n3ds_shaders.h +@@ -0,0 +1,49 @@ ++/* ++ Simple DirectMedia Layer ++ Copyright (C) 1997-2022 Sam Lantinga ++ ++ This software is provided 'as-is', without any express or implied ++ warranty. In no event will the authors be held liable for any damages ++ arising from the use of this software. ++ ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ ++ 1. The origin of this software must not be misrepresented; you must not ++ claim that you wrote the original software. If you use this software ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ 3. This notice may not be removed or altered from any source distribution. ++*/ ++ ++#ifndef SDL_RENDER_N3DS_SHADERS_H ++#define SDL_RENDER_N3DS_SHADERS_H ++ ++unsigned char n3ds_shader_v[] = { ++ 0x44, 0x56, 0x4c, 0x42, 0x01, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x44, 0x56, 0x4c, 0x50, ++ 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, ++ 0x09, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x01, 0xf0, 0x07, 0x4e, 0x02, 0xf0, 0x07, 0x4e, ++ 0x03, 0x08, 0x02, 0x08, 0x04, 0x18, 0x02, 0x08, 0x05, 0x28, 0x02, 0x08, 0x06, 0x38, 0x02, 0x08, ++ 0x07, 0x20, 0x40, 0x4c, 0x88, 0xf0, 0x27, 0x20, 0x00, 0x00, 0x00, 0x88, 0x6c, 0x03, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0xe2, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0x0a, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x68, 0xc3, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xc3, 0x06, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x62, 0xc3, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xc3, 0x06, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x6f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xd5, 0x06, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x44, 0x56, 0x4c, 0x45, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, ++ 0x01, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, ++ 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, ++ 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, ++ 0x01, 0x01, 0x37, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, ++ 0x02, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x13, 0x00, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, ++ 0x6f, 0x6e, 0x00, 0x00 ++}; ++ ++#endif // SDL_RENDER_N3DS_SHADERS_H ++ ++/* vi: set ts=4 sw=4 expandtab: */ +diff --git a/src/render/n3ds/shader_src/shader.v.pica b/src/render/n3ds/shader_src/shader.v.pica +new file mode 100644 +index 000000000..9b2b17ab1 +--- /dev/null ++++ b/src/render/n3ds/shader_src/shader.v.pica +@@ -0,0 +1,59 @@ ++; Simple DirectMedia Layer ++; Copyright (C) 1997-2022 Sam Lantinga ++; ++; This software is provided 'as-is', without any express or implied ++; warranty. In no event will the authors be held liable for any damages ++; arising from the use of this software. ++; ++; Permission is granted to anyone to use this software for any purpose, ++; including commercial applications, and to alter it and redistribute it ++; freely, subject to the following restrictions: ++; ++; 1. The origin of this software must not be misrepresented; you must not ++; claim that you wrote the original software. If you use this software ++; in a product, an acknowledgment in the product documentation would be ++; appreciated but is not required. ++; 2. Altered source versions must be plainly marked as such, and must not be ++; misrepresented as being the original software. ++; 3. This notice may not be removed or altered from any source distribution. ++ ++; Uniforms ++.fvec projection[4] ++ ++; Constants ++.constf const(0.0, 1.0, 0.00392156862745098, 0.5) ++.alias ZEROS const.xxxx ; Vector full of zeros ++.alias ONES const.yyyy ; Vector full of ones ++.alias HALFS const.wwww ; Vector full of 0.5s ++ ++; Outputs ++.out outpos position ++.out outclr color ++.out outtc0 texcoord0 ++ ++; Inputs (defined as aliases for convenience) ++.alias inpos v0 ++.alias inclr v1 ++.alias intc0 v2 ++ ++.proc main ++ ; Force the z and w components of inpos to be 0.5 and 1.0 respectively ++ mov r0.xy, inpos ++ mov r0.z, HALFS ++ mov r0.w, ONES ++ ++ ; outpos = projectionMatrix * inpos ++ dp4 outpos.x, projection[0], r0 ++ dp4 outpos.y, projection[1], r0 ++ dp4 outpos.z, projection[2], r0 ++ dp4 outpos.w, projection[3], r0 ++ ++ ; outtc0 = intc0 ++ mov outtc0, intc0 ++ ++ ; Normalize color by multiplying by 1 / 255 ++ mul outclr, const.z, inclr ++ ++ ; We're finished ++ end ++.end diff --git a/3ds/SDL2/PKGBUILD b/3ds/SDL2/PKGBUILD new file mode 100644 index 00000000..df3c5944 --- /dev/null +++ b/3ds/SDL2/PKGBUILD @@ -0,0 +1,37 @@ +pkgname=3ds-SDL2 +pkgver=2.32.10 +pkgrel=1 +pkgdesc="Simple DirectMedia Layer version 2 (Nintendo 3DS port)" +arch=('any') +url="https://libsdl.org/" +license=('MIT') +groups=('3ds-portlibs' '3ds-sdl2-libs') +makedepends=( + '3ds-pkg-config' + 'cmake' + 'dkp-toolchain-vars' +) +options=(!strip libtool staticlibs) +source=("https://github.com/libsdl-org/SDL/releases/download/release-$pkgver/SDL2-$pkgver.tar.gz" + "3ds-initial-accelerated-renderer_2024-10-12.patch") +sha256sums=('5f5993c530f084535c65a6879e9b26ad441169b3e25d789d83287040a9ca5165' + '346f33eb747faa1d65584d775ede95c784e92a7d5fe919279610074c14be7f29') + +prepare() { + cd SDL2-$pkgver + patch -Np1 -i "$srcdir/3ds-initial-accelerated-renderer_2024-10-12.patch" +} + +build() { + source /opt/devkitpro/3dsvars.sh + + arm-none-eabi-cmake \ + -S SDL2-$pkgver \ + -B build \ + -DCMAKE_BUILD_TYPE=Release + cmake --build build +} + +package() { + DESTDIR="$pkgdir" cmake --install build +} diff --git a/3ds/SDL2_gfx/PKGBUILD b/3ds/SDL2_gfx/PKGBUILD new file mode 100644 index 00000000..98c30650 --- /dev/null +++ b/3ds/SDL2_gfx/PKGBUILD @@ -0,0 +1,35 @@ +pkgname=3ds-SDL2_gfx +pkgver=1.0.4 +pkgrel=1 +pkgdesc="SDL2 graphics drawing primitives and other support functions (Nintendo 3DS port)" +arch=('any') +url="https://www.ferzkopp.net/wordpress/2016/01/02/sdl_gfx-sdl2_gfx/" +license=('zlib') +groups=('3ds-portlibs' '3ds-sdl2-libs') +depends=( + '3ds-SDL2' +) +makedepends=( + '3ds-pkg-config' + 'dkp-toolchain-vars' +) +options=(!strip libtool staticlibs) +source=("https://www.ferzkopp.net/Software/SDL2_gfx/SDL2_gfx-$pkgver.tar.gz") +sha256sums=('63e0e01addedc9df2f85b93a248f06e8a04affa014a835c2ea34bfe34e576262') + +build() { + source /opt/devkitpro/3dsvars.sh + + cd SDL2_gfx-$pkgver + ./configure \ + --prefix="$PORTLIBS_PREFIX" \ + --host=arm-none-eabi \ + --disable-shared \ + --enable-static \ + --disable-mmx + make +} + +package() { + make -C SDL2_gfx-$pkgver DESTDIR="$pkgdir" install +} diff --git a/3ds/SDL2_image/PKGBUILD b/3ds/SDL2_image/PKGBUILD new file mode 100644 index 00000000..791fd5c6 --- /dev/null +++ b/3ds/SDL2_image/PKGBUILD @@ -0,0 +1,37 @@ +pkgname=3ds-SDL2_image +pkgver=2.8.8 +pkgrel=1 +pkgdesc="Image decoding for many popular formats for SDL2 (Nintendo 3DS port)" +arch=('any') +url="https://libsdl.org/" +license=('MIT') +groups=('3ds-portlibs' '3ds-sdl2-libs') +depends=( + '3ds-libjpeg-turbo' + '3ds-libpng' + '3ds-SDL2' +) +makedepends=( + '3ds-pkg-config' + 'cmake' + 'dkp-toolchain-vars' +) +options=(!strip libtool staticlibs) +source=("https://github.com/libsdl-org/SDL_image/releases/download/release-$pkgver/SDL2_image-$pkgver.tar.gz") +sha256sums=('2213b56fdaff2220d0e38c8e420cbe1a83c87374190cba8c70af2156097ce30a') + +build() { + source /opt/devkitpro/3dsvars.sh + + arm-none-eabi-cmake \ + -S SDL2_image-$pkgver \ + -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DSDL2IMAGE_BACKEND_STB=OFF \ + -DSDL2IMAGE_DEPS_SHARED=OFF + cmake --build build +} + +package() { + DESTDIR="$pkgdir" cmake --install build +} diff --git a/3ds/SDL2_mixer/PKGBUILD b/3ds/SDL2_mixer/PKGBUILD new file mode 100644 index 00000000..1448fdcd --- /dev/null +++ b/3ds/SDL2_mixer/PKGBUILD @@ -0,0 +1,47 @@ +pkgname=3ds-SDL2_mixer +pkgver=2.8.1 +pkgrel=1 +pkgdesc="Audio mixer that supports various file formats for SDL2 (Nintendo 3DS port)" +arch=('any') +url="https://libsdl.org/" +license=('MIT') +groups=('3ds-portlibs' '3ds-sdl2-libs') +depends=( + '3ds-flac' + '3ds-libmodplug' + '3ds-libvorbisidec' + '3ds-mpg123' + '3ds-opusfile' + '3ds-SDL2' +) +makedepends=( + '3ds-pkg-config' + 'cmake' + 'dkp-toolchain-vars' +) +options=(!strip libtool staticlibs) +source=("https://github.com/libsdl-org/SDL_mixer/releases/download/release-$pkgver/SDL2_mixer-$pkgver.tar.gz") +sha256sums=('cb760211b056bfe44f4a1e180cc7cb201137e4d1572f2002cc1be728efd22660') + +build() { + source /opt/devkitpro/3dsvars.sh + + arm-none-eabi-cmake \ + -S SDL2_mixer-$pkgver \ + -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DSDL2MIXER_DEPS_SHARED=OFF \ + -DSDL2MIXER_FLAC_LIBFLAC=ON \ + -DSDL2MIXER_MIDI=OFF \ + -DSDL2MIXER_MOD_MODPLUG=ON \ + -DSDL2MIXER_MOD_XMP=OFF \ + -DSDL2MIXER_MP3_MPG123=ON \ + -DSDL2MIXER_VORBIS=TREMOR \ + -DSDL2MIXER_WAVPACK=OFF + cmake --build build +} + +package() { + DESTDIR="$pkgdir" cmake --install build + sed 's/tremor/vorbisidec/g' -i "$pkgdir/opt/devkitpro/portlibs/3ds/lib/pkgconfig/SDL2_mixer.pc" +} diff --git a/3ds/SDL2_ttf/PKGBUILD b/3ds/SDL2_ttf/PKGBUILD new file mode 100644 index 00000000..9220f6be --- /dev/null +++ b/3ds/SDL2_ttf/PKGBUILD @@ -0,0 +1,38 @@ +pkgname=3ds-SDL2_ttf +pkgver=2.24.0 +pkgrel=1 +pkgdesc="Support for TrueType (.ttf) font files with SDL2 (Nintendo 3DS port)" +arch=('any') +url="https://github.com/libsdl-org/SDL_ttf" +license=('MIT') +groups=('3ds-portlibs' '3ds-sdl2-libs') +depends=( + '3ds-freetype' + '3ds-SDL2' +) +makedepends=( + '3ds-pkg-config' + 'dkp-toolchain-vars' +) +options=(!strip libtool staticlibs) +source=("https://github.com/libsdl-org/SDL_ttf/releases/download/release-$pkgver/SDL2_ttf-$pkgver.tar.gz") +sha256sums=('0b2bf1e7b6568adbdbc9bb924643f79d9dedafe061fa1ed687d1d9ac4e453bfd') + +build() { + source /opt/devkitpro/3dsvars.sh + + cd SDL2_ttf-$pkgver + sed '/^noinst_PROGRAMS/d' -i Makefile.in + # Linking error with cmake + ./configure \ + --prefix="$PORTLIBS_PREFIX" \ + --host=arm-none-eabi \ + --disable-shared \ + --enable-static \ + --disable-harfbuzz + make +} + +package() { + make -C SDL2_ttf-$pkgver DESTDIR="$pkgdir" install +}