diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a9269a7e1399..86513783bd334 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2854,6 +2854,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) @@ -2907,9 +2910,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 ffcafd895ce8a..1b6ca2cdd7eb9 100644 --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -464,6 +464,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 511c652407320..89d78c40b8b3d 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 0000000000000..cc0cc53b48f15 --- /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 0000000000000..15c34975efa38 --- /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 7ff4f87a19fa1..778538e7c11f4 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 9fdc385f65822..df79d513ecc72 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 0000000000000..d015b8b13ba7c --- /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 0000000000000..ce380d652c6ad --- /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 0000000000000..9b2b17ab19f8b --- /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/test/testdrawchessboard.c b/test/testdrawchessboard.c index 6f0362fdb5ba6..f4a7db514a473 100644 --- a/test/testdrawchessboard.c +++ b/test/testdrawchessboard.c @@ -12,7 +12,10 @@ This file is created by : Nitin Jain (nitin.j4@samsung.com) */ -/* Sample program: Draw a Chess Board by using SDL_CreateSoftwareRenderer API */ +/* Sample program: Draw a Chess Board by using the SDL render API */ + +/* This allows testing SDL_CreateSoftwareRenderer with the window surface API. Undefine it to use the accelerated renderer instead. */ +#define USE_SOFTWARE_RENDERER #include #include @@ -25,9 +28,12 @@ SDL_Window *window; SDL_Renderer *renderer; -SDL_Surface *surface; int done; +#ifdef USE_SOFTWARE_RENDERER +SDL_Surface *surface; +#endif + void DrawChessBoard(void) { int row = 0, column = 0, x = 0; @@ -50,7 +56,6 @@ void DrawChessBoard(void) SDL_RenderFillRect(renderer, &rect); } } - SDL_RenderPresent(renderer); } void loop(void) @@ -58,6 +63,7 @@ void loop(void) SDL_Event e; while (SDL_PollEvent(&e)) { +#ifdef USE_SOFTWARE_RENDERER /* Re-create when window has been resized */ if ((e.type == SDL_WINDOWEVENT) && (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)) { @@ -69,6 +75,7 @@ void loop(void) SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(renderer); } +#endif if (e.type == SDL_QUIT) { done = 1; @@ -87,11 +94,19 @@ void loop(void) } } + /* Clear the rendering surface with the specified color */ + SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_RenderClear(renderer); + DrawChessBoard(); + SDL_RenderPresent(renderer); + +#ifdef USE_SOFTWARE_RENDERER /* Got everything on rendering surface, now Update the drawing image on window screen */ SDL_UpdateWindowSurface(window); +#endif } int main(int argc, char *argv[]) @@ -111,17 +126,17 @@ int main(int argc, char *argv[]) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Window creation fail : %s\n", SDL_GetError()); return 1; } +#ifdef USE_SOFTWARE_RENDERER surface = SDL_GetWindowSurface(window); renderer = SDL_CreateSoftwareRenderer(surface); +#else + renderer = SDL_CreateRenderer(window, -1, 0); +#endif if (!renderer) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Render creation for surface fail : %s\n", SDL_GetError()); return 1; } - /* Clear the rendering surface with the specified color */ - SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); - SDL_RenderClear(renderer); - /* Draw the Image on rendering surface */ done = 0; #ifdef __EMSCRIPTEN__