SDL-1.2: dc: Add strided textured video mode

From 6648216ef09099d722623a155693fabee972380f Mon Sep 17 00:00:00 2001
From: GPF <[EMAIL REDACTED]>
Date: Sun, 14 Jun 2026 20:10:05 -0700
Subject: [PATCH] dc: Add strided textured video mode

---
 Makefile.dc                |  16 +--
 README.DC                  |   2 +-
 include/SDL_dreamcast.h    |   1 +
 src/video/dc/SDL_dcvideo.c | 208 +++++++++++++++++++++++++------------
 4 files changed, 155 insertions(+), 72 deletions(-)

diff --git a/Makefile.dc b/Makefile.dc
index 4899bfb1..8a642704 100644
--- a/Makefile.dc
+++ b/Makefile.dc
@@ -1,7 +1,7 @@
 # Custom version by Ian Micheal included fast math built in DreamHAL
 # based on chui's great SDL, now for KOS 2.x fast blit and pixel paths.
 # DreamHAL memory routines and replacements; threading updates.
-# Set GL=1 to build the GLdc-enabled archive.
+# GLdc support is enabled by default; SDL_OPENGL selects it at runtime.
 
 CC := kos-cc
 AR := kos-ar
@@ -9,12 +9,10 @@ LD := kos-ld
 ARFLAGS ?= rcs
 
 # ------------------------------ Build variant ------------------------------
-ifdef GL
-DEFS   += -DSDL_VIDEO_OPENGL=1
-TARGET := libSDL_gl.a
-else
+# Dreamcast SDL now builds with GLdc support enabled by default.
+# Runtime selection happens via SDL_OPENGL in SDL_SetVideoMode().
+DEFS += -DSDL_VIDEO_OPENGL=1
 TARGET := libSDL.a
-endif
 
 # ------------------------------ Flags ------------------------------
 # KOS provides KOS_BASE and KOS_ARCH via environ.sh
@@ -24,7 +22,11 @@ KOS_CFLAGS += -Isrc/video -Isrc/video/dc -DNO_SIGNAL_H -DENABLE_DC
 # Ensure Dreamcast platform config and backend are selected
 KOS_CFLAGS += -D__DREAMCAST__ -DSDL_VIDEO_DRIVER_DREAMCAST
 
-CFLAGS   := -Wimplicit-function-declaration $(DEFS) -Iinclude
+CFLAGS += -Wimplicit-function-declaration
+CFLAGS += $(KOS_CFLAGS)
+CFLAGS += $(DEFS)
+CFLAGS += -Iinclude
+
 DEPFLAGS := -MMD -MP
 
 # ------------------------------ Sources ------------------------------
diff --git a/README.DC b/README.DC
index a1a24873..f7baa6bf 100644
--- a/README.DC
+++ b/README.DC
@@ -65,7 +65,7 @@ Select a backend via `Makefile.dc` or by API before `SDL_SetVideoMode`.
 
 **Video & display**
 
-* `SDL_DC_SetVideoDriver(SDL_DC_DMA_VIDEO | SDL_DC_TEXTURED_VIDEO | SDL_DC_DIRECT_VIDEO)` — choose backend (call before `SDL_SetVideoMode`).
+* `SDL_DC_SetVideoDriver(SDL_DC_DMA_VIDEO | SDL_DC_TEXTURED_VIDEO | SDL_DC_TEXTURED_STRIDED_VIDEO | SDL_DC_DIRECT_VIDEO)` — choose backend (call before `SDL_SetVideoMode`).
 * `SDL_DC_SetWindow(width, height)` — set virtual window size (textured mode).
 
 **Timing / refresh**
diff --git a/include/SDL_dreamcast.h b/include/SDL_dreamcast.h
index 2cb2efc8..8b32cc20 100644
--- a/include/SDL_dreamcast.h
+++ b/include/SDL_dreamcast.h
@@ -9,6 +9,7 @@ extern "C" {
 typedef enum {
     SDL_DC_DMA_VIDEO,
     SDL_DC_TEXTURED_VIDEO,
+    SDL_DC_TEXTURED_STRIDED_VIDEO,
     SDL_DC_DIRECT_VIDEO
 } SDL_DC_driver;
 
diff --git a/src/video/dc/SDL_dcvideo.c b/src/video/dc/SDL_dcvideo.c
index 13562a25..8d20207e 100644
--- a/src/video/dc/SDL_dcvideo.c
+++ b/src/video/dc/SDL_dcvideo.c
@@ -285,22 +285,28 @@ const static SDL_Rect *tex_modes[]={
 	NULL
 };
 
+
 SDL_Rect **DC_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
 {
-#ifndef SDL_VIDEO_OPENGL
-	if ((!(flags & SDL_FULLSCREEN))&&(SDL_DC_GetVideoDriver()==SDL_DC_TEXTURED_VIDEO))
+	SDL_DC_driver driver = SDL_DC_GetVideoDriver();
+
+	if (!(flags & SDL_OPENGL) && (!(flags & SDL_FULLSCREEN)) &&
+	    (driver == SDL_DC_TEXTURED_VIDEO || driver == SDL_DC_TEXTURED_STRIDED_VIDEO))
 	{
 		switch(format->BitsPerPixel)
 		{
 			case 16:
-				SDL_Rect **thetex_modes = (SDL_Rect **)&tex_modes;
-				return thetex_modes;
+				if (driver == SDL_DC_TEXTURED_STRIDED_VIDEO) {
+					return((SDL_Rect **)-1);
+				} else {
+					SDL_Rect **thetex_modes = (SDL_Rect **)&tex_modes;
+					return thetex_modes;
+				}
 			default:
 				return NULL;	
 		}
 	}
 	else
-#endif
 		switch(format->BitsPerPixel)
 		{
 			case 15:
@@ -315,6 +321,7 @@ SDL_Rect **DC_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
 	return((SDL_Rect **)-1);
 }
 
+
 static int sdl_dc_pvr_inited=0;
 static int sdl_dc_wait_vblank=0;
 
@@ -323,16 +330,26 @@ void SDL_DC_VerticalWait(SDL_bool value)
 	sdl_dc_wait_vblank=value;
 }
 
-#ifndef SDL_VIDEO_OPENGL
 static int sdl_dc_textured=0;
 static int sdl_dc_width=0;
 static int sdl_dc_height=0;
 static int sdl_dc_bpp=0;
 static int sdl_dc_wtex=0;
 static int sdl_dc_htex=0;
+static int sdl_dc_pvr_wtex=0;
+static int sdl_dc_pvr_htex=0;
+static int sdl_dc_tex_bytes=0;
+static int sdl_dc_strided=0;
+static Uint32 sdl_dc_txr_format=0;
 static pvr_ptr_t sdl_dc_memtex;
 static unsigned short *sdl_dc_buftex;
 static void *sdl_dc_memfreed;
+
+#ifdef SDL_VIDEO_OPENGL
+static int sdl_dc_use_gl=0;
+#define SDL_DC_USING_GL() (sdl_dc_use_gl)
+#else
+#define SDL_DC_USING_GL() (0)
 #endif
 
 static void *sdl_dc_dblfreed=NULL;
@@ -341,6 +358,23 @@ static unsigned sdl_dc_dblsize=0;
 
 int __sdl_dc_is_60hz=0;
 
+static int DC_NextPowerOfTwo(int value)
+{
+	int result = 64;
+
+	while (result < value) {
+		result <<= 1;
+	}
+
+	return result;
+}
+
+static int DC_IsTexturedDriver(SDL_DC_driver driver)
+{
+	return (driver == SDL_DC_TEXTURED_VIDEO ||
+	        driver == SDL_DC_TEXTURED_STRIDED_VIDEO);
+}
+
 SDL_Surface *DC_SetVideoMode(_THIS, SDL_Surface *current,
 				int width, int height, int bpp, Uint32 flags)
 {
@@ -348,30 +382,71 @@ SDL_Surface *DC_SetVideoMode(_THIS, SDL_Surface *current,
 	Uint32 Rmask, Gmask, Bmask;
 
 	
-#ifndef SDL_VIDEO_OPENGL
-	if ((!(flags&SDL_FULLSCREEN))&&(SDL_DC_GetVideoDriver()==SDL_DC_TEXTURED_VIDEO))
+#ifdef SDL_VIDEO_OPENGL
+	sdl_dc_use_gl = ((flags & SDL_OPENGL) != 0);
+#else
+	if (flags & SDL_OPENGL) {
+		SDL_SetError("OpenGL support not compiled in");
+		return(NULL);
+	}
+#endif
+
+	if (!SDL_DC_USING_GL() && (!(flags&SDL_FULLSCREEN)) &&
+	    DC_IsTexturedDriver(SDL_DC_GetVideoDriver()))
 	{
-		// fprintf(stderr, "\nWelcome to SDLdc = SDL_DC_TEXTURED_VIDEO");
-		for(sdl_dc_wtex=64;sdl_dc_wtex<width;sdl_dc_wtex<<=1);
-		for(sdl_dc_htex=64;sdl_dc_htex<height;sdl_dc_htex<<=1);
-		if (sdl_dc_wtex!=width || sdl_dc_htex!=height || bpp !=16) 
+		sdl_dc_width=width;
+		sdl_dc_height=height;
+		sdl_dc_bpp=bpp;
+		sdl_dc_strided=(SDL_DC_GetVideoDriver()==SDL_DC_TEXTURED_STRIDED_VIDEO);
+
+		if (bpp != 16) 
 		{
 			sdl_dc_textured=0;
 			SDL_SetError("Couldn't find requested mode in list");
 			return(NULL);
 		}
-		sdl_dc_width=width;
-		sdl_dc_height=height;
-		sdl_dc_bpp=bpp;
+
+		if (sdl_dc_strided) {
+			if ((width & 31) != 0) {
+				sdl_dc_textured = 0;
+				SDL_SetError("Strided textured mode requires width to be a multiple of 32 pixels");
+				return NULL;
+			}
+
+			if (height <= 0) {
+				sdl_dc_textured = 0;
+				SDL_SetError("Strided textured mode requires a positive height");
+				return NULL;
+			}
+
+			sdl_dc_wtex = width;
+			sdl_dc_htex = height;
+			sdl_dc_pvr_wtex = DC_NextPowerOfTwo(width);
+			sdl_dc_pvr_htex = DC_NextPowerOfTwo(height);
+		}
+		else {
+			sdl_dc_wtex=DC_NextPowerOfTwo(width);
+			sdl_dc_htex=DC_NextPowerOfTwo(height);
+			if (sdl_dc_wtex!=width || sdl_dc_htex!=height) 
+			{
+				sdl_dc_textured=0;
+				SDL_SetError("Couldn't find requested mode in list");
+				return(NULL);
+			}
+			sdl_dc_pvr_wtex=sdl_dc_wtex;
+			sdl_dc_pvr_htex=sdl_dc_htex;
+		}
+
 		width=640;
 		height=480;
 		bpp=16;
 		sdl_dc_textured=-1;
-		// fprintf(stderr, "\nBye to SDLdc = SDL_DC_TEXTURED_VIDEO - sdl_dc_width %d ,sdl_dc_height %d ,sdl_dc_bpp %d",sdl_dc_width,sdl_dc_height,sdl_dc_bpp);
 	}
-	else
+	else {
 		sdl_dc_textured=0;
-#endif
+		sdl_dc_strided=0;
+	}
+
 
 	if (!vid_check_cable())
 	{
@@ -435,9 +510,6 @@ SDL_Surface *DC_SetVideoMode(_THIS, SDL_Surface *current,
 		Rmask = 0x00ff0000;
 		Gmask = 0x0000ff00;
 		Bmask = 0x000000ff;
-#ifdef	SDL_VIDEO_OPENGL
-		if (!(flags & SDL_OPENGL))
-#endif
 		break;
 	default:
 		SDL_SetError("Couldn't find requested mode in list");
@@ -451,17 +523,16 @@ SDL_Surface *DC_SetVideoMode(_THIS, SDL_Surface *current,
 
 
 	/* Set up the new mode framebuffer */
-#ifndef SDL_VIDEO_OPENGL
-	if (sdl_dc_textured)
+	if (!SDL_DC_USING_GL() && sdl_dc_textured)
 	{
 		// fprintf(stdout, "\nWelcome to SDLdc3! -sdl_dc_width :%d , sdl_dc_height %d \n",sdl_dc_width,sdl_dc_height);
+		current->flags = SDL_SWSURFACE;
 		current->w=sdl_dc_width;
 		current->h=sdl_dc_height;
-		current->pitch = sdl_dc_width*2; //(bpp>>3);
+		current->pitch = sdl_dc_wtex*2; //(bpp>>3);
 		__sdl_dc_mouse_shift=640/sdl_dc_width;
 	}
 	else
-#endif
 	{
 		current->flags = (SDL_FULLSCREEN|SDL_HWSURFACE);
 		current->w = width;
@@ -476,17 +547,23 @@ SDL_Surface *DC_SetVideoMode(_THIS, SDL_Surface *current,
 	vid_set_mode(disp_mode,pixel_mode);
 	current->pixels = vram_l; //vram_s;
 
-#ifndef SDL_VIDEO_OPENGL
-	if (sdl_dc_textured)
+	if (!SDL_DC_USING_GL() && sdl_dc_textured)
 	{
 		pvr_init_defaults();
 		pvr_dma_init();
 		sdl_dc_pvr_inited = 1;
-		sdl_dc_memtex = pvr_mem_malloc(sdl_dc_wtex*sdl_dc_htex*2);
+		sdl_dc_tex_bytes = sdl_dc_wtex*sdl_dc_htex*(sdl_dc_bpp>>3);
+		sdl_dc_txr_format = PVR_TXRFMT_RGB565|PVR_TXRFMT_NONTWIDDLED;
+		if (sdl_dc_strided) {
+			sdl_dc_txr_format |= (1 << 25); // PVR_TXRFMT_X32_STRIDE;
+		}
+		sdl_dc_memtex = pvr_mem_malloc(sdl_dc_pvr_wtex*sdl_dc_pvr_htex*(sdl_dc_bpp>>3));
+		SDL_DC_SetWindow(sdl_dc_width, sdl_dc_height);
+
 		if (flags & SDL_DOUBLEBUF)
 		{
 			current->flags |= SDL_DOUBLEBUF;
-			sdl_dc_memfreed = calloc(64+(sdl_dc_wtex*sdl_dc_htex*(sdl_dc_bpp>>3)),1);
+			sdl_dc_memfreed = calloc(64+sdl_dc_tex_bytes,1);
 			sdl_dc_buftex = (unsigned short *)(((((unsigned)sdl_dc_memfreed)+32)/32)*32);
 			current->pixels = sdl_dc_buftex;
 		}
@@ -496,25 +573,22 @@ SDL_Surface *DC_SetVideoMode(_THIS, SDL_Surface *current,
 			current->pixels = sdl_dc_memtex;
 		}
 	}
-	else
-#else
-	if (flags & SDL_OPENGL) {
+#ifdef SDL_VIDEO_OPENGL
+	else if (SDL_DC_USING_GL()) {
 		this->gl_config.driver_loaded = 1;
 		current->flags = SDL_FULLSCREEN | SDL_OPENGL;
 		current->pixels = NULL;
 		sdl_dc_pvr_inited = 1;
 		glKosInit();
-	} else
+	}
 #endif
-	if (flags & SDL_DOUBLEBUF) {
+	else if (flags & SDL_DOUBLEBUF) {
 		current->flags |= SDL_DOUBLEBUF;
-#ifndef SDL_VIDEO_OPENGL
 		if (SDL_DC_GetVideoDriver()!=SDL_DC_DIRECT_VIDEO)
 		{
 			pvr_dma_init();
 			sdl_dc_pvr_inited = 1;
 		}
-#endif
 		sdl_dc_dblsize=(width*height*(bpp>>3));
 		sdl_dc_dblfreed = calloc(128+sdl_dc_dblsize,1);
 		sdl_dc_dblmem = (unsigned short *)(((((unsigned)sdl_dc_dblfreed)+64)/64)*64);
@@ -546,29 +620,30 @@ static void DC_UnlockHWSurface(_THIS, SDL_Surface *surface)
 	return;
 }
 
-#ifndef SDL_VIDEO_OPENGL
-
 static float sdl_dc_u1=0.3f;
 static float sdl_dc_u2=0.3f;
 static float sdl_dc_v1=0.9f;
 static float sdl_dc_v2=0.6f;
 
-#endif
-
 void SDL_DC_SetWindow(int width, int height)
 {
-#ifndef SDL_VIDEO_OPENGL
 	// fprintf(stdout, "\nWelcome to SDL_DC_SetWindow width: %d , height : %d, sdl_dc_width: %d , sdl_dc_height: %d \n", width, height, sdl_dc_width, sdl_dc_height);
 	sdl_dc_width=width;
 	sdl_dc_height=height;
-	sdl_dc_u1=0.3f*(1.0f/((float)sdl_dc_wtex));
-	sdl_dc_v1=0.3f*(1.0f/((float)sdl_dc_wtex));
-	sdl_dc_u2=(((float)sdl_dc_width)+0.7f)*(1.0f/((float)sdl_dc_wtex));
-	sdl_dc_v2=(((float)sdl_dc_height)+0.7f)*(1.0f/((float)sdl_dc_htex));
-#endif
+
+	if (sdl_dc_strided) {
+		sdl_dc_u1=0.0f;
+		sdl_dc_v1=0.0f;
+		sdl_dc_u2=((float)sdl_dc_width)*(1.0f/((float)sdl_dc_pvr_wtex));
+		sdl_dc_v2=((float)sdl_dc_height)*(1.0f/((float)sdl_dc_pvr_htex));
+	} else {
+		sdl_dc_u1=0.3f*(1.0f/((float)sdl_dc_pvr_wtex));
+		sdl_dc_v1=0.3f*(1.0f/((float)sdl_dc_pvr_wtex));
+		sdl_dc_u2=(((float)sdl_dc_width)+0.7f)*(1.0f/((float)sdl_dc_pvr_wtex));
+		sdl_dc_v2=(((float)sdl_dc_height)+0.7f)*(1.0f/((float)sdl_dc_pvr_htex));
+	}
 }
 
-#ifndef SDL_VIDEO_OPENGL
 static void sdl_dc_blit_textured(void)
 {
 #define DX1 0.0f
@@ -590,14 +665,17 @@ static void sdl_dc_blit_textured(void)
 
     if (sdl_dc_buftex)
     {
-        dcache_flush_range((unsigned)sdl_dc_buftex, sdl_dc_wtex*sdl_dc_htex*2);
+        dcache_flush_range((unsigned)sdl_dc_buftex, sdl_dc_tex_bytes);
         while (!pvr_dma_ready());
-        pvr_txr_load_dma(sdl_dc_buftex, sdl_dc_memtex, sdl_dc_wtex*sdl_dc_htex*2, -1, NULL, 0);
+        pvr_txr_load_dma(sdl_dc_buftex, sdl_dc_memtex, sdl_dc_tex_bytes, -1, NULL, 0);
         // pvr_txr_load(sdl_dc_buftex, sdl_dc_memtex, sdl_dc_wtex*sdl_dc_htex*2);
     }
 
     pvr_dr_init(&dr_state);
-    pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565|PVR_TXRFMT_NONTWIDDLED, sdl_dc_wtex, sdl_dc_htex, sdl_dc_memtex, PVR_FILTER_NONE);//any filtering makes fps dip in software rendering
+    if (sdl_dc_strided) {
+        PVR_SET(PVR_TEXTURE_MODULO, sdl_dc_wtex / 32);
+    }
+    pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, sdl_dc_txr_format, sdl_dc_pvr_wtex, sdl_dc_pvr_htex, sdl_dc_memtex, PVR_FILTER_NONE);//any filtering makes fps dip in software rendering
 
     hdr = (pvr_poly_hdr_t *)pvr_dr_target(dr_state);
     pvr_poly_compile(hdr, &cxt);
@@ -641,16 +719,13 @@ static void sdl_dc_blit_textured(void)
 #undef DWI
 #undef DHE
 }
-#endif
 extern int virtual_keyboard_visible;
 extern void drawVirtualKeyboard(void* fb);
 static int DC_FlipHWSurface(_THIS, SDL_Surface *surface)
 {
-#ifndef SDL_VIDEO_OPENGL
-	if (sdl_dc_textured)
+	if (!SDL_DC_USING_GL() && sdl_dc_textured)
 		sdl_dc_blit_textured();
-	else
-	if (surface->flags & SDL_DOUBLEBUF) {
+	else if (!SDL_DC_USING_GL() && (surface->flags & SDL_DOUBLEBUF)) {
 		if (sdl_dc_wait_vblank)
 			vid_waitvbl();
 		if (SDL_DC_GetVideoDriver()!=SDL_DC_DIRECT_VIDEO)
@@ -662,21 +737,17 @@ static int DC_FlipHWSurface(_THIS, SDL_Surface *surface)
 		else
 			sq_cpy(vram_l,sdl_dc_dblmem,sdl_dc_dblsize);
 	}
-	else
-#endif
-		if (sdl_dc_wait_vblank)
-			vid_waitvbl();
+	else if (sdl_dc_wait_vblank)
+		vid_waitvbl();
 
 	return(0);
 }
 
 static void DC_UpdateRects(_THIS, int numrects, SDL_Rect *rects)
 {
-#ifndef SDL_VIDEO_OPENGL
-	if (sdl_dc_textured){				
+	if (!SDL_DC_USING_GL() && sdl_dc_textured){				
 			sdl_dc_blit_textured();
 	}
-#endif
 }
 
 static int DC_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
@@ -697,7 +768,6 @@ static void DC_VideoQuit(_THIS)
 		if (sdl_dc_dblmem)
 			pvr_init_defaults();
 //		pvr_dma_shutdown();
-#ifdef HAVE_DCTEXTURED
 		if (sdl_dc_buftex)
 			free(sdl_dc_memfreed);
 		sdl_dc_buftex=0;
@@ -707,7 +777,17 @@ static void DC_VideoQuit(_THIS)
 			pvr_mem_reset();
 		}
 		sdl_dc_memtex=0;
-#endif
+		sdl_dc_textured=0;
+		sdl_dc_width=0;
+		sdl_dc_height=0;
+		sdl_dc_bpp=0;
+		sdl_dc_wtex=0;
+		sdl_dc_htex=0;
+		sdl_dc_pvr_wtex=0;
+		sdl_dc_pvr_htex=0;
+		sdl_dc_tex_bytes=0;
+		sdl_dc_strided=0;
+		sdl_dc_txr_format=0;
 		if (sdl_dc_dblmem)
 			free(sdl_dc_dblfreed);
 		sdl_dc_dblmem=NULL;