From 751e9af468e72158c6174a1c326f539ca1bb0ac3 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 8 Jan 2024 10:46:59 +0300 Subject: [PATCH 01/21] wii: make sure argv[] has at least one element All SDL2 tests expect argv to be non empty and access argv[1] without first checking argc. While this is indeed a bug in the test program, it's likely that other programs also do the same, so let's create a workaround in our SDL main. --- src/main/wii/SDL_wii_main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/wii/SDL_wii_main.c b/src/main/wii/SDL_wii_main.c index f399aa7628793..9a42125aae401 100644 --- a/src/main/wii/SDL_wii_main.c +++ b/src/main/wii/SDL_wii_main.c @@ -76,7 +76,13 @@ int main(int argc, char *argv[]) // TODO KEYBOARD_Init(NULL); fatInitDefault(); - /* Call the user's main function */ + /* Call the user's main function. Make sure that argv contains at least one + * element. */ + if (!argv || argv[0] == NULL) { + static const char *dummy_argv[2] = { "app", NULL }; + argc = 1; + argv = (char**)dummy_argv; + } return SDL_main(argc, argv); } From 1fd622c047341f4b08cb3fb7793b61597dfc8728 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 8 Jan 2024 16:22:43 +0300 Subject: [PATCH 02/21] ogc: rendering module (just lines and points so far) --- CMakeLists.txt | 4 +- include/SDL_config.h.cmake | 1 + src/render/SDL_render.c | 3 + src/render/SDL_sysrender.h | 1 + src/render/ogc/SDL_render_ogc.c | 309 +++++++++++++++++++++++++++++ src/video/ogc/SDL_ogcframebuffer.c | 8 +- src/video/ogc/SDL_ogcvideo.c | 13 ++ src/video/ogc/SDL_ogcvideo.h | 2 + 8 files changed, 333 insertions(+), 8 deletions(-) create mode 100644 src/render/ogc/SDL_render_ogc.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c6552832ec69a..e6834c350ac81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2931,8 +2931,10 @@ elseif(OGC) if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_OGC 1) - file(GLOB OGC_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/ogc/*.c) + set(SDL_VIDEO_RENDER_OGC 1) + file(GLOB OGC_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/ogc/*.c ${SDL2_SOURCE_DIR}/src/render/ogc/*.c) list(APPEND SOURCE_FILES ${OGC_VIDEO_SOURCES}) + set(SDL_VIDEO_OPENGL 0) set(HAVE_SDL_VIDEO TRUE) endif() diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake index 8d9cb5caf1d8f..13f9a6022f52a 100644 --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -472,6 +472,7 @@ #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@ +#cmakedefine SDL_VIDEO_RENDER_OGC @SDL_VIDEO_RENDER_OGC@ /* Enable OpenGL support */ #cmakedefine SDL_VIDEO_OPENGL @SDL_VIDEO_OPENGL@ diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index b650c6ac40189..dc7a5b0b26d06 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -124,6 +124,9 @@ static const SDL_RenderDriver *render_drivers[] = { #if SDL_VIDEO_RENDER_VITA_GXM &VITA_GXM_RenderDriver, #endif +#ifdef SDL_VIDEO_RENDER_OGC + &OGC_RenderDriver, +#endif #if SDL_VIDEO_RENDER_SW &SW_RenderDriver #endif diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 36e9555bb162f..d3db2b38795b0 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -307,6 +307,7 @@ extern SDL_RenderDriver PS2_RenderDriver; extern SDL_RenderDriver PSP_RenderDriver; extern SDL_RenderDriver SW_RenderDriver; extern SDL_RenderDriver VITA_GXM_RenderDriver; +extern SDL_RenderDriver OGC_RenderDriver; /* Blend mode functions */ extern SDL_BlendFactor SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode); diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c new file mode 100644 index 0000000000000..75b03cc7702ab --- /dev/null +++ b/src/render/ogc/SDL_render_ogc.c @@ -0,0 +1,309 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 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" + +#ifdef SDL_VIDEO_RENDER_OGC + +#include "../SDL_sysrender.h" +#include "SDL_hints.h" + +#include "../../video/ogc/SDL_ogcgxcommon.h" +#include "../../video/ogc/SDL_ogcvideo.h" + +#include +#include +#include + +typedef struct +{ + u32 drawColor; +} OGC_RenderData; + +static void OGC_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) +{ +} + +static int OGC_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + // TODO + return 0; +} + +static int OGC_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, void **pixels, int *pitch) +{ + // TODO + return 0; +} + +static void OGC_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + // TODO +} + +static int OGC_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, const void *pixels, int pitch) +{ + // TODO + return 0; +} + +static void OGC_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode) +{ + // TODO +} + +static int OGC_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) +{ + return 0; +} + +static int OGC_QueueSetViewport(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + return 0; /* nothing to do in this backend. */ +} + +static int OGC_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, + const SDL_FPoint *points, int count) +{ + size_t size = count * sizeof(SDL_FPoint); + SDL_FPoint *vertices = SDL_AllocateRenderVertices(renderer, size, + 4, &cmd->data.draw.first); + if (!vertices) { + return -1; + } + + cmd->data.draw.count = count; + SDL_memcpy(vertices, points, size); + return 0; +} + +static int OGC_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) +{ + // TODO + return 0; +} + +static int OGC_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + // TODO + return 0; +} + +static int OGC_RenderSetClipRect(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + // TODO + return 0; +} + +static int OGC_RenderSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + // TODO + return 0; +} + +static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + // TODO + return 0; +} + +static void OGC_SetBlendMode(SDL_Renderer *renderer, int blendMode) +{ + // TODO +} + +static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) +{ + // TODO + return 0; +} + +int OGC_RenderPrimitive(SDL_Renderer *renderer, u8 primitive, + void *vertices, SDL_RenderCommand *cmd) +{ + const size_t count = cmd->data.draw.count; + const SDL_FPoint *verts = (SDL_FPoint *)(vertices + cmd->data.draw.first); + + const Uint8 r = cmd->data.draw.r; + const Uint8 g = cmd->data.draw.g; + const Uint8 b = cmd->data.draw.b; + const Uint8 a = cmd->data.draw.a; + OGC_SetBlendMode(renderer, cmd->data.draw.blend); + + /* TODO: optimize state changes. We can probably avoid passing the color, + * if we set it on a register using GX_SetTevColor(). */ + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + + GX_Begin(primitive, GX_VTXFMT0, count); + for (int i = 0; i < count; i++) { + GX_Position2f32(verts[i].x, verts[i].y); + GX_Color4u8(r, g, b, a); + } + GX_End(); + + return 0; +} + +static int OGC_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETVIEWPORT: + OGC_RenderSetViewPort(renderer, cmd); + break; + case SDL_RENDERCMD_SETCLIPRECT: + OGC_RenderSetClipRect(renderer, cmd); + break; + case SDL_RENDERCMD_SETDRAWCOLOR: + OGC_RenderSetDrawColor(renderer, cmd); + break; + case SDL_RENDERCMD_CLEAR: + OGC_RenderClear(renderer, cmd); + break; + case SDL_RENDERCMD_DRAW_POINTS: + OGC_RenderPrimitive(renderer, GX_POINTS, vertices, cmd); + break; + case SDL_RENDERCMD_DRAW_LINES: + OGC_RenderPrimitive(renderer, GX_LINESTRIP, vertices, cmd); + break; + case SDL_RENDERCMD_FILL_RECTS: /* unused */ + break; + case SDL_RENDERCMD_COPY: /* unused */ + break; + case SDL_RENDERCMD_COPY_EX: /* unused */ + break; + case SDL_RENDERCMD_GEOMETRY: + OGC_RenderGeometry(renderer, vertices, cmd); + break; + case SDL_RENDERCMD_NO_OP: + break; + } + cmd = cmd->next; + } + return 0; +} + +static int OGC_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, + Uint32 format, void *pixels, int pitch) +{ + return SDL_Unsupported(); +} + +static int OGC_RenderPresent(SDL_Renderer *renderer) +{ + GX_DrawDone(); + + OGC_VideoFlip(renderer->window); + return 0; +} + +static void OGC_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ +} + +static void OGC_DestroyRenderer(SDL_Renderer *renderer) +{ + OGC_RenderData *data = renderer->driverdata; + + if (data) { + SDL_free(data); + } + + SDL_free(renderer); +} + +static int OGC_SetVSync(SDL_Renderer *renderer, const int vsync) +{ + return 0; +} + +static SDL_Renderer *OGC_CreateRenderer(SDL_Window *window, Uint32 flags) +{ + SDL_Renderer *renderer; + OGC_RenderData *data; + + renderer = (SDL_Renderer *)SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + + data = (OGC_RenderData *)SDL_calloc(1, sizeof(*data)); + if (!data) { + OGC_DestroyRenderer(renderer); + SDL_OutOfMemory(); + return NULL; + } + + renderer->WindowEvent = OGC_WindowEvent; + renderer->CreateTexture = OGC_CreateTexture; + renderer->UpdateTexture = OGC_UpdateTexture; + renderer->LockTexture = OGC_LockTexture; + renderer->UnlockTexture = OGC_UnlockTexture; + renderer->SetTextureScaleMode = OGC_SetTextureScaleMode; + renderer->SetRenderTarget = OGC_SetRenderTarget; + renderer->QueueSetViewport = OGC_QueueSetViewport; + renderer->QueueSetDrawColor = OGC_QueueSetViewport; + renderer->QueueDrawPoints = OGC_QueueDrawPoints; + renderer->QueueDrawLines = OGC_QueueDrawPoints; + renderer->QueueGeometry = OGC_QueueGeometry; + renderer->RunCommandQueue = OGC_RunCommandQueue; + renderer->RenderReadPixels = OGC_RenderReadPixels; + renderer->RenderPresent = OGC_RenderPresent; + renderer->DestroyTexture = OGC_DestroyTexture; + renderer->DestroyRenderer = OGC_DestroyRenderer; + renderer->SetVSync = OGC_SetVSync; + renderer->info = OGC_RenderDriver.info; + renderer->driverdata = data; + renderer->window = window; + + return renderer; +} + +SDL_RenderDriver OGC_RenderDriver = { + .CreateRenderer = OGC_CreateRenderer, + .info = { + .name = "ogc", + .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, + .num_texture_formats = 2, + .texture_formats = { + [0] = SDL_PIXELFORMAT_RGB565, + [1] = SDL_PIXELFORMAT_ARGB8888, + // TODO: add more + }, + .max_texture_width = 1024, + .max_texture_height = 1024, + } +}; + +#endif /* SDL_VIDEO_RENDER_OGC */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/ogc/SDL_ogcframebuffer.c b/src/video/ogc/SDL_ogcframebuffer.c index 0c53c1452c248..f7e8a4835e0df 100644 --- a/src/video/ogc/SDL_ogcframebuffer.c +++ b/src/video/ogc/SDL_ogcframebuffer.c @@ -94,7 +94,6 @@ int SDL_OGC_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, v int SDL_OGC_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects) { - SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata; u8 gx_format; @@ -105,12 +104,7 @@ int SDL_OGC_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *r draw_screen_rect(window); GX_DrawDone(); - GX_CopyDisp(videodata->xfb[0], GX_TRUE); - GX_DrawDone(); - GX_Flush(); - - VIDEO_Flush(); - VIDEO_WaitVSync(); + OGC_VideoFlip(window); return 0; } diff --git a/src/video/ogc/SDL_ogcvideo.c b/src/video/ogc/SDL_ogcvideo.c index 22db5c4cabca6..81f3ee546519b 100644 --- a/src/video/ogc/SDL_ogcvideo.c +++ b/src/video/ogc/SDL_ogcvideo.c @@ -172,6 +172,19 @@ void OGC_VideoQuit(_THIS) free(MEM_K1_TO_K0(videodata->xfb[1])); } +void OGC_VideoFlip(SDL_Window *window) +{ + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; + + GX_CopyDisp(videodata->xfb[0], GX_TRUE); + GX_DrawDone(); + GX_Flush(); + + VIDEO_Flush(); + VIDEO_WaitVSync(); +} + #endif /* SDL_VIDEO_DRIVER_OGC */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/ogc/SDL_ogcvideo.h b/src/video/ogc/SDL_ogcvideo.h index b653f12bc541a..e89f943365ba9 100644 --- a/src/video/ogc/SDL_ogcvideo.h +++ b/src/video/ogc/SDL_ogcvideo.h @@ -41,6 +41,8 @@ typedef struct SDL_WindowData SDL_PixelFormatEnum surface_format; } SDL_WindowData; +void OGC_VideoFlip(SDL_Window *window); + #endif /* SDL_ogcvideo_h_ */ /* vi: set ts=4 sw=4 expandtab: */ From 947cda50cbacf9f1e6aa503a6285e18428971763 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 8 Jan 2024 17:03:16 +0300 Subject: [PATCH 03/21] ogc: do not repeat color information --- src/render/ogc/SDL_render_ogc.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 75b03cc7702ab..bc30d08de2af4 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -145,27 +145,29 @@ int OGC_RenderPrimitive(SDL_Renderer *renderer, u8 primitive, { const size_t count = cmd->data.draw.count; const SDL_FPoint *verts = (SDL_FPoint *)(vertices + cmd->data.draw.first); + GXColor c = { + cmd->data.draw.r, + cmd->data.draw.g, + cmd->data.draw.b, + cmd->data.draw.a + }; - const Uint8 r = cmd->data.draw.r; - const Uint8 g = cmd->data.draw.g; - const Uint8 b = cmd->data.draw.b; - const Uint8 a = cmd->data.draw.a; OGC_SetBlendMode(renderer, cmd->data.draw.blend); - /* TODO: optimize state changes. We can probably avoid passing the color, - * if we set it on a register using GX_SetTevColor(). */ - GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + /* TODO: optimize state changes. */ + GX_SetTevColor(GX_TEVREG0, c); + GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); + GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_A0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); + GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GX_ClearVtxDesc(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); - GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); - GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GX_Begin(primitive, GX_VTXFMT0, count); for (int i = 0; i < count; i++) { GX_Position2f32(verts[i].x, verts[i].y); - GX_Color4u8(r, g, b, a); } GX_End(); From ff85121d293ef8c51032218e2710758e4334d519 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Mon, 8 Jan 2024 22:20:35 +0300 Subject: [PATCH 04/21] ogc: render, add fillrects --- src/render/ogc/SDL_render_ogc.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index bc30d08de2af4..abe9a0c950e8e 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -96,6 +96,30 @@ static int OGC_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, return 0; } +static int OGC_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, + const SDL_FRect *rects, int count) +{ + size_t size = count * sizeof(SDL_FPoint) * 4; + SDL_FPoint *vertices = SDL_AllocateRenderVertices(renderer, size, + 4, &cmd->data.draw.first); + if (!vertices) { + return -1; + } + + cmd->data.draw.count = count; + for (int i = 0; i < count; i++) { + vertices[i].x = rects[i].x; + vertices[i].y = rects[i].y; + vertices[i+1].x = rects[i].x + rects[i].w; + vertices[i+1].y = rects[i].y; + vertices[i+2].x = rects[i].x + rects[i].w; + vertices[i+2].y = rects[i].y + rects[i].h; + vertices[i+3].x = rects[i].x; + vertices[i+3].y = rects[i].y + rects[i].h; + } + return 0; +} + static int OGC_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, @@ -143,7 +167,7 @@ static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_Render int OGC_RenderPrimitive(SDL_Renderer *renderer, u8 primitive, void *vertices, SDL_RenderCommand *cmd) { - const size_t count = cmd->data.draw.count; + size_t count = cmd->data.draw.count; const SDL_FPoint *verts = (SDL_FPoint *)(vertices + cmd->data.draw.first); GXColor c = { cmd->data.draw.r, @@ -165,6 +189,8 @@ int OGC_RenderPrimitive(SDL_Renderer *renderer, u8 primitive, GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); + if (primitive == GX_QUADS) count *= 4; + GX_Begin(primitive, GX_VTXFMT0, count); for (int i = 0; i < count; i++) { GX_Position2f32(verts[i].x, verts[i].y); @@ -196,7 +222,8 @@ static int OGC_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, v case SDL_RENDERCMD_DRAW_LINES: OGC_RenderPrimitive(renderer, GX_LINESTRIP, vertices, cmd); break; - case SDL_RENDERCMD_FILL_RECTS: /* unused */ + case SDL_RENDERCMD_FILL_RECTS: + OGC_RenderPrimitive(renderer, GX_QUADS, vertices, cmd); break; case SDL_RENDERCMD_COPY: /* unused */ break; @@ -276,6 +303,7 @@ static SDL_Renderer *OGC_CreateRenderer(SDL_Window *window, Uint32 flags) renderer->QueueSetDrawColor = OGC_QueueSetViewport; renderer->QueueDrawPoints = OGC_QueueDrawPoints; renderer->QueueDrawLines = OGC_QueueDrawPoints; + renderer->QueueFillRects = OGC_QueueFillRects; renderer->QueueGeometry = OGC_QueueGeometry; renderer->RunCommandQueue = OGC_RunCommandQueue; renderer->RenderReadPixels = OGC_RenderReadPixels; From 26b4074e0e0dbc59bfd6d96d4475bcbe483c083e Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 9 Jan 2024 14:34:16 +0300 Subject: [PATCH 05/21] ogc: basic implementation of geometry drawing --- src/render/ogc/SDL_render_ogc.c | 155 ++++++++++++++++++++++++++++++-- src/video/ogc/SDL_ogcpixels.c | 5 ++ src/video/ogc/SDL_ogcpixels.h | 2 + 3 files changed, 155 insertions(+), 7 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index abe9a0c950e8e..a5d395a150bd6 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -26,6 +26,7 @@ #include "SDL_hints.h" #include "../../video/ogc/SDL_ogcgxcommon.h" +#include "../../video/ogc/SDL_ogcpixels.h" #include "../../video/ogc/SDL_ogcvideo.h" #include @@ -37,13 +38,37 @@ typedef struct u32 drawColor; } OGC_RenderData; +typedef struct +{ + void *texels; + u16 pitch; + u8 format; +} OGC_TextureData; + static void OGC_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) { } static int OGC_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) { - // TODO + u32 texture_size; + OGC_TextureData *ogc_tex; + + ogc_tex = SDL_calloc(1, sizeof(OGC_TextureData)); + if (!ogc_tex) { + return SDL_OutOfMemory(); + } + + ogc_tex->format = OGC_texture_format_from_SDL(texture->format); + texture_size = GX_GetTexBufferSize(texture->w, texture->h, ogc_tex->format, + GX_FALSE, 0); + ogc_tex->texels = memalign(32, texture_size); + if (!ogc_tex->texels) { + SDL_free(ogc_tex); + return SDL_OutOfMemory(); + } + + texture->driverdata = ogc_tex; return 0; } @@ -62,7 +87,13 @@ static void OGC_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) static int OGC_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { - // TODO + OGC_TextureData *ogc_tex = texture->driverdata; + u8 format; + + // TODO: take rect into account + OGC_pixels_to_texture((void*)pixels, texture->format, texture->w, texture->h, + pitch, ogc_tex->texels, &format); + return 0; } @@ -121,11 +152,66 @@ static int OGC_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, } static int OGC_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, + 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) { - // TODO + int i; + int count = indices ? num_indices : num_vertices; + size_t size_per_element; + char *vertices; + + cmd->data.draw.count = count; + size_indices = indices ? size_indices : 0; + + size_per_element = sizeof(SDL_FPoint) + sizeof(SDL_Color); + if (texture) { + size_per_element += sizeof(SDL_FPoint); + } + + vertices = SDL_AllocateRenderVertices(renderer, count * size_per_element, + 4, &cmd->data.draw.first); + if (!vertices) { + return -1; + } + + for (i = 0; i < count; i++) { + int j; + float *xy_; + float *uv_; + SDL_Color col; + char *vertex; + SDL_FPoint *vertex_xy; + 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); + + vertex = vertices + size_per_element * i; + + vertex_xy = (SDL_FPoint *)vertex; + vertex_xy->x = xy_[0]; + vertex_xy->y = xy_[1]; + + *(SDL_Color *)(vertex + sizeof(SDL_FPoint)) = col; + + if (texture) { + SDL_FPoint *vertex_uv = (SDL_FPoint *)(vertex + sizeof(SDL_FPoint) + sizeof(SDL_Color)); + vertex_uv->x = uv_[0]; + vertex_uv->y = uv_[1]; + } + } + return 0; } @@ -158,9 +244,57 @@ static void OGC_SetBlendMode(SDL_Renderer *renderer, int blendMode) // TODO } -static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) +static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, + SDL_RenderCommand *cmd) { - // TODO + const size_t count = cmd->data.draw.count; + SDL_Texture *texture = cmd->data.draw.texture; + size_t size_per_element; + + OGC_SetBlendMode(renderer, cmd->data.draw.blend); + + size_per_element = sizeof(SDL_FPoint) + sizeof(SDL_Color); + if (texture) { + size_per_element += sizeof(SDL_FPoint); + OGC_TextureData *ogc_tex = texture->driverdata; + OGC_load_texture(ogc_tex->texels, texture->w, texture->h, + ogc_tex->format); + } + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + if (texture) { + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + GX_SetNumTexGens(1); + GX_SetNumChans(1); + GX_SetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, 0, + GX_DF_NONE, GX_AF_NONE); + + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + } else { + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + } + + GX_Begin(GX_TRIANGLES, GX_VTXFMT0, count); + for (int i = 0; i < count; i++) { + void *vertex = vertices + cmd->data.draw.first + size_per_element * i; + SDL_FPoint *vertex_xy = vertex; + SDL_Color *c = (SDL_Color*)(vertex + sizeof(SDL_FPoint)); + + GX_Position2f32(vertex_xy->x, vertex_xy->y); + GX_Color4u8(c->r, c->g, c->b, c->a); + if (texture) { + SDL_FPoint *vertex_uv = (SDL_FPoint *)(vertex + sizeof(SDL_FPoint) + sizeof(SDL_Color)); + GX_TexCoord2f32(vertex_uv->x, vertex_uv->y); + } + } + GX_End(); return 0; } @@ -256,6 +390,13 @@ static int OGC_RenderPresent(SDL_Renderer *renderer) static void OGC_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) { + OGC_TextureData *ogc_tex = texture->driverdata; + + if (ogc_tex) { + free(ogc_tex->texels); + SDL_free(ogc_tex); + texture->driverdata = NULL; + } } static void OGC_DestroyRenderer(SDL_Renderer *renderer) @@ -326,7 +467,7 @@ SDL_RenderDriver OGC_RenderDriver = { .num_texture_formats = 2, .texture_formats = { [0] = SDL_PIXELFORMAT_RGB565, - [1] = SDL_PIXELFORMAT_ARGB8888, + [1] = SDL_PIXELFORMAT_RGBA8888, // TODO: add more }, .max_texture_width = 1024, diff --git a/src/video/ogc/SDL_ogcpixels.c b/src/video/ogc/SDL_ogcpixels.c index b54690f547f33..c47138287aa54 100644 --- a/src/video/ogc/SDL_ogcpixels.c +++ b/src/video/ogc/SDL_ogcpixels.c @@ -313,4 +313,9 @@ void OGC_pixels_from_texture(void *pixels, const SDL_PixelFormatEnum format, } } +u8 OGC_texture_format_from_SDL(const SDL_PixelFormatEnum format) +{ + return texture_format_from_SDL(format); +} + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/ogc/SDL_ogcpixels.h b/src/video/ogc/SDL_ogcpixels.h index f036e7f80cf3a..b6ac8a0583c58 100644 --- a/src/video/ogc/SDL_ogcpixels.h +++ b/src/video/ogc/SDL_ogcpixels.h @@ -34,6 +34,8 @@ void OGC_pixels_from_texture(void *pixels, SDL_PixelFormatEnum format, int16_t w, int16_t h, int16_t pitch, void *texels); +u8 OGC_texture_format_from_SDL(const SDL_PixelFormatEnum format); + #endif /* SDL_ogcpixels_h_ */ /* vi: set ts=4 sw=4 expandtab: */ From 264c7c7f1256533555c2a6e9a7f4bfd22880ab71 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 9 Jan 2024 15:06:06 +0300 Subject: [PATCH 06/21] ogc: implement RenderClear --- src/render/ogc/SDL_render_ogc.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index a5d395a150bd6..466e2f0ecd23d 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -235,7 +235,31 @@ static int OGC_RenderSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { - // TODO + GXColor c = { + cmd->data.color.r, + cmd->data.color.g, + cmd->data.color.b, + cmd->data.color.a + }; + + /* TODO: optimize state changes. */ + GX_SetTevColor(GX_TEVREG0, c); + GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); + GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_A0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); + GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); + + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position2s16(0, 0); + GX_Position2s16(renderer->window->w, 0); + GX_Position2s16(renderer->window->w, renderer->window->h); + GX_Position2s16(0, renderer->window->h); + GX_End(); + return 0; } From 996c9c1338b8d6c22905f3094df334151c6aca03 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 9 Jan 2024 21:03:03 +0300 Subject: [PATCH 07/21] ogc: start implementing blend modes in geometry --- src/render/ogc/SDL_render_ogc.c | 36 ++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 466e2f0ecd23d..c9d45e2c58264 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -43,6 +43,7 @@ typedef struct void *texels; u16 pitch; u8 format; + u8 needed_stages; // Normally 1, set to 2 for palettized formats } OGC_TextureData; static void OGC_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) @@ -60,6 +61,7 @@ static int OGC_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) } ogc_tex->format = OGC_texture_format_from_SDL(texture->format); + ogc_tex->needed_stages = (ogc_tex->format == GX_TF_CI8) ? 2 : 1; texture_size = GX_GetTexBufferSize(texture->w, texture->h, ogc_tex->format, GX_FALSE, 0); ogc_tex->texels = memalign(32, texture_size); @@ -242,6 +244,7 @@ static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) cmd->data.color.a }; + GX_SetNumTevStages(1); /* TODO: optimize state changes. */ GX_SetTevColor(GX_TEVREG0, c); GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); @@ -291,16 +294,39 @@ static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); if (texture) { + OGC_TextureData *ogc_tex = texture->driverdata; + u8 stage = GX_TEVSTAGE0 + ogc_tex->needed_stages - 1; + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); GX_SetNumTexGens(1); - GX_SetNumChans(1); - GX_SetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, 0, - GX_DF_NONE, GX_AF_NONE); GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); - GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); - GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTevOrder(stage, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + switch (cmd->data.draw.blend) { + case SDL_BLENDMODE_BLEND: + GX_SetTevOp(stage, GX_MODULATE); + break; + case SDL_BLENDMODE_MOD: + GX_SetTevColorIn(stage, GX_CC_ZERO, GX_CC_RASC, GX_CC_TEXC, GX_CC_ZERO); + GX_SetTevAlphaIn(stage, GX_CA_RASA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); + break; + case SDL_BLENDMODE_NONE: + /* With SDL_BLENDMODE_NONE the transparent pixels are first + * converted to black, so let's use two stages: + * 1) For color, we blend the texture color with black, using the + * texture alpha as factor. For alpha, we generate full opacity + * 2) We blend the result from stage 1 with the rasterizer + */ + GX_SetTevColorIn(stage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_TEXA, GX_CC_ZERO); + GX_SetTevAlphaIn(stage, GX_CA_RASA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); + stage++; + GX_SetTevOrder(stage, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTevColorIn(stage, GX_CC_ZERO, GX_CC_RASC, GX_CC_CPREV, GX_CC_ZERO); + GX_SetTevAlphaIn(stage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV); + break; + } + GX_SetNumTevStages(stage - GX_TEVSTAGE0 + 1); } else { GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); } From 176cc4ead22a798c892a4e689aaa3a5b508bdb44 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Tue, 9 Jan 2024 23:34:03 +0300 Subject: [PATCH 08/21] ogc: proper implementation of blend modes --- src/render/ogc/SDL_render_ogc.c | 43 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index c9d45e2c58264..e565ae2cfa750 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -244,6 +244,7 @@ static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) cmd->data.color.a }; + GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); GX_SetNumTevStages(1); /* TODO: optimize state changes. */ GX_SetTevColor(GX_TEVREG0, c); @@ -268,7 +269,23 @@ static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) static void OGC_SetBlendMode(SDL_Renderer *renderer, int blendMode) { - // TODO + switch (blendMode) { + case SDL_BLENDMODE_NONE: + GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_BLEND: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_ADD: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_MOD: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_MUL: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + } } static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, @@ -303,29 +320,7 @@ static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); GX_SetTevOrder(stage, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); - switch (cmd->data.draw.blend) { - case SDL_BLENDMODE_BLEND: - GX_SetTevOp(stage, GX_MODULATE); - break; - case SDL_BLENDMODE_MOD: - GX_SetTevColorIn(stage, GX_CC_ZERO, GX_CC_RASC, GX_CC_TEXC, GX_CC_ZERO); - GX_SetTevAlphaIn(stage, GX_CA_RASA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); - break; - case SDL_BLENDMODE_NONE: - /* With SDL_BLENDMODE_NONE the transparent pixels are first - * converted to black, so let's use two stages: - * 1) For color, we blend the texture color with black, using the - * texture alpha as factor. For alpha, we generate full opacity - * 2) We blend the result from stage 1 with the rasterizer - */ - GX_SetTevColorIn(stage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_TEXA, GX_CC_ZERO); - GX_SetTevAlphaIn(stage, GX_CA_RASA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); - stage++; - GX_SetTevOrder(stage, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); - GX_SetTevColorIn(stage, GX_CC_ZERO, GX_CC_RASC, GX_CC_CPREV, GX_CC_ZERO); - GX_SetTevAlphaIn(stage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV); - break; - } + GX_SetTevOp(stage, GX_MODULATE); GX_SetNumTevStages(stage - GX_TEVSTAGE0 + 1); } else { GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); From d56e26b40da08f76e0f8a4d061dbee3c93b2590f Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Wed, 10 Jan 2024 17:29:40 +0300 Subject: [PATCH 09/21] ogc: minimize blend mode changes --- src/render/ogc/SDL_render_ogc.c | 57 ++++++++++++++++++++------------- src/video/ogc/SDL_ogcvideo.c | 2 +- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index e565ae2cfa750..19c3d8d5c2bf0 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -35,7 +35,7 @@ typedef struct { - u32 drawColor; + SDL_BlendMode current_blend_mode; } OGC_RenderData; typedef struct @@ -50,6 +50,38 @@ static void OGC_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event { } +static inline void OGC_SetBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend_mode) +{ + OGC_RenderData *data = renderer->driverdata; + + if (blend_mode == data->current_blend_mode) { + /* Nothing to do */ + return; + } + + switch (blend_mode) { + case SDL_BLENDMODE_NONE: + GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_BLEND: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_ADD: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_MOD: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR); + break; + case SDL_BLENDMODE_MUL: + GX_SetBlendMode(GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + break; + default: + return; + } + + data->current_blend_mode = blend_mode; +} + static int OGC_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) { u32 texture_size; @@ -267,27 +299,6 @@ static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) return 0; } -static void OGC_SetBlendMode(SDL_Renderer *renderer, int blendMode) -{ - switch (blendMode) { - case SDL_BLENDMODE_NONE: - GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); - break; - case SDL_BLENDMODE_BLEND: - GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); - break; - case SDL_BLENDMODE_ADD: - GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR); - break; - case SDL_BLENDMODE_MOD: - GX_SetBlendMode(GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR); - break; - case SDL_BLENDMODE_MUL: - GX_SetBlendMode(GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_INVSRCALPHA, GX_LO_CLEAR); - break; - } -} - static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) { @@ -478,6 +489,8 @@ static SDL_Renderer *OGC_CreateRenderer(SDL_Window *window, Uint32 flags) return NULL; } + data->current_blend_mode = SDL_BLENDMODE_NONE; + renderer->WindowEvent = OGC_WindowEvent; renderer->CreateTexture = OGC_CreateTexture; renderer->UpdateTexture = OGC_UpdateTexture; diff --git a/src/video/ogc/SDL_ogcvideo.c b/src/video/ogc/SDL_ogcvideo.c index 81f3ee546519b..adf37dd52215b 100644 --- a/src/video/ogc/SDL_ogcvideo.c +++ b/src/video/ogc/SDL_ogcvideo.c @@ -133,7 +133,7 @@ int OGC_VideoInit(_THIS) ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE)); GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); GX_SetCullMode(GX_CULL_NONE); - GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); From 6696e3cc64d49cd707da69e184c3c29ec71e8669 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Wed, 10 Jan 2024 17:30:10 +0300 Subject: [PATCH 10/21] ogc: move texture code in single block --- src/render/ogc/SDL_render_ogc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 19c3d8d5c2bf0..e4d7ffc75005f 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -309,12 +309,6 @@ static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, OGC_SetBlendMode(renderer, cmd->data.draw.blend); size_per_element = sizeof(SDL_FPoint) + sizeof(SDL_Color); - if (texture) { - size_per_element += sizeof(SDL_FPoint); - OGC_TextureData *ogc_tex = texture->driverdata; - OGC_load_texture(ogc_tex->texels, texture->w, texture->h, - ogc_tex->format); - } GX_ClearVtxDesc(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); @@ -323,7 +317,12 @@ static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); if (texture) { OGC_TextureData *ogc_tex = texture->driverdata; - u8 stage = GX_TEVSTAGE0 + ogc_tex->needed_stages - 1; + u8 stage; + + size_per_element += sizeof(SDL_FPoint); + OGC_load_texture(ogc_tex->texels, texture->w, texture->h, + ogc_tex->format); + stage = GX_TEVSTAGE0 + ogc_tex->needed_stages - 1; GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); From b6375570e6d83593429fccf617adb83e0bf8c1e9 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Thu, 11 Jan 2024 18:36:08 +0300 Subject: [PATCH 11/21] ogc: implement RenderClear via XFB copy This has the advantage that it operates on the whole EFB, ignoring viewport and clip regions. --- src/render/ogc/SDL_render_ogc.c | 39 +++++++++++++++--------------- src/video/ogc/SDL_ogcframebuffer.c | 2 +- src/video/ogc/SDL_ogcgxcommon.h | 2 ++ src/video/ogc/SDL_ogcvideo.c | 12 ++++++--- src/video/ogc/SDL_ogcvideo.h | 3 ++- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index e4d7ffc75005f..011ac8dcc8c38 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -36,6 +36,8 @@ typedef struct { SDL_BlendMode current_blend_mode; + GXColor clear_color; + int ops_after_present; } OGC_RenderData; typedef struct @@ -269,6 +271,8 @@ static int OGC_RenderSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { + OGC_RenderData *data = renderer->driverdata; + GXColor c = { cmd->data.color.r, cmd->data.color.g, @@ -276,25 +280,16 @@ static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) cmd->data.color.a }; - GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); - GX_SetNumTevStages(1); - /* TODO: optimize state changes. */ - GX_SetTevColor(GX_TEVREG0, c); - GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); - GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_A0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); - GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); - GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); - - GX_ClearVtxDesc(); - GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); - GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); + /* If nothing has been drawn after Present, and if the clear color has not + * changed, there's no need to do anything here. */ + if (data->ops_after_present == 0 && + GX_COLOR_AS_U32(c) == GX_COLOR_AS_U32(data->clear_color)) { + return 0; + } - GX_Begin(GX_QUADS, GX_VTXFMT0, 4); - GX_Position2s16(0, 0); - GX_Position2s16(renderer->window->w, 0); - GX_Position2s16(renderer->window->w, renderer->window->h); - GX_Position2s16(0, renderer->window->h); - GX_End(); + data->clear_color = c; + GX_SetCopyClear(c, GX_MAX_Z24); + GX_CopyDisp(OGC_video_get_xfb(SDL_GetVideoDevice()), GX_TRUE); return 0; } @@ -302,10 +297,12 @@ static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) { + OGC_RenderData *data = renderer->driverdata; const size_t count = cmd->data.draw.count; SDL_Texture *texture = cmd->data.draw.texture; size_t size_per_element; + data->ops_after_present++; OGC_SetBlendMode(renderer, cmd->data.draw.blend); size_per_element = sizeof(SDL_FPoint) + sizeof(SDL_Color); @@ -356,6 +353,7 @@ static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, int OGC_RenderPrimitive(SDL_Renderer *renderer, u8 primitive, void *vertices, SDL_RenderCommand *cmd) { + OGC_RenderData *data = renderer->driverdata; size_t count = cmd->data.draw.count; const SDL_FPoint *verts = (SDL_FPoint *)(vertices + cmd->data.draw.first); GXColor c = { @@ -365,6 +363,7 @@ int OGC_RenderPrimitive(SDL_Renderer *renderer, u8 primitive, cmd->data.draw.a }; + data->ops_after_present++; OGC_SetBlendMode(renderer, cmd->data.draw.blend); /* TODO: optimize state changes. */ @@ -439,7 +438,9 @@ static int OGC_RenderPresent(SDL_Renderer *renderer) { GX_DrawDone(); - OGC_VideoFlip(renderer->window); + OGC_video_flip(SDL_GetVideoDevice()); + + data->ops_after_present = 0; return 0; } diff --git a/src/video/ogc/SDL_ogcframebuffer.c b/src/video/ogc/SDL_ogcframebuffer.c index f7e8a4835e0df..13bbf6abbdecf 100644 --- a/src/video/ogc/SDL_ogcframebuffer.c +++ b/src/video/ogc/SDL_ogcframebuffer.c @@ -104,7 +104,7 @@ int SDL_OGC_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *r draw_screen_rect(window); GX_DrawDone(); - OGC_VideoFlip(window); + OGC_video_flip(_this); return 0; } diff --git a/src/video/ogc/SDL_ogcgxcommon.h b/src/video/ogc/SDL_ogcgxcommon.h index c47601d8db80e..87dd5fe764021 100644 --- a/src/video/ogc/SDL_ogcgxcommon.h +++ b/src/video/ogc/SDL_ogcgxcommon.h @@ -25,6 +25,8 @@ #include +#define GX_COLOR_AS_U32(c) *((u32*)&c) + void OGC_draw_init(int w, int h, int h_aspect, int v_aspect); void OGC_prepare_texels(void *pixels, int w, int h, int pitch, Uint32 format, void *texels, u8 *gx_format); diff --git a/src/video/ogc/SDL_ogcvideo.c b/src/video/ogc/SDL_ogcvideo.c index adf37dd52215b..986d2fa5aa8ec 100644 --- a/src/video/ogc/SDL_ogcvideo.c +++ b/src/video/ogc/SDL_ogcvideo.c @@ -172,12 +172,16 @@ void OGC_VideoQuit(_THIS) free(MEM_K1_TO_K0(videodata->xfb[1])); } -void OGC_VideoFlip(SDL_Window *window) +void *OGC_video_get_xfb(_THIS) { - SDL_VideoDevice *_this = SDL_GetVideoDevice(); - SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; + SDL_VideoData *videodata = _this->driverdata; + return videodata->xfb[0]; +} - GX_CopyDisp(videodata->xfb[0], GX_TRUE); +void OGC_video_flip(_THIS) +{ + void *xfb = OGC_video_get_xfb(_this); + GX_CopyDisp(xfb, GX_TRUE); GX_DrawDone(); GX_Flush(); diff --git a/src/video/ogc/SDL_ogcvideo.h b/src/video/ogc/SDL_ogcvideo.h index e89f943365ba9..bee2c464573e7 100644 --- a/src/video/ogc/SDL_ogcvideo.h +++ b/src/video/ogc/SDL_ogcvideo.h @@ -41,7 +41,8 @@ typedef struct SDL_WindowData SDL_PixelFormatEnum surface_format; } SDL_WindowData; -void OGC_VideoFlip(SDL_Window *window); +void *OGC_video_get_xfb(SDL_VideoDevice *device); +void OGC_video_flip(SDL_VideoDevice *device); #endif /* SDL_ogcvideo_h_ */ From f1501ee4bb4415ac8f4e5ac95107a9c848d34124 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Wed, 10 Jan 2024 18:15:12 +0300 Subject: [PATCH 12/21] ogc: implement render to texture --- src/render/ogc/SDL_render_ogc.c | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 011ac8dcc8c38..e00cc0973634c 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -33,6 +33,9 @@ #include #include +#define MAX_EFB_WIDTH 640 +#define MAX_EFB_HEIGHT 528 + typedef struct { SDL_BlendMode current_blend_mode; @@ -84,6 +87,16 @@ static inline void OGC_SetBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend_ data->current_blend_mode = blend_mode; } +static void save_efb_to_texture(SDL_Renderer *renderer) +{ + SDL_Texture *texture = renderer->target; + OGC_TextureData *ogc_tex = texture->driverdata; + + GX_SetTexCopySrc(0, 0, texture->w, texture->h); + GX_SetTexCopyDst(texture->w, texture->h, ogc_tex->format, GX_FALSE); + GX_CopyTex(ogc_tex->texels, GX_TRUE); +} + static int OGC_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) { u32 texture_size; @@ -140,6 +153,26 @@ static void OGC_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture static int OGC_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) { + OGC_RenderData *data = renderer->driverdata; + + if (texture) { + if (texture->w > MAX_EFB_WIDTH || texture->h > MAX_EFB_HEIGHT) { + return SDL_SetError("Render target bigger than EFB"); + } + + if (data->ops_after_present > 0) { + /* We should save the current EFB contents into the window data. + * However, it's unclear whether this is a possible scenario, since + * all actual drawing happens in RunCommandQueue() and this method + * will not be called in between of the drawing operations; but + * just to be on the safe side, log a warning. We can come back to + * this later and implement the EFB saving if we see that this + * happens in real life. + */ + SDL_LogWarn(SDL_LOG_CATEGORY_RENDER, + "Render target set after drawing!"); + } + } return 0; } @@ -425,6 +458,11 @@ static int OGC_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, v } cmd = cmd->next; } + + if (renderer->target) { + save_efb_to_texture(renderer); + } + return 0; } From 0b61f34882e2c3115394ecd9448e5b4cec752efd Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Thu, 11 Jan 2024 20:50:45 +0300 Subject: [PATCH 13/21] ogc: manually draw the last pixel of a line When drawing line strips, the last pixel is left out. --- src/render/ogc/SDL_render_ogc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index e00cc0973634c..916bf67e8480d 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -418,6 +418,13 @@ int OGC_RenderPrimitive(SDL_Renderer *renderer, u8 primitive, } GX_End(); + /* The last point is not drawn */ + if (primitive == GX_LINESTRIP) { + GX_Begin(GX_POINTS, GX_VTXFMT0, 1); + GX_Position2f32(verts[count - 1].x, verts[count - 1].y); + GX_End(); + } + return 0; } From 0dbdd630b53c59ed94a3d7a80d3e2fcf221bd010 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Thu, 11 Jan 2024 23:47:26 +0300 Subject: [PATCH 14/21] ogc: implement SetViewport --- src/render/ogc/SDL_render_ogc.c | 8 +++++++- src/video/ogc/SDL_ogcgxcommon.c | 30 +++++++++++++++++++----------- src/video/ogc/SDL_ogcgxcommon.h | 1 + src/video/ogc/SDL_ogcvideo.c | 3 --- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 916bf67e8480d..1e684aa7a8728 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -286,7 +286,13 @@ static int OGC_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL static int OGC_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { - // TODO + OGC_RenderData *data = (OGC_RenderData *)renderer->driverdata; + const SDL_Rect *viewport = &cmd->data.viewport.rect; + + float v_aspect = viewport->h / 2.0; + float h_aspect = viewport->w / 2.0; + OGC_set_viewport(viewport->x, viewport->y, viewport->w, viewport->h, + h_aspect, v_aspect); return 0; } diff --git a/src/video/ogc/SDL_ogcgxcommon.c b/src/video/ogc/SDL_ogcgxcommon.c index 787e6ccc10a1a..7d4f6570fb23d 100644 --- a/src/video/ogc/SDL_ogcgxcommon.c +++ b/src/video/ogc/SDL_ogcgxcommon.c @@ -52,11 +52,28 @@ static const f32 tex_pos[] __attribute__((aligned(32))) = { 1.0, }; -void OGC_draw_init(int w, int h, int h_aspect, int v_aspect) +void OGC_set_viewport(int x, int y, int w, int h, float h_aspect, float v_aspect) { Mtx m, mv, view; Mtx44 proj; + GX_SetViewport(x, y, w, h, 0, 1); + GX_SetScissor(x, y, w, h); + + memset(&view, 0, sizeof(Mtx)); + guLookAt(view, &cam.pos, &cam.up, &cam.view); + guMtxIdentity(m); + guMtxTransApply(m, m, 0.5 + (-w / 2.0), 0.5 + (-h / 2.0), 1000); + guMtxConcat(view, m, mv); + GX_LoadPosMtxImm(mv, GX_PNMTX0); + + // matrix, t, b, l, r, n, f + guOrtho(proj, v_aspect, -v_aspect, -h_aspect, h_aspect, 100, 1000); + GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC); +} + +void OGC_draw_init(int w, int h, int h_aspect, int v_aspect) +{ SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "OGC_draw_init called with %d, %d", w, h); GX_ClearVtxDesc(); @@ -77,16 +94,7 @@ void OGC_draw_init(int w, int h, int h_aspect, int v_aspect) GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE); GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); - memset(&view, 0, sizeof(Mtx)); - guLookAt(view, &cam.pos, &cam.up, &cam.view); - guMtxIdentity(m); - guMtxTransApply(m, m, -w / 2, -h / 2, 1000); - guMtxConcat(view, m, mv); - GX_LoadPosMtxImm(mv, GX_PNMTX0); - - // matrix, t, b, l, r, n, f - guOrtho(proj, v_aspect, -v_aspect, -h_aspect, h_aspect, 100, 1000); - GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC); + OGC_set_viewport(0, 0, w, h, h_aspect, v_aspect); GX_InvVtxCache(); // update vertex cache } diff --git a/src/video/ogc/SDL_ogcgxcommon.h b/src/video/ogc/SDL_ogcgxcommon.h index 87dd5fe764021..df1eb585b9e79 100644 --- a/src/video/ogc/SDL_ogcgxcommon.h +++ b/src/video/ogc/SDL_ogcgxcommon.h @@ -28,6 +28,7 @@ #define GX_COLOR_AS_U32(c) *((u32*)&c) void OGC_draw_init(int w, int h, int h_aspect, int v_aspect); +void OGC_set_viewport(int x, int y, int w, int h, float h_aspect, float v_aspect); void OGC_prepare_texels(void *pixels, int w, int h, int pitch, Uint32 format, void *texels, u8 *gx_format); void OGC_load_texture(void *texels, int w, int h, u8 gx_format); diff --git a/src/video/ogc/SDL_ogcvideo.c b/src/video/ogc/SDL_ogcvideo.c index 986d2fa5aa8ec..386a48e75c880 100644 --- a/src/video/ogc/SDL_ogcvideo.c +++ b/src/video/ogc/SDL_ogcvideo.c @@ -119,9 +119,6 @@ int OGC_VideoInit(_THIS) memset(videodata->gp_fifo, 0, DEFAULT_FIFO_SIZE); GX_Init(videodata->gp_fifo, DEFAULT_FIFO_SIZE); - GX_SetViewport(0, 0, vmode->fbWidth, vmode->efbHeight, 0, 1); - GX_SetScissor(0, 0, vmode->fbWidth, vmode->efbHeight); - /* Setup the EFB -> XFB copy operation */ GX_SetDispCopySrc(0, 0, vmode->fbWidth, vmode->efbHeight); GX_SetDispCopyDst(vmode->fbWidth, vmode->xfbHeight); From 74c0e8d4ece37f8644b46ca8bcda335a4e38498b Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Fri, 12 Jan 2024 19:06:57 +0300 Subject: [PATCH 15/21] ogc: prefer rendering lines as a primitive --- src/render/ogc/SDL_render_ogc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 1e684aa7a8728..2a63b6fbe4dd4 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -565,6 +565,11 @@ static SDL_Renderer *OGC_CreateRenderer(SDL_Window *window, Uint32 flags) renderer->driverdata = data; renderer->window = window; + if (!SDL_GetHint(SDL_HINT_RENDER_LINE_METHOD)) { + /* SDL sets the default one to point drawing, but we prefer lines */ + SDL_SetHint(SDL_HINT_RENDER_LINE_METHOD, "2"); + } + return renderer; } From 26b038dc7739eee16a09e7e77863721c9419df0c Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Fri, 12 Jan 2024 19:17:56 +0300 Subject: [PATCH 16/21] ogc: implement setting clip rect --- src/render/ogc/SDL_render_ogc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 2a63b6fbe4dd4..dea0155883b28 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -298,7 +298,17 @@ static int OGC_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd) static int OGC_RenderSetClipRect(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { - // TODO + const SDL_Rect *rect = &cmd->data.cliprect.rect; + + if (cmd->data.cliprect.enabled) { + GX_SetScissor(renderer->viewport.x + rect->x, + renderer->viewport.y + rect->y, + rect->w, rect->h); + GX_SetClipMode(GX_CLIP_ENABLE); + } else { + GX_SetClipMode(GX_CLIP_DISABLE); + } + return 0; } From 88559141b7edc361920d281bcf09a80e842b52b7 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Fri, 12 Jan 2024 21:10:37 +0300 Subject: [PATCH 17/21] ogc: implement toggling VSync --- src/render/ogc/SDL_render_ogc.c | 8 +++++++- src/video/ogc/SDL_ogcframebuffer.c | 2 +- src/video/ogc/SDL_ogcvideo.c | 6 ++++-- src/video/ogc/SDL_ogcvideo.h | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index dea0155883b28..f711a087b26a6 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -41,6 +41,7 @@ typedef struct SDL_BlendMode current_blend_mode; GXColor clear_color; int ops_after_present; + bool vsync; } OGC_RenderData; typedef struct @@ -497,9 +498,11 @@ static int OGC_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, static int OGC_RenderPresent(SDL_Renderer *renderer) { + OGC_RenderData *data = renderer->driverdata; + GX_DrawDone(); - OGC_video_flip(SDL_GetVideoDevice()); + OGC_video_flip(SDL_GetVideoDevice(), data->vsync); data->ops_after_present = 0; return 0; @@ -529,6 +532,8 @@ static void OGC_DestroyRenderer(SDL_Renderer *renderer) static int OGC_SetVSync(SDL_Renderer *renderer, const int vsync) { + OGC_RenderData *data = (OGC_RenderData *)renderer->driverdata; + data->vsync = vsync ? 1 : 0; return 0; } @@ -551,6 +556,7 @@ static SDL_Renderer *OGC_CreateRenderer(SDL_Window *window, Uint32 flags) } data->current_blend_mode = SDL_BLENDMODE_NONE; + data->vsync = true; renderer->WindowEvent = OGC_WindowEvent; renderer->CreateTexture = OGC_CreateTexture; diff --git a/src/video/ogc/SDL_ogcframebuffer.c b/src/video/ogc/SDL_ogcframebuffer.c index 13bbf6abbdecf..a45e2d70439fc 100644 --- a/src/video/ogc/SDL_ogcframebuffer.c +++ b/src/video/ogc/SDL_ogcframebuffer.c @@ -104,7 +104,7 @@ int SDL_OGC_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *r draw_screen_rect(window); GX_DrawDone(); - OGC_video_flip(_this); + OGC_video_flip(_this, true); return 0; } diff --git a/src/video/ogc/SDL_ogcvideo.c b/src/video/ogc/SDL_ogcvideo.c index 386a48e75c880..28e0cccb1b5ce 100644 --- a/src/video/ogc/SDL_ogcvideo.c +++ b/src/video/ogc/SDL_ogcvideo.c @@ -175,7 +175,7 @@ void *OGC_video_get_xfb(_THIS) return videodata->xfb[0]; } -void OGC_video_flip(_THIS) +void OGC_video_flip(_THIS, bool vsync) { void *xfb = OGC_video_get_xfb(_this); GX_CopyDisp(xfb, GX_TRUE); @@ -183,7 +183,9 @@ void OGC_video_flip(_THIS) GX_Flush(); VIDEO_Flush(); - VIDEO_WaitVSync(); + if (vsync) { + VIDEO_WaitVSync(); + } } #endif /* SDL_VIDEO_DRIVER_OGC */ diff --git a/src/video/ogc/SDL_ogcvideo.h b/src/video/ogc/SDL_ogcvideo.h index bee2c464573e7..193d94e82f377 100644 --- a/src/video/ogc/SDL_ogcvideo.h +++ b/src/video/ogc/SDL_ogcvideo.h @@ -42,7 +42,7 @@ typedef struct SDL_WindowData } SDL_WindowData; void *OGC_video_get_xfb(SDL_VideoDevice *device); -void OGC_video_flip(SDL_VideoDevice *device); +void OGC_video_flip(SDL_VideoDevice *device, bool vsync); #endif /* SDL_ogcvideo_h_ */ From db46446f690505d8566283f363d2982678f6e0fd Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Fri, 12 Jan 2024 21:25:03 +0300 Subject: [PATCH 18/21] ogc: do not implement SetDrawColor --- src/render/ogc/SDL_render_ogc.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index f711a087b26a6..1393918258f37 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -313,12 +313,6 @@ static int OGC_RenderSetClipRect(SDL_Renderer *renderer, SDL_RenderCommand *cmd) return 0; } -static int OGC_RenderSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd) -{ - // TODO - return 0; -} - static int OGC_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { OGC_RenderData *data = renderer->driverdata; @@ -456,7 +450,8 @@ static int OGC_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, v OGC_RenderSetClipRect(renderer, cmd); break; case SDL_RENDERCMD_SETDRAWCOLOR: - OGC_RenderSetDrawColor(renderer, cmd); + /* This is a no-op, since every command carries the color, and + * setting it on the FIFO is not expensive. */ break; case SDL_RENDERCMD_CLEAR: OGC_RenderClear(renderer, cmd); From 116b0cc83a8517ec37ec8061c4248c1decfc4cf1 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Sat, 13 Jan 2024 22:59:10 +0300 Subject: [PATCH 19/21] ogc: implement locking textures, add more texture formats --- src/render/ogc/SDL_render_ogc.c | 43 ++++++-- src/video/ogc/SDL_ogcframebuffer.c | 14 ++- src/video/ogc/SDL_ogcgxcommon.c | 12 --- src/video/ogc/SDL_ogcgxcommon.h | 2 - src/video/ogc/SDL_ogcpixels.c | 164 +++++++++++++++++------------ src/video/ogc/SDL_ogcpixels.h | 5 +- 6 files changed, 148 insertions(+), 92 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 1393918258f37..77fea90337b94 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -30,6 +30,7 @@ #include "../../video/ogc/SDL_ogcvideo.h" #include +#include #include #include @@ -47,7 +48,10 @@ typedef struct typedef struct { void *texels; + void *pixels; + SDL_Rect pixels_rect; u16 pitch; + u16 pixels_pitch; u8 format; u8 needed_stages; // Normally 1, set to 2 for palettized formats } OGC_TextureData; @@ -125,24 +129,45 @@ static int OGC_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) static int OGC_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) { - // TODO + OGC_TextureData *ogc_tex = texture->driverdata; + + ogc_tex->pixels = SDL_malloc(rect->w * rect->h * SDL_BYTESPERPIXEL(texture->format)); + ogc_tex->pixels_pitch = rect->w * SDL_BYTESPERPIXEL(texture->format); + ogc_tex->pixels_rect = *rect; + *pixels = ogc_tex->pixels; + *pitch = ogc_tex->pixels_pitch; return 0; } static void OGC_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) { - // TODO + OGC_TextureData *ogc_tex = texture->driverdata; + u32 texture_size; + + OGC_pixels_to_texture(ogc_tex->pixels, texture->format, + &ogc_tex->pixels_rect, ogc_tex->pixels_pitch, + ogc_tex->texels, texture->w); + /* It would be more effective if we updated only the changed range here, + * but the complexity is probably not worth the effort. */ + texture_size = GX_GetTexBufferSize(texture->w, texture->h, ogc_tex->format, + GX_FALSE, 0); + DCStoreRange(ogc_tex->texels, texture_size); + GX_InvalidateTexAll(); + + + if (ogc_tex->pixels) { + SDL_free(ogc_tex->pixels); + ogc_tex->pixels = NULL; + } } static int OGC_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { OGC_TextureData *ogc_tex = texture->driverdata; - u8 format; - // TODO: take rect into account - OGC_pixels_to_texture((void*)pixels, texture->format, texture->w, texture->h, - pitch, ogc_tex->texels, &format); + OGC_pixels_to_texture((void*)pixels, texture->format, rect, + pitch, ogc_tex->texels, texture->w); return 0; } @@ -287,7 +312,6 @@ static int OGC_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL static int OGC_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { - OGC_RenderData *data = (OGC_RenderData *)renderer->driverdata; const SDL_Rect *viewport = &cmd->data.viewport.rect; float v_aspect = viewport->h / 2.0; @@ -589,10 +613,13 @@ SDL_RenderDriver OGC_RenderDriver = { .info = { .name = "ogc", .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, - .num_texture_formats = 2, + .num_texture_formats = 5, .texture_formats = { [0] = SDL_PIXELFORMAT_RGB565, [1] = SDL_PIXELFORMAT_RGBA8888, + [2] = SDL_PIXELFORMAT_ARGB8888, + [3] = SDL_PIXELFORMAT_RGB24, + [4] = SDL_PIXELFORMAT_XRGB8888, // TODO: add more }, .max_texture_width = 1024, diff --git a/src/video/ogc/SDL_ogcframebuffer.c b/src/video/ogc/SDL_ogcframebuffer.c index a45e2d70439fc..74d952d99be8c 100644 --- a/src/video/ogc/SDL_ogcframebuffer.c +++ b/src/video/ogc/SDL_ogcframebuffer.c @@ -25,6 +25,7 @@ #include "../SDL_sysvideo.h" #include "SDL_ogcframebuffer_c.h" #include "SDL_ogcgxcommon.h" +#include "SDL_ogcpixels.h" #include "SDL_ogcvideo.h" #include @@ -95,11 +96,18 @@ int SDL_OGC_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, v int SDL_OGC_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects) { SDL_WindowData *windowdata = (SDL_WindowData *)window->driverdata; + u32 texture_size; u8 gx_format; - OGC_prepare_texels(windowdata->pixels, window->w, window->h, window->surface->pitch, - windowdata->surface_format, - windowdata->texels, &gx_format); + gx_format = OGC_texture_format_from_SDL(windowdata->surface_format); + for (int i = 0; i < numrects; i++) { + OGC_pixels_to_texture(windowdata->pixels, windowdata->surface_format, &rects[i], + window->surface->pitch, windowdata->texels, window->w); + } + texture_size = GX_GetTexBufferSize(window->w, window->h, gx_format, + GX_FALSE, 0); + DCStoreRange(windowdata->texels, texture_size); + GX_InvalidateTexAll(); OGC_load_texture(windowdata->texels, window->w, window->h, gx_format); draw_screen_rect(window); GX_DrawDone(); diff --git a/src/video/ogc/SDL_ogcgxcommon.c b/src/video/ogc/SDL_ogcgxcommon.c index 7d4f6570fb23d..b58d635bacfef 100644 --- a/src/video/ogc/SDL_ogcgxcommon.c +++ b/src/video/ogc/SDL_ogcgxcommon.c @@ -99,18 +99,6 @@ void OGC_draw_init(int w, int h, int h_aspect, int v_aspect) GX_InvVtxCache(); // update vertex cache } -void OGC_prepare_texels(void *pixels, int w, int h, int pitch, Uint32 format, - void *texels, u8 *gx_format) -{ - u32 texture_size; - - OGC_pixels_to_texture(pixels, format, w, h, pitch, - texels, gx_format); - texture_size = GX_GetTexBufferSize(w, h, *gx_format, GX_FALSE, 0); - DCStoreRange(texels, texture_size); - GX_InvalidateTexAll(); -} - void OGC_load_texture(void *texels, int w, int h, u8 format) { GXTexObj texobj_a, texobj_b; diff --git a/src/video/ogc/SDL_ogcgxcommon.h b/src/video/ogc/SDL_ogcgxcommon.h index df1eb585b9e79..8286a08676a41 100644 --- a/src/video/ogc/SDL_ogcgxcommon.h +++ b/src/video/ogc/SDL_ogcgxcommon.h @@ -29,8 +29,6 @@ void OGC_draw_init(int w, int h, int h_aspect, int v_aspect); void OGC_set_viewport(int x, int y, int w, int h, float h_aspect, float v_aspect); -void OGC_prepare_texels(void *pixels, int w, int h, int pitch, Uint32 format, - void *texels, u8 *gx_format); void OGC_load_texture(void *texels, int w, int h, u8 gx_format); #endif /* SDL_ogcgxcommon_h_ */ diff --git a/src/video/ogc/SDL_ogcpixels.c b/src/video/ogc/SDL_ogcpixels.c index c47138287aa54..2d87b40d955be 100644 --- a/src/video/ogc/SDL_ogcpixels.c +++ b/src/video/ogc/SDL_ogcpixels.c @@ -24,6 +24,38 @@ #include +#define PIXELS_TO_TEXTURE_32(format_func) \ + static void pixels_to_texture_ ## format_func( \ + void *pixels, const SDL_Rect *rect, int16_t pitch, void *texture, int16_t tex_width) \ + { \ + int16_t tex_pitch = (tex_width + 3) / 4 * 4; \ + for (int row = 0; row < rect->h; row++) { \ + int y = rect->y + row; \ + u32 *src = (u32 *)((u8 *)pixels + pitch * row); \ + for (int col = 0; col < rect->w; col++) { \ + int x = rect->x + col; \ + u32 offset = (((y >> 2) << 4) * tex_pitch) + \ + ((x >> 2) << 6) + (((y % 4 << 2) + x % 4) << 1); \ + set_pixel_to_texture_ ## format_func(texture, offset, *src++); \ + } \ + } \ + } + +#define PIXELS_FROM_TEXTURE_32(format_func) \ + static void pixels_from_texture_ ## format_func( \ + void *pixels, int16_t w, int16_t h, int16_t pitch, void *texture) \ + { \ + int tex_width = (w + 3) / 4 * 4; \ + for (int y = 0; y < h; y++) { \ + u32 *dst = (u32 *)((u8 *)pixels + pitch * y); \ + for (int x = 0; x < w; x++) { \ + u32 offset = (((y >> 2) << 4) * tex_width) + \ + ((x >> 2) << 6) + (((y % 4 << 2) + x % 4) << 1); \ + *dst++ = get_pixel_from_texture_ ## format_func(texture, offset); \ + } \ + } \ + } + static u8 texture_format_from_SDL(const SDL_PixelFormatEnum format) { switch (format) { @@ -33,6 +65,8 @@ static u8 texture_format_from_SDL(const SDL_PixelFormatEnum format) return GX_TF_RGB565; case SDL_PIXELFORMAT_RGB24: case SDL_PIXELFORMAT_RGBA8888: + case SDL_PIXELFORMAT_ARGB8888: + case SDL_PIXELFORMAT_XRGB8888: return GX_TF_RGBA8; default: SDL_LogError(SDL_LOG_CATEGORY_VIDEO, @@ -42,17 +76,21 @@ static u8 texture_format_from_SDL(const SDL_PixelFormatEnum format) return 0xff; // invalid } -static inline void set_pixel_to_texture_32(int x, int y, u32 color, void *texture, int tex_width) +static inline void set_pixel_to_texture_ARGB(void *texture, u32 offset, u32 color) { - u8 *tex = texture; - u32 offset; + *(u16*)(texture + offset) = color >> 16; + *(u16*)(texture + offset + 32) = color; +} - offset = (((y >> 2) << 4) * tex_width) + ((x >> 2) << 6) + (((y % 4 << 2) + x % 4) << 1); +static inline u32 get_pixel_from_texture_ARGB(void *texture, u32 offset) +{ + return *(u16*)(texture + offset) << 16 | + *(u16*)(texture + offset + 32); +} - *(tex + offset) = color; - *(tex + offset + 1) = color >> 24; - *(tex + offset + 32) = color >> 16; - *(tex + offset + 33) = color >> 8; +static inline void set_pixel_to_texture_RGBA(void *texture, u32 offset, u32 color) +{ + set_pixel_to_texture_ARGB(texture, offset, (color << 24) | (color >> 8)); } static inline u32 get_pixel_from_texture_32(int x, int y, void *texture, int tex_width) @@ -68,19 +106,7 @@ static inline u32 get_pixel_from_texture_32(int x, int y, void *texture, int tex *(tex + offset + 33) << 8; } -static void pixels_RGBA_to_texture(void *pixels, int16_t w, int16_t h, - int16_t pitch, void *texture) -{ - u32 *src = pixels; - - int tex_width = (w + 3) / 4 * 4; - for (int y = 0; y < h; y++) { - src = (u32 *)((u8 *)pixels + pitch * y); - for (int x = 0; x < w; x++) { - set_pixel_to_texture_32(x, y, *src++, texture, tex_width); - } - } -} +PIXELS_TO_TEXTURE_32(RGBA) static void pixels_RGBA_from_texture(void *pixels, int16_t w, int16_t h, int16_t pitch, void *texture) @@ -96,20 +122,16 @@ static void pixels_RGBA_from_texture(void *pixels, int16_t w, int16_t h, } } -static void pixels_XRGB_to_texture(void *pixels, int16_t w, int16_t h, - int16_t pitch, void *texture) -{ - u32 *src = pixels; +PIXELS_TO_TEXTURE_32(ARGB) +PIXELS_FROM_TEXTURE_32(ARGB) - int tex_width = (w + 3) / 4 * 4; - for (int y = 0; y < h; y++) { - src = (u32 *)((u8 *)pixels + pitch * y); - for (int x = 0; x < w; x++) { - set_pixel_to_texture_32(x, y, (*src++) << 8 | 0xff, texture, tex_width); - } - } +static inline void set_pixel_to_texture_XRGB(void *texture, u32 offset, u32 color) +{ + set_pixel_to_texture_ARGB(texture, offset, 0xff000000 | color); } +PIXELS_TO_TEXTURE_32(XRGB) + static void pixels_XRGB_from_texture(void *pixels, int16_t w, int16_t h, int16_t pitch, void *texture) { @@ -124,20 +146,23 @@ static void pixels_XRGB_from_texture(void *pixels, int16_t w, int16_t h, } } -static void pixels_RGB_to_texture(void *pixels, int16_t w, int16_t h, - int16_t pitch, void *texture) +static void pixels_RGB_to_texture(void *pixels, const SDL_Rect *rect, + int16_t pitch, void *texture, int16_t tex_width) { u8 *src = pixels; - int tex_width = (w + 3) / 4 * 4; - for (int y = 0; y < h; y++) { - src = (u8 *)pixels + pitch * y; - for (int x = 0; x < w; x++) { + int tex_pitch = (tex_width + 3) / 4 * 4; + for (int row = 0; row < rect->h; row++) { + int y = rect->y + row; + src = (u8 *)pixels + pitch * row; + for (int col = 0; col < rect->w; col++) { + int x = rect->x + col; + u32 offset = (((y >> 2) << 4) * tex_pitch) + + ((x >> 2) << 6) + (((y % 4 << 2) + x % 4) << 1); u8 r = *src++; u8 g = *src++; u8 b = *src++; - set_pixel_to_texture_32(x, y, r << 24 | g << 16 | b << 8 | 0xff, - texture, tex_width); + set_pixel_to_texture_ARGB(texture, offset, 0xff000000 | r << 16 | g << 8 | b); } } } @@ -159,28 +184,33 @@ static void pixels_RGB_from_texture(void *pixels, int16_t w, int16_t h, } } -static void pixels_16_to_texture(void *pixels, int16_t pitch, int16_t h, - void *texture) +static void pixels_16_to_texture(void *pixels, const SDL_Rect *rect, int16_t pitch, + void *texture, int16_t tex_width) { - long long int *dst = texture; - long long int *src1 = pixels; - long long int *src2 = (long long int *)((char *)pixels + (pitch * 1)); - long long int *src3 = (long long int *)((char *)pixels + (pitch * 2)); - long long int *src4 = (long long int *)((char *)pixels + (pitch * 3)); - int rowpitch = (pitch >> 3) * 3; - - for (int y = 0; y < h; y += 4) { - for (int x = 0; x < pitch; x += 8) { + /* We only support coordinates multiple of 4. While we don't add support + * for arbitrary coordinates (TODO), let's restrict the paint area to the + * full 4x4 cells covered by the rect. In the future we can add code to + * fill up the remaining borders. */ + int x0 = (rect->x + 3) & ~0x3; + int x1 = (rect->x + rect->w) & ~0x3; + int y0 = (rect->y + 3) & ~0x3; + int y1 = (rect->y + rect->h) & ~0x3; + int skipped_bytes_left = (x0 - rect->x) * 2; + int tex_pitch = tex_width * 2; + + + for (int row = 0; row < y1 - y0; row += 4) { + u64 *src1 = pixels + (skipped_bytes_left + row * pitch); + u64 *src2 = (void*)src1 + (pitch * 1); + u64 *src3 = (void*)src1 + (pitch * 2); + u64 *src4 = (void*)src1 + (pitch * 3); + u64 *dst = texture + (x0 * 8 + (y0 + row) * tex_pitch); + for (int col = x0; col < x1; col += 4) { *dst++ = *src1++; *dst++ = *src2++; *dst++ = *src3++; *dst++ = *src4++; } - - src1 = src4; - src2 += rowpitch; - src3 += rowpitch; - src4 += rowpitch; } } @@ -258,26 +288,27 @@ static void pixels_8_from_texture(void *pixels, int16_t w, int16_t h, } void OGC_pixels_to_texture(void *pixels, const SDL_PixelFormatEnum format, - int16_t w, int16_t h, int16_t pitch, - void *texture, u8 *gx_format) + const SDL_Rect *rect, int16_t pitch, + void *texture, int16_t tex_width) { - *gx_format = texture_format_from_SDL(format); - switch (format) { case SDL_PIXELFORMAT_INDEX8: - pixels_8_to_texture(pixels, w, h, pitch, texture); + pixels_8_to_texture(pixels, rect->w, rect->h, pitch, texture); break; case SDL_PIXELFORMAT_RGB565: - pixels_16_to_texture(pixels, pitch, h, texture); + pixels_16_to_texture(pixels, rect, pitch, texture, tex_width); break; case SDL_PIXELFORMAT_RGB24: - pixels_RGB_to_texture(pixels, w, h, pitch, texture); + pixels_RGB_to_texture(pixels, rect, pitch, texture, tex_width); break; case SDL_PIXELFORMAT_RGBA8888: - pixels_RGBA_to_texture(pixels, w, h, pitch, texture); + pixels_to_texture_RGBA(pixels, rect, pitch, texture, tex_width); + break; + case SDL_PIXELFORMAT_ARGB8888: + pixels_to_texture_ARGB(pixels, rect, pitch, texture, tex_width); break; case SDL_PIXELFORMAT_XRGB8888: - pixels_XRGB_to_texture(pixels, w, h, pitch, texture); + pixels_to_texture_XRGB(pixels, rect, pitch, texture, tex_width); break; default: SDL_LogError(SDL_LOG_CATEGORY_VIDEO, @@ -303,6 +334,9 @@ void OGC_pixels_from_texture(void *pixels, const SDL_PixelFormatEnum format, case SDL_PIXELFORMAT_RGBA8888: pixels_RGBA_from_texture(pixels, w, h, pitch, texture); break; + case SDL_PIXELFORMAT_ARGB8888: + pixels_from_texture_ARGB(pixels, w, h, pitch, texture); + break; case SDL_PIXELFORMAT_XRGB8888: pixels_XRGB_from_texture(pixels, w, h, pitch, texture); break; diff --git a/src/video/ogc/SDL_ogcpixels.h b/src/video/ogc/SDL_ogcpixels.h index b6ac8a0583c58..c92d032aa48bc 100644 --- a/src/video/ogc/SDL_ogcpixels.h +++ b/src/video/ogc/SDL_ogcpixels.h @@ -24,12 +24,13 @@ #define SDL_ogcpixels_h_ #include "SDL_pixels.h" +#include "SDL_rect.h" #include void OGC_pixels_to_texture(void *pixels, SDL_PixelFormatEnum format, - int16_t w, int16_t h, int16_t pitch, - void *texels, u8 *gx_format); + const SDL_Rect *rect, int16_t pitch, + void *texels, int16_t tex_width); void OGC_pixels_from_texture(void *pixels, SDL_PixelFormatEnum format, int16_t w, int16_t h, int16_t pitch, void *texels); From 17c4c413625d5d9a759c0317690b6b1ce5641df2 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Sun, 14 Jan 2024 09:58:43 +0300 Subject: [PATCH 20/21] ogc: implement texture scale mode --- src/render/ogc/SDL_render_ogc.c | 9 ++++++--- src/video/ogc/SDL_ogcframebuffer.c | 3 ++- src/video/ogc/SDL_ogcgxcommon.c | 17 +++++++++++++++-- src/video/ogc/SDL_ogcgxcommon.h | 5 ++++- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/render/ogc/SDL_render_ogc.c b/src/render/ogc/SDL_render_ogc.c index 77fea90337b94..96c8de160d28c 100644 --- a/src/render/ogc/SDL_render_ogc.c +++ b/src/render/ogc/SDL_render_ogc.c @@ -172,9 +172,12 @@ static int OGC_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return 0; } -static void OGC_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode) +static void OGC_SetTextureScaleMode(SDL_Renderer *renderer, + SDL_Texture *texture, + SDL_ScaleMode scaleMode) { - // TODO + /* Nothing to do here: the scale mode is applied to the texture when + * loading it in OGC_load_texture(). */ } static int OGC_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) @@ -386,7 +389,7 @@ static int OGC_RenderGeometry(SDL_Renderer *renderer, void *vertices, size_per_element += sizeof(SDL_FPoint); OGC_load_texture(ogc_tex->texels, texture->w, texture->h, - ogc_tex->format); + ogc_tex->format, texture->scaleMode); stage = GX_TEVSTAGE0 + ogc_tex->needed_stages - 1; GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); diff --git a/src/video/ogc/SDL_ogcframebuffer.c b/src/video/ogc/SDL_ogcframebuffer.c index 74d952d99be8c..21fb7ee0cb449 100644 --- a/src/video/ogc/SDL_ogcframebuffer.c +++ b/src/video/ogc/SDL_ogcframebuffer.c @@ -108,7 +108,8 @@ int SDL_OGC_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *r GX_FALSE, 0); DCStoreRange(windowdata->texels, texture_size); GX_InvalidateTexAll(); - OGC_load_texture(windowdata->texels, window->w, window->h, gx_format); + OGC_load_texture(windowdata->texels, window->w, window->h, gx_format, + SDL_ScaleModeNearest); draw_screen_rect(window); GX_DrawDone(); diff --git a/src/video/ogc/SDL_ogcgxcommon.c b/src/video/ogc/SDL_ogcgxcommon.c index b58d635bacfef..eeb1146ad9578 100644 --- a/src/video/ogc/SDL_ogcgxcommon.c +++ b/src/video/ogc/SDL_ogcgxcommon.c @@ -99,7 +99,8 @@ void OGC_draw_init(int w, int h, int h_aspect, int v_aspect) GX_InvVtxCache(); // update vertex cache } -void OGC_load_texture(void *texels, int w, int h, u8 format) +void OGC_load_texture(void *texels, int w, int h, u8 format, + SDL_ScaleMode scale_mode) { GXTexObj texobj_a, texobj_b; @@ -127,7 +128,19 @@ void OGC_load_texture(void *texels, int w, int h, u8 format) GX_InitTexObj(&texobj_a, texels, w, h, format, GX_CLAMP, GX_CLAMP, GX_FALSE); } - GX_InitTexObjLOD(&texobj_a, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + switch (scale_mode) { + case SDL_ScaleModeLinear: + GX_InitTexObjLOD(&texobj_a, GX_LINEAR, GX_LINEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + break; + case SDL_ScaleModeBest: + GX_InitTexObjLOD(&texobj_a, GX_LIN_MIP_LIN, GX_LINEAR, + 0.0f, 10.0f, 0.0f, 0, GX_ENABLE, GX_ANISO_4); + break; + default: + GX_InitTexObjLOD(&texobj_a, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + } GX_LoadTexObj(&texobj_a, GX_TEXMAP0); // load texture object so its ready to use } diff --git a/src/video/ogc/SDL_ogcgxcommon.h b/src/video/ogc/SDL_ogcgxcommon.h index 8286a08676a41..fe3360fe0d49a 100644 --- a/src/video/ogc/SDL_ogcgxcommon.h +++ b/src/video/ogc/SDL_ogcgxcommon.h @@ -23,13 +23,16 @@ #ifndef SDL_ogcgxcommon_h_ #define SDL_ogcgxcommon_h_ +#include "SDL_render.h" + #include #define GX_COLOR_AS_U32(c) *((u32*)&c) void OGC_draw_init(int w, int h, int h_aspect, int v_aspect); void OGC_set_viewport(int x, int y, int w, int h, float h_aspect, float v_aspect); -void OGC_load_texture(void *texels, int w, int h, u8 gx_format); +void OGC_load_texture(void *texels, int w, int h, u8 gx_format, + SDL_ScaleMode scale_mode); #endif /* SDL_ogcgxcommon_h_ */ From eaf7109e33642d32add6b393170ced54aadb4707 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Sun, 14 Jan 2024 11:23:05 +0300 Subject: [PATCH 21/21] ogc: implement double-buffering --- src/video/ogc/SDL_ogcvideo.c | 8 ++++++-- src/video/ogc/SDL_ogcvideo.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/video/ogc/SDL_ogcvideo.c b/src/video/ogc/SDL_ogcvideo.c index 28e0cccb1b5ce..b86da4ec633a5 100644 --- a/src/video/ogc/SDL_ogcvideo.c +++ b/src/video/ogc/SDL_ogcvideo.c @@ -108,7 +108,7 @@ int OGC_VideoInit(_THIS) /* Allocate the XFB */ videodata->xfb[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(vmode)); - videodata->xfb[1] = NULL; /* We'll allocate this when double-buffering */ + videodata->xfb[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(vmode)); VIDEO_ClearFrameBuffer(vmode, videodata->xfb[0], COLOR_BLACK); VIDEO_SetNextFramebuffer(videodata->xfb[0]); @@ -172,20 +172,24 @@ void OGC_VideoQuit(_THIS) void *OGC_video_get_xfb(_THIS) { SDL_VideoData *videodata = _this->driverdata; - return videodata->xfb[0]; + return videodata->xfb[videodata->fb_index]; } void OGC_video_flip(_THIS, bool vsync) { + SDL_VideoData *videodata = _this->driverdata; void *xfb = OGC_video_get_xfb(_this); GX_CopyDisp(xfb, GX_TRUE); GX_DrawDone(); GX_Flush(); + VIDEO_SetNextFramebuffer(xfb); VIDEO_Flush(); if (vsync) { VIDEO_WaitVSync(); } + + videodata->fb_index ^= 1; } #endif /* SDL_VIDEO_DRIVER_OGC */ diff --git a/src/video/ogc/SDL_ogcvideo.h b/src/video/ogc/SDL_ogcvideo.h index 193d94e82f377..3821d1966c085 100644 --- a/src/video/ogc/SDL_ogcvideo.h +++ b/src/video/ogc/SDL_ogcvideo.h @@ -32,6 +32,7 @@ typedef struct SDL_VideoData GXRModeObj *vmode; u8 *gp_fifo; void *xfb[2]; + u8 fb_index; } SDL_VideoData; typedef struct SDL_WindowData