libretro: Support blargg's NTSC filter

This commit is contained in:
Rupert Carmichael 2023-08-04 21:01:06 -04:00 committed by Screwtapello
parent ea05fa0c3e
commit 18afbc3770
2 changed files with 109 additions and 8 deletions

View File

@ -287,7 +287,32 @@ static void flush_variables()
else
run_ahead_frames = atoi(variable.value);
}
variable = { "bsnes_video_filter", nullptr };
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
{
if (strcmp(variable.value, "None") == 0) {
program->filterRender = &Filter::None::render;
program->filterSize = &Filter::None::size;
}
else if (strcmp(variable.value, "NTSC (RF)") == 0) {
program->filterRender = &Filter::NTSC_RF::render;
program->filterSize = &Filter::NTSC_RF::size;
}
else if (strcmp(variable.value, "NTSC (Composite)") == 0) {
program->filterRender = &Filter::NTSC_Composite::render;
program->filterSize = &Filter::NTSC_Composite::size;
}
else if (strcmp(variable.value, "NTSC (S-Video)") == 0) {
program->filterRender = &Filter::NTSC_SVideo::render;
program->filterSize = &Filter::NTSC_SVideo::size;
}
else if (strcmp(variable.value, "NTSC (RGB)") == 0) {
program->filterRender = &Filter::NTSC_RGB::render;
program->filterSize = &Filter::NTSC_RGB::size;
}
}
// Refresh Geometry
struct retro_system_av_info avinfo;
retro_get_system_av_info(&avinfo);
@ -483,6 +508,7 @@ static void set_environment_info(retro_environment_t cb)
{ "bsnes_coprocessor_prefer_hle", "Coprocessor Prefer HLE; ON|OFF" },
{ "bsnes_sgb_bios", "Preferred Super GameBoy BIOS (restart); SGB1.sfc|SGB2.sfc" },
{ "bsnes_run_ahead_frames", "Amount of frames for run-ahead; OFF|1|2|3|4" },
{ "bsnes_video_filter", "Video Filter; None|NTSC (RF)|NTSC (Composite)|NTSC (S-Video)|NTSC (RGB)" },
{ nullptr },
};
cb(RETRO_ENVIRONMENT_SET_VARIABLES, const_cast<retro_variable *>(vars));
@ -653,13 +679,14 @@ RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code)
RETRO_API bool retro_load_game(const retro_game_info *game)
{
// bsnes uses 0RGB1555 internally but it is deprecated
// let software conversion happen in frontend
/*retro_pixel_format fmt = RETRO_PIXEL_FORMAT_0RGB1555;
retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
return false;*/
return false;
emulator->configure("Audio/Frequency", SAMPLERATE);
program->filterRender = &Filter::None::render;
program->filterSize = &Filter::None::size;
program->updateVideoPalette();
flush_variables();

View File

@ -46,6 +46,7 @@ struct Program : Emulator::Platform
auto openRomBSMemory(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>;
auto hackPatchMemory(vector<uint8_t>& data) -> void;
auto updateVideoPalette() -> void;
string base_name;
@ -79,6 +80,13 @@ public:
struct BSMemory : Game {
vector<uint8_t> program;
} bsMemory;
uint32_t palette[32768];
uint32_t paletteDimmed[32768];
uint32_t videoOut[2304*2160];
Filter::Render filterRender;
Filter::Size filterSize;
};
static Program *program = nullptr;
@ -157,6 +165,59 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) ->
return result;
}
auto Program::updateVideoPalette() -> void {
double luminance = 100.0 / 100.0;
double saturation = 100.0 / 100.0;
double gamma = 150.0 / 100.0;
uint depth = 24;
for(uint color : range(32768)) {
uint16 r = (color >> 10) & 31;
uint16 g = (color >> 5) & 31;
uint16 b = (color >> 0) & 31;
r = r << 3 | r >> 2; r = r << 8 | r << 0;
g = g << 3 | g >> 2; g = g << 8 | g << 0;
b = b << 3 | b >> 2; b = b << 8 | b << 0;
if(saturation != 1.0) {
uint16 grayscale = uclamp<16>((r + g + b) / 3);
double inverse = max(0.0, 1.0 - saturation);
r = uclamp<16>(r * saturation + grayscale * inverse);
g = uclamp<16>(g * saturation + grayscale * inverse);
b = uclamp<16>(b * saturation + grayscale * inverse);
}
if(gamma != 1.0) {
double reciprocal = 1.0 / 32767.0;
r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, gamma));
g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, gamma));
b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, gamma));
}
if(luminance != 1.0) {
r = uclamp<16>(r * luminance);
g = uclamp<16>(g * luminance);
b = uclamp<16>(b * luminance);
}
switch(depth) {
case 24: palette[color] = r >> 8 << 16 | g >> 8 << 8 | b >> 8 << 0; break;
case 30: palette[color] = r >> 6 << 20 | g >> 6 << 10 | b >> 6 << 0; break;
}
r >>= 1;
g >>= 1;
b >>= 1;
switch(depth) {
case 24: paletteDimmed[color] = r >> 8 << 16 | g >> 8 << 8 | b >> 8 << 0; break;
case 30: paletteDimmed[color] = r >> 6 << 20 | g >> 6 << 10 | b >> 6 << 0; break;
}
}
}
auto Program::load() -> void {
emulator->unload();
emulator->load();
@ -227,12 +288,12 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
{
if (loadGameBoy(gameBoy.location))
{
return { id, NULL };
return { id, "" };
}
}
else if (id == 3) {
if (loadBSMemory(bsMemory.location)) {
return { id, NULL };
return { id, "" };
}
}
return { id, options(0) };
@ -245,7 +306,20 @@ auto Program::videoFrame(const uint16* data, uint pitch, uint width, uint height
data += 8 * (pitch >> 1) * multiplier;
height -= 16 * multiplier;
}
video_cb(data, width, height, pitch);
uint filterWidth = width, filterHeight = height;
filterSize(filterWidth, filterHeight);
// Scale the NTSC filter properly for HD Mode 7
if ((scale > 1) && (filterWidth == 602))
{
filterWidth = 301 * scale;
}
filterRender(palette, videoOut, filterWidth << 2, (const uint16_t*)data, pitch, width, height);
video_cb(videoOut, filterWidth, filterHeight, filterWidth << 2);
}
// Double the fun!