Add option for CRT shader (#314)

* add CRT shader

* more

* more

* update type

* more

* more

* more

* more
This commit is contained in:
Logan McNaughton
2025-03-10 16:52:25 +01:00
committed by GitHub
parent 67a8bb5e4c
commit e93c275795
10 changed files with 1136 additions and 41 deletions
+59 -25
View File
@@ -3,6 +3,7 @@
#include "rdp_device.hpp"
#include "interface.hpp"
#include "spirv.hpp"
#include "spirv_crt.hpp"
#include <SDL3/SDL_vulkan.h>
using namespace Vulkan;
@@ -51,8 +52,6 @@ enum vi_registers
VI_REGS_COUNT
};
static bool fullscreen;
static bool integer_scaling;
static SDL_Window *window;
static RDP::CommandProcessor *processor;
static SDL_WSIPlatform *wsi_platform;
@@ -64,6 +63,8 @@ static CALL_BACK callback;
static GFX_INFO gfx_info;
static uint32_t region;
static bool crop_letterbox;
static const uint32_t *fragment_spirv;
static size_t fragment_size;
typedef struct
{
@@ -77,6 +78,12 @@ typedef struct
uint8_t depthbuffer_enabled;
} FrameBufferInfo;
typedef struct
{
float SourceSize[4];
float OutputSize[4];
} Push;
static uint8_t *rdram_dirty;
static uint64_t sync_signal;
static FrameBufferInfo frame_buffer_info;
@@ -165,8 +172,8 @@ bool sdl_event_filter(void *userdata, SDL_Event *event)
case SDL_SCANCODE_RETURN:
if (event->key.mod & SDL_KMOD_ALT)
{
fullscreen = !fullscreen;
SDL_SetWindowFullscreen(window, fullscreen);
gfx_info.fullscreen = !gfx_info.fullscreen;
SDL_SetWindowFullscreen(window, gfx_info.fullscreen);
}
break;
case SDL_SCANCODE_F:
@@ -176,7 +183,7 @@ bool sdl_event_filter(void *userdata, SDL_Event *event)
}
break;
case SDL_SCANCODE_ESCAPE:
if (fullscreen)
if (gfx_info.fullscreen)
callback.emu_running = false;
break;
case SDL_SCANCODE_F4:
@@ -196,7 +203,7 @@ bool sdl_event_filter(void *userdata, SDL_Event *event)
return 0;
}
void rdp_new_processor(GFX_INFO _gfx_info, uint32_t upscale)
void rdp_new_processor(GFX_INFO _gfx_info)
{
memset(&frame_buffer_info, 0, sizeof(FrameBufferInfo));
sync_signal = 0;
@@ -209,12 +216,12 @@ void rdp_new_processor(GFX_INFO _gfx_info, uint32_t upscale)
}
RDP::CommandProcessorFlags flags = 0;
if (upscale == 2)
if (gfx_info.upscale == 2)
{
flags |= RDP::COMMAND_PROCESSOR_FLAG_SUPER_SAMPLED_DITHER_BIT;
flags |= RDP::COMMAND_PROCESSOR_FLAG_UPSCALING_2X_BIT;
}
else if (upscale == 4)
else if (gfx_info.upscale == 4)
{
flags |= RDP::COMMAND_PROCESSOR_FLAG_SUPER_SAMPLED_DITHER_BIT;
flags |= RDP::COMMAND_PROCESSOR_FLAG_UPSCALING_4X_BIT;
@@ -223,7 +230,7 @@ void rdp_new_processor(GFX_INFO _gfx_info, uint32_t upscale)
processor = new RDP::CommandProcessor(wsi->get_device(), gfx_info.RDRAM, 0, gfx_info.RDRAM_SIZE, gfx_info.RDRAM_SIZE / 2, flags);
}
void rdp_init(void *_window, GFX_INFO _gfx_info, uint32_t _upscale, bool _integer_scaling, bool _fullscreen)
void rdp_init(void *_window, GFX_INFO _gfx_info)
{
window = (SDL_Window *)_window;
bool result = SDL_AddEventWatch(sdl_event_filter, nullptr);
@@ -234,8 +241,18 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, uint32_t _upscale, bool _intege
}
gfx_info = _gfx_info;
fullscreen = _fullscreen;
integer_scaling = _integer_scaling;
if (gfx_info.crt)
{
fragment_spirv = crt_fragment_spirv;
fragment_size = sizeof(crt_fragment_spirv);
}
else
{
fragment_spirv = plain_fragment_spirv;
fragment_size = sizeof(plain_fragment_spirv);
}
bool window_vsync = 0;
wsi = new WSI;
wsi_platform = new SDL_WSIPlatform;
@@ -254,7 +271,7 @@ void rdp_init(void *_window, GFX_INFO _gfx_info, uint32_t _upscale, bool _intege
}
rdram_dirty = (uint8_t *)malloc(gfx_info.RDRAM_SIZE / 8);
rdp_new_processor(gfx_info, _upscale);
rdp_new_processor(gfx_info);
if (!processor->device_is_supported())
{
@@ -297,15 +314,14 @@ void rdp_close()
}
}
static void calculate_viewport(float *x, float *y, float *width, float *height)
static void calculate_viewport(float *x, float *y, float *width, float *height, uint32_t display_height)
{
const int32_t display_width = gfx_info.widescreen ? (gfx_info.PAL ? 512 : 426) : (gfx_info.PAL ? 384 : 320);
const int32_t display_height = gfx_info.PAL ? 288 : 240;
uint32_t display_width = gfx_info.widescreen ? display_height * 16 / 9 : display_height * 4 / 3;
int w, h;
SDL_GetWindowSize(window, &w, &h);
if (integer_scaling)
if (gfx_info.integer_scaling)
{
// Integer scaling path
int scale_x = w / display_width;
@@ -320,6 +336,13 @@ static void calculate_viewport(float *x, float *y, float *width, float *height)
*width = scaled_width;
*height = scaled_height;
// Center the viewport
int integer_x = (w - *width) / 2.0f;
int integer_y = (h - *height) / 2.0f;
*x = integer_x;
*y = integer_y;
}
else
{
@@ -330,11 +353,11 @@ static void calculate_viewport(float *x, float *y, float *width, float *height)
*width = display_width * scale;
*height = display_height * scale;
}
// Center the viewport
*x = (w - *width) / 2.0f;
*y = (h - *height) / 2.0f;
// Center the viewport
*x = (w - *width) / 2.0f;
*y = (h - *height) / 2.0f;
}
}
static void render_frame(Vulkan::Device &device)
@@ -361,15 +384,16 @@ static void render_frame(Vulkan::Device &device)
Vulkan::ImageHandle image = processor->scanout(options);
// Normally reflection is automated.
Vulkan::ResourceLayout vertex_layout = {};
Vulkan::ResourceLayout fragment_layout = {};
fragment_layout.output_mask = 1 << 0;
fragment_layout.sets[0].sampled_image_mask = 1 << 0;
if (gfx_info.crt)
fragment_layout.push_constant_size = sizeof(Push);
// This request is cached.
auto *program = device.request_program(vertex_spirv, sizeof(vertex_spirv),
fragment_spirv, sizeof(fragment_spirv),
fragment_spirv, fragment_size,
&vertex_layout,
&fragment_layout);
@@ -379,9 +403,6 @@ static void render_frame(Vulkan::Device &device)
auto rp = device.get_swapchain_render_pass(Vulkan::SwapchainRenderPass::ColorOnly);
cmd->begin_render_pass(rp);
VkViewport vp = cmd->get_viewport();
calculate_viewport(&vp.x, &vp.y, &vp.width, &vp.height);
cmd->set_program(program);
// Basic default render state.
@@ -392,6 +413,19 @@ static void render_frame(Vulkan::Device &device)
// If we don't have an image, we just get a cleared screen in the render pass.
if (image)
{
VkViewport vp = cmd->get_viewport();
calculate_viewport(&vp.x, &vp.y, &vp.width, &vp.height, image->get_height());
if (gfx_info.crt)
{
// Set shader parameters
Push push = {
{float(image->get_width()), float(image->get_height()), 1.0f / float(image->get_width()), 1.0f / float(image->get_height())},
{vp.width, vp.height, 1.0f / vp.width, 1.0f / vp.height},
};
cmd->push_constants(&push, 0, sizeof(push));
}
cmd->set_texture(0, 0, image->get_view(), Vulkan::StockSampler::LinearClamp);
cmd->set_viewport(vp);
// The vertices are constants in the shader.