Skip to content

Commit

Permalink
added Dreamcast specific texture formats to convert_format in the ope…
Browse files Browse the repository at this point in the history
…ngl hardware render driver for use with GLdc with new glTexSubImage2D function. Added 50hz/60hz selector from SDL 1.2, added more video framebuffer screen formats.
GPF committed Oct 14, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 48f2a11 commit 199641f
Showing 9 changed files with 1,265 additions and 22 deletions.
23 changes: 23 additions & 0 deletions src/render/opengl/SDL_render_gl.c
Original file line number Diff line number Diff line change
@@ -412,12 +412,35 @@ convert_format(GL_RenderData *renderdata, Uint32 pixel_format,
GLint *internalFormat, GLenum *format, GLenum *type)
{
switch (pixel_format) {
#ifdef __DREAMCAST__
case SDL_PIXELFORMAT_RGB888:
*internalFormat = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_BYTE; // Dreamcast-specific handling for RGB888
break;
case SDL_PIXELFORMAT_RGB565:
*internalFormat = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_SHORT_5_6_5; // Dreamcast-specific handling for RGB565
break;
case SDL_PIXELFORMAT_ARGB1555:
*internalFormat = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_5_5_5_1; // Dreamcast-specific handling for ARGB1555
break;
case SDL_PIXELFORMAT_ARGB8888:
*internalFormat = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_BYTE; // Dreamcast-specific handling for ARGB8888
break;
#else
case SDL_PIXELFORMAT_ARGB8888:
case SDL_PIXELFORMAT_RGB888:
*internalFormat = GL_RGBA8;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
break;
#endif
case SDL_PIXELFORMAT_ABGR8888:
case SDL_PIXELFORMAT_BGR888:
*internalFormat = GL_RGBA8;
807 changes: 807 additions & 0 deletions src/video/dreamcast/60hz.h

Large diffs are not rendered by default.

19 changes: 12 additions & 7 deletions src/video/dreamcast/SDL_dreamcastopengl.c
Original file line number Diff line number Diff line change
@@ -23,7 +23,9 @@
#include "SDL_dreamcastopengl.h"
#include "SDL_video.h"


#if defined(SDL_VIDEO_DRIVER_DREAMCAST) && defined(SDL_VIDEO_OPENGL)
#include <kos.h>
#include "GL/gl.h"
#include "GL/glu.h"
#include "GL/glkos.h"
@@ -186,6 +188,8 @@ int DREAMCAST_GL_Initialize(_THIS) {
if (DREAMCAST_GL_LoadLibrary(_this, NULL) < 0) {
return -1;
}

// vid_set_mode(DM_640x480_VGA, PM_RGB888);
return 0;
}

@@ -212,17 +216,18 @@ SDL_GLContext DREAMCAST_GL_CreateContext(_THIS, SDL_Window *window) {
}

// Store the GL attributes in the context
context->red_size = _this->gl_config.red_size;
context->green_size = _this->gl_config.green_size;
context->blue_size = _this->gl_config.blue_size;
context->alpha_size = _this->gl_config.alpha_size;
context->depth_size = _this->gl_config.depth_size;
context->stencil_size = _this->gl_config.stencil_size;
context->double_buffer = _this->gl_config.double_buffer;
// context->red_size = 5; // You can still set these if you want to keep them in context
// context->green_size = 6;
// context->blue_size = 5;
// context->alpha_size = 0;
// context->depth_size = 32;
// context->stencil_size = 8;
// context->double_buffer = 1;

return (SDL_GLContext) context;
}


int DREAMCAST_GL_MakeCurrent(_THIS, SDL_Window *window, SDL_GLContext context) {
DreamcastGLContext *glcontext = (DreamcastGLContext *) context;

199 changes: 186 additions & 13 deletions src/video/dreamcast/SDL_dreamcastvideo.c
Original file line number Diff line number Diff line change
@@ -53,11 +53,25 @@
#define DREAMCASTVID_DRIVER_EVDEV_NAME "dcevdev"

#include "SDL_dreamcastopengl.h"
//Custom code for 60Hz
static int sdl_dc_no_ask_60hz=0;
static int sdl_dc_default_60hz=0;
#include "60hz.h"

void SDL_DC_ShowAskHz(SDL_bool value)
{
sdl_dc_no_ask_60hz=!value;
}

void SDL_DC_Default60Hz(SDL_bool value)
{
sdl_dc_default_60hz=value;
}
/* Initialization/Query functions */
static int DREAMCAST_VideoInit(_THIS);
static void DREAMCAST_VideoQuit(_THIS);

void DREAMCAST_GetDisplayModes(_THIS, SDL_VideoDisplay *display);
int DREAMCAST_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
#ifdef SDL_INPUT_LINUXEV
static int evdev = 0;
static void DREAMCAST_EVDEV_Poll(_THIS);
@@ -108,6 +122,8 @@ static SDL_VideoDevice *DREAMCAST_CreateDevice(void)
device->VideoInit = DREAMCAST_VideoInit;
device->VideoQuit = DREAMCAST_VideoQuit;
device->PumpEvents = DREAMCAST_PumpEvents;
device->GetDisplayModes = DREAMCAST_GetDisplayModes;
device->SetDisplayMode = DREAMCAST_SetDisplayMode;
#ifdef SDL_INPUT_LINUXEV
if (evdev) {
device->PumpEvents = DREAMCAST_EVDEV_Poll;
@@ -152,32 +168,189 @@ static void DREAMCAST_EVDEV_Poll(_THIS)
SDL_EVDEV_Poll();
}
#endif

int DREAMCAST_VideoInit(_THIS)
{
int __sdl_dc_is_60hz=0;
int DREAMCAST_VideoInit(_THIS) {
SDL_VideoDisplay display;
SDL_DisplayMode current_mode;
int disp_mode;
int pixel_mode;
int width = 640, height = 480; // Default to 640x480, modify based on requirements
int bpp = 32; // Bits per pixel
Uint32 Rmask, Gmask, Bmask;

SDL_zero(current_mode);

current_mode.w = 640;
current_mode.h = 480;
current_mode.refresh_rate = 60;
// Determine if we are in 60Hz or 50Hz mode based on cable type and region
if (!vid_check_cable()) {
__sdl_dc_is_60hz = 1; // 60Hz for VGA
if (width == 320 && height == 240) disp_mode = DM_320x240_VGA;
else if (width == 640 && height == 480) disp_mode = DM_640x480_VGA;
else if (width == 768 && height == 480) disp_mode = DM_768x480_PAL_IL;
else {
SDL_SetError("Couldn't find requested mode in list");
return -1;
}
} else if (flashrom_get_region() != FLASHROM_REGION_US && !sdl_dc_ask_60hz()) {
__sdl_dc_is_60hz = 0;
if (width == 320 && height == 240) disp_mode = DM_320x240_PAL;
else if (width == 640 && height == 480) disp_mode = DM_640x480_PAL_IL;
else {
SDL_SetError("Couldn't find requested mode in list");
return -1;
}
} else {
__sdl_dc_is_60hz = 1; // Default to 60Hz
if (width == 320 && height == 240) disp_mode = DM_320x240;
else if (width == 640 && height == 480) disp_mode = DM_640x480;
else if (width == 768 && height == 480) disp_mode = DM_768x480;
else {
SDL_SetError("Couldn't find requested mode in list");
return -1;
}
}

// Set pixel mode based on bpp
switch (bpp) {
case 15: // ARGB1555
pixel_mode = PM_RGB555;
Rmask = 0x00007C00; // 5 bits for Red
Gmask = 0x000003E0; // 5 bits for Green
Bmask = 0x0000001F; // 5 bits for Blue
current_mode.format = SDL_PIXELFORMAT_ARGB1555; // Set the correct format
break;
case 16: // RGB565
pixel_mode = PM_RGB565;
Rmask = 0x0000F800;
Gmask = 0x000007E0;
Bmask = 0x0000001F;
current_mode.format = SDL_PIXELFORMAT_RGB565; // Set the correct format
break;
case 24: // RGB888
case 32: // ARGB8888
pixel_mode = PM_RGB888;
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
current_mode.format = SDL_PIXELFORMAT_RGB888; // Set the correct format
break;
default:
SDL_SetError("Unsupported pixel format");
return -1;
}

/* 32 bpp for default */
current_mode.format = SDL_PIXELFORMAT_RGB888;
// Initialize other properties of current_mode as needed
current_mode.w = width;
current_mode.h = height;
current_mode.refresh_rate = (__sdl_dc_is_60hz) ? 60 : 50; // Set refresh rate based on __sdl_dc_is_60hz
current_mode.driverdata = NULL;

SDL_zero(display);
display.desktop_mode = current_mode;
display.current_mode = current_mode;
display.driverdata = NULL;
SDL_AddDisplayMode(&display, &current_mode);

SDL_AddDisplayMode(&display, &current_mode);
SDL_AddVideoDisplay(&display, SDL_TRUE);
/* Set video mode using KOS */
vid_set_mode(DM_640x480_NTSC_IL, PM_RGB888);
return 1;

// Set the mode using KOS
vid_set_mode(disp_mode, pixel_mode);

SDL_Log("SDL2 Dreamcast video initialized.");
return 1; // Success
}


void DREAMCAST_GetDisplayModes(_THIS, SDL_VideoDisplay *display) {
SDL_DisplayMode mode;
int refresh_rate = __sdl_dc_is_60hz ? 60 : 50; // Determine refresh rate based on flag

// Adding a 320x240 mode in ARGB1555 format
SDL_zero(mode);
mode.w = 320;
mode.h = 240;
mode.format = SDL_PIXELFORMAT_ARGB1555; // Change as necessary
mode.refresh_rate = refresh_rate; // Use the determined refresh rate
SDL_AddDisplayMode(display, &mode);

// Adding a 640x480 mode in RGB565 format
SDL_zero(mode);
mode.w = 640;
mode.h = 480;
mode.format = SDL_PIXELFORMAT_RGB565; // Change as necessary
mode.refresh_rate = refresh_rate; // Use the determined refresh rate
SDL_AddDisplayMode(display, &mode);

// Adding a 768x480 mode in RGB565 format
SDL_zero(mode);
mode.w = 768;
mode.h = 480;
mode.format = SDL_PIXELFORMAT_RGB565; // Change as necessary
mode.refresh_rate = refresh_rate; // Use the determined refresh rate
SDL_AddDisplayMode(display, &mode);

// Adding a 640x480 mode in RGB888 format
SDL_zero(mode);
mode.w = 640;
mode.h = 480;
mode.format = SDL_PIXELFORMAT_RGB888; // Change as necessary
mode.refresh_rate = refresh_rate; // Use the determined refresh rate
SDL_AddDisplayMode(display, &mode);

// Optionally, you can add more modes depending on your requirements
// e.g., 800x600, 1024x768, etc.

SDL_Log("Display modes for Dreamcast retrieved successfully.");
}

int DREAMCAST_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode) {
int pixel_mode;
int disp_mode = -1; // Initialize to an invalid value

// Determine the appropriate display mode based on width and height
if (__sdl_dc_is_60hz) {
if (mode->w == 320 && mode->h == 240) {
disp_mode = DM_320x240; // 60Hz mode
} else if (mode->w == 640 && mode->h == 480) {
disp_mode = DM_640x480; // 60Hz mode
} else if (mode->w == 768 && mode->h == 480) {
disp_mode = DM_768x480; // 60Hz mode
}
} else { // 50Hz
if (mode->w == 320 && mode->h == 240) {
disp_mode = DM_320x240_PAL; // 50Hz mode
} else if (mode->w == 640 && mode->h == 480) {
disp_mode = DM_640x480_PAL_IL; // 50Hz mode
}
}

// Check if the determined display mode is valid
if (disp_mode < 0) {
SDL_SetError("Unsupported display mode");
return -1;
}

// Determine pixel mode based on the format in the mode
switch (mode->format) {
case SDL_PIXELFORMAT_ARGB1555:
pixel_mode = PM_RGB555; // Use the correct constant
break;
case SDL_PIXELFORMAT_RGB565:
pixel_mode = PM_RGB565; // Keep as is
break;
case SDL_PIXELFORMAT_RGB888:
pixel_mode = PM_RGB888; // Keep as is
break;
case SDL_PIXELFORMAT_UNKNOWN:
default:
SDL_SetError("Unsupported pixel format");
return -1;
}

// Set the video mode using KOS (no return value handling)
vid_set_mode(disp_mode, pixel_mode); // Assuming this function works fine without return checks

SDL_Log("Display mode set to: %dx%d @ %dHz", mode->w, mode->h, mode->refresh_rate);
return 0; // Return success
}

void DREAMCAST_VideoQuit(_THIS)
8 changes: 7 additions & 1 deletion test/dreamcast/test/nehe06/nehe06.c
Original file line number Diff line number Diff line change
@@ -71,11 +71,17 @@ void LoadGLTextures() {
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->w, image1->h, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->pixels);

// Create an empty texture first
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image1->w, image1->h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);

// Update the texture with the image data
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image1->w, image1->h, GL_RGB, GL_UNSIGNED_BYTE, image1->pixels);

SDL_FreeSurface(image1);
}


void InitGL(int Width, int Height) {
glViewport(0, 0, Width, Height);
LoadGLTextures();
2 changes: 1 addition & 1 deletion test/dreamcast/test/sdlanimation/Makefile.dc
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ clean:
rm -f $(OBJS) $(TARGET).elf $(TARGET).bin romdisk.img

$(TARGET).elf: $(OBJS)
kos-cc -o $(TARGET).elf $(OBJS) -lSDL2 -lSDL2main -lm -lkosutils
kos-cc -o $(TARGET).elf $(OBJS) -lSDL2 -lSDL2main -lGL -lm -lkosutils

$(TARGET).bin: $(TARGET).elf
$(KOS_OBJCOPY) -O binary $(TARGET).elf $(TARGET).bin
29 changes: 29 additions & 0 deletions test/dreamcast/test/textureGLrender/Makefile.dc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
TARGET = texture
OBJS = texture.o romdisk.o
KOS_ROMDISK_DIR = romdisk

# Update include paths for SDL2
KOS_CFLAGS+= -I../../../../build-scripts/build/include -I../../../../build-scripts/build/include-config-/SDL2 -I../../../../build-scripts/build/include/SDL2 -I. -DDREAMCAST

#KOS_CFLAGS+= -O3 -ffast-math -fno-common -fno-builtin -fno-exceptions -fstrict-aliasing

#KOS_CPPFLAGS=-fno-operator-names -fno-exceptions $(KOS_CFLAGS)

# Update library paths and libraries for SDL2
KOS_LIBS+= -L../../../../build-scripts/build -lSDL2 -lSDL2main

all: $(TARGET).bin

include $(KOS_BASE)/Makefile.rules

clean:
rm -f $(OBJS) $(TARGET).elf $(TARGET).bin romdisk.o romdisk.img

$(TARGET).elf: $(OBJS)
kos-cc -o $(TARGET).elf $(OBJS) -lSDL2 -lSDL2main -lGL -lm -lkosutils

$(TARGET).bin: $(TARGET).elf
$(KOS_OBJCOPY) -O binary $(TARGET).elf $(TARGET).bin

run: $(TARGET).bin
$(KOS_LOADER) $(TARGET).bin
Binary file not shown.
200 changes: 200 additions & 0 deletions test/dreamcast/test/textureGLrender/texture.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#include <SDL2/SDL.h>
#include <GL/gl.h>
#include <math.h>
#include <stdio.h>

#define FPS 60
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480

const Uint32 waittime = 1000 / FPS;

float xrot = 0.0f, yrot = 0.0f, zrot = 0.0f;
const float ROTATION_SPEED = 0.1f;

SDL_Texture* texture = NULL;

SDL_Texture* LoadTexture(SDL_Renderer* renderer, const char* filename) {
Uint8 *rowhi, *rowlo;
Uint8 *tmpbuf, tmpch;
SDL_Surface* image = SDL_LoadBMP(filename);
if (!image) {
fprintf(stderr, "Unable to load %s: %s\n", filename, SDL_GetError());
return NULL;
}
tmpbuf = (Uint8 *)malloc(image->pitch);
if (!tmpbuf) {
fprintf(stderr, "Out of memory\n");
return NULL;
}
rowhi = (Uint8 *)image->pixels;
rowlo = rowhi + (image->h * image->pitch) - image->pitch;
for (int i = 0; i < image->h / 2; ++i) {
for (int j = 0; j < image->w; ++j) {
tmpch = rowhi[j * 3];
rowhi[j * 3] = rowhi[j * 3 + 2];
rowhi[j * 3 + 2] = tmpch;
tmpch = rowlo[j * 3];
rowlo[j * 3] = rowlo[j * 3 + 2];
rowlo[j * 3 + 2] = tmpch;
}
memcpy(tmpbuf, rowhi, image->pitch);
memcpy(rowhi, rowlo, image->pitch);
memcpy(rowlo, tmpbuf, image->pitch);
rowhi += image->pitch;
rowlo -= image->pitch;
}
free(tmpbuf);

// Set transparent pixel as the pixel at (0,0) for BMPs with no alpha channel
if (image->format->BitsPerPixel == 24 || image->format->BitsPerPixel == 32) {
Uint32 transparentColor = *(Uint32 *)image->pixels;
SDL_SetColorKey(image, SDL_TRUE, transparentColor);
}

// Convert the surface to a format compatible with the renderer
SDL_Surface* converted_surface = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_ARGB1555, 0);
if (!converted_surface) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to convert surface: %s\n", SDL_GetError());
SDL_FreeSurface(image);
return NULL;
}

// Create texture from the converted surface
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, converted_surface);
if (!texture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create texture: %s\n", SDL_GetError());
}

SDL_FreeSurface(image); // Free the original surface
SDL_FreeSurface(converted_surface); // Free the converted surface
return texture;
}



void DrawScene(SDL_Renderer* renderer) {
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
SDL_RenderClear(renderer);

// Calculate the center of the screen
int centerX = WINDOW_WIDTH / 2;
int centerY = WINDOW_HEIGHT / 2;

// Define the center of the texture
int textureWidth = 256; // Width of your texture
int textureHeight = 256; // Height of your texture

// Define the corners of the quad
SDL_Point corners[4] = {
{-textureWidth / 2, -textureHeight / 2}, // Top-left corner
{textureWidth / 2, -textureHeight / 2}, // Top-right corner
{textureWidth / 2, textureHeight / 2}, // Bottom-right corner
{-textureWidth / 2, textureHeight / 2} // Bottom-left corner
};

// Apply rotation around the center
for (int i = 0; i < 4; i++) {
float x = corners[i].x;
float y = corners[i].y;

// Rotate around Z axis
corners[i].x = (int)(x * cos(zrot) - y * sin(zrot));
corners[i].y = (int)(x * sin(zrot) + y * cos(zrot));
}

// Calculate the destination rectangle's position (centered)
SDL_Rect dstRect = {
centerX - textureWidth / 2, // Center the rectangle on the screen
centerY - textureHeight / 2, // Center the rectangle on the screen
textureWidth, // Texture width
textureHeight // Texture height
};

// Draw the rotated quad with vertical flip
SDL_RenderCopyEx(renderer, texture, NULL, &dstRect, zrot * (180.0 / M_PI), NULL, SDL_FLIP_VERTICAL);

SDL_RenderPresent(renderer);
}


void HandleJoystickInput(SDL_Joystick* joystick) {
Uint8 hat = SDL_JoystickGetHat(joystick, 0);

if (hat & SDL_HAT_UP) xrot += ROTATION_SPEED;
if (hat & SDL_HAT_DOWN) xrot -= ROTATION_SPEED;
if (hat & SDL_HAT_LEFT) yrot -= ROTATION_SPEED;
if (hat & SDL_HAT_RIGHT) yrot += ROTATION_SPEED;

// Add Z-axis rotation with joystick buttons
if (SDL_JoystickGetButton(joystick, 0)) zrot += ROTATION_SPEED;
if (SDL_JoystickGetButton(joystick, 1)) zrot -= ROTATION_SPEED;
}

int main(int argc, char** argv) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL could not initialize! SDL Error: %s\n", SDL_GetError());
return 1;
}

SDL_Window* window = SDL_CreateWindow("SDL2 Texture Demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN|SDL_VIDEO_OPENGL);
if (!window) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Window could not be created! SDL Error: %s\n", SDL_GetError());
return 1;
}

SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (!renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Renderer could not be created! SDL Error: %s\n", SDL_GetError());
return 1;
}
SDL_RendererInfo info;
if (SDL_GetRendererInfo(renderer, &info) == 0) {
SDL_Log("Renderer Name: %s", info.name);
SDL_Log("Texture Formats: ");
for (int i = 0; i < info.num_texture_formats; i++) {
SDL_Log(" %s", SDL_GetPixelFormatName(info.texture_formats[i]));
}
}
texture = LoadTexture(renderer, "/rd/Troy2024.bmp");
if (!texture) {
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}

SDL_Event event;
int running = 1;

SDL_Joystick* joystick = SDL_JoystickOpen(0);
if (!joystick) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to open joystick: %s\n", SDL_GetError());
}

while (running) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = 0;
}
}

if (joystick) {
HandleJoystickInput(joystick);
}

DrawScene(renderer);

SDL_Delay(waittime); // Wait for the remaining frame time
}

if (joystick) {
SDL_JoystickClose(joystick);
}
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

0 comments on commit 199641f

Please sign in to comment.