Skip to content

Commit

Permalink
libretro: Complete overhaul of video output code
Browse files Browse the repository at this point in the history
- Vertical overscan masking is now adjustable. 0, 8, 12, or 16 pixels
  may be masked from both the top and bottom of the video output.

- Aspect ratios are now calculated correctly, and the proper pixel
  aspect ratio will now be applied regardless of overscan settings.
  The 4:3 option will just use a 4:3 display aspect ratio regardless
  of overscan settings.

- Base sizes have been adjusted to reflect the ideal base sizes rather
  than the doubled sizes used internally. This makes the initial window
  size what one would reasonably expect instead of being doubled.

- Masking off the Super Game Boy border now works as it should. The
  aspect ratios chosen for general usage will apply to the masked SGB
  video output. The NTSC filter will now also work properly with this
  mode active. Additionally, this setting is now independent from the
  general overscan masking setting.
  • Loading branch information
carmiker committed Aug 4, 2024
1 parent d63ec98 commit e285c58
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 70 deletions.
84 changes: 51 additions & 33 deletions bsnes/target-libretro/libretro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,34 +57,42 @@ static bool ppu_fast_options = true;
#define RETRO_MEMORY_GB_SRAM ((2 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_BSX_SRAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM)

#define ASPECT_NTSC 8.0 / 7.0
#define ASPECT_PAL 7375000.0 / 5320342.5
#define VIDEO_WIDTH 256
#define VIDEO_HEIGHT 240

static double get_aspect_ratio()
{
double ratio;

if (aspect_ratio_mode == 0 && program->gameBoy.program && sgb_border_disabled == true && program->overscan == false)
ratio = 10.0/9.0;
else if (aspect_ratio_mode == 0 && program->superFamicom.region == "NTSC")
ratio = 1.306122;
else if (aspect_ratio_mode == 0 && program->superFamicom.region == "PAL")
ratio = 1.584216;
else if (aspect_ratio_mode == 1) // 8:7 or 10:9 depending on whenever the SGB border is shown
if (program->gameBoy.program && sgb_border_disabled == true && program->overscan == false)
ratio = 10.0/9.0;
else
ratio = 8.0/7.0;
else if (aspect_ratio_mode == 2) // 4:3
return 4.0/3.0;
else if (aspect_ratio_mode == 3) // NTSC
ratio = 1.306122;
else if (aspect_ratio_mode == 4) // PAL
ratio = 1.584216;
else
ratio = 8.0/7.0; // Default
switch (aspect_ratio_mode) {
default: case 0: { // Auto PAR
ratio = program->superFamicom.region == "PAL" ? ASPECT_PAL : ASPECT_NTSC;
break;
}
case 1: { // 1:1 PAR
ratio = 1.0;
break;
}
case 2: { // 4:3 DAR
return 4.0 / 3.0;
}
case 3: { // Force correct NTSC PAR
ratio = ASPECT_NTSC;
break;
}
case 4: { // Force correct PAL PAR
ratio = ASPECT_PAL;
break;
}
}

if (program->overscan)
return (ratio / 240) * 224;
else
return ratio;
if (sgb_border_disabled && program->gameBoy.program) {
return (160.0 * ratio) / 144.0;
}

return (VIDEO_WIDTH * ratio) / (double(VIDEO_HEIGHT) - (program->overscan << 1));
}

static bool update_option_visibility(void)
Expand Down Expand Up @@ -139,7 +147,7 @@ static void update_variables(void)

if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var))
{
if (strcmp(var.value, "8:7") == 0)
if (strcmp(var.value, "1:1") == 0)
aspect_ratio_mode = 1;
else if (strcmp(var.value, "4:3") == 0)
aspect_ratio_mode = 2;
Expand All @@ -151,15 +159,19 @@ static void update_variables(void)
aspect_ratio_mode = 0;
}

var.key = "bsnes_ppu_show_overscan";
var.key = "bsnes_ppu_overscan_v";
var.value = NULL;

if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var))
{
if (strcmp(var.value, "ON") == 0)
program->overscan = true;
else if (strcmp(var.value, "OFF") == 0)
program->overscan = false;
if (strcmp(var.value, "16") == 0)
program->overscan = 16;
else if (strcmp(var.value, "12") == 0)
program->overscan = 12;
else if (strcmp(var.value, "8") == 0)
program->overscan = 8;
else if (strcmp(var.value, "0") == 0)
program->overscan = 0;
}

var.key = "bsnes_blur_emulation";
Expand Down Expand Up @@ -738,11 +750,17 @@ void retro_get_system_info(retro_system_info *info)

void retro_get_system_av_info(struct retro_system_av_info *info)
{
info->geometry.base_width = 512; // accurate ppu
info->geometry.base_height = program->overscan ? 480 : 448; // accurate ppu
info->geometry.max_width = 2048; // 8x 256 for hd mode 7
info->geometry.max_height = 1920; // 8x 240
info->geometry.base_width = VIDEO_WIDTH;
info->geometry.base_height = VIDEO_HEIGHT;
info->geometry.max_width = VIDEO_WIDTH << 3; // 8x 256 for hd mode 7
info->geometry.max_height = VIDEO_HEIGHT << 3; // 8x 240

if (!(sgb_border_disabled && program->gameBoy.program)) {
info->geometry.base_height -= program->overscan * 2;
}

info->geometry.aspect_ratio = get_aspect_ratio();

info->timing.sample_rate = SAMPLERATE;

if (retro_get_region() == RETRO_REGION_NTSC) {
Expand Down
20 changes: 11 additions & 9 deletions bsnes/target-libretro/libretro_core_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,27 +100,29 @@ struct retro_core_option_v2_definition option_defs_us[] = {
"video",
{
{ "Auto", NULL },
{ "8:7", "Pixel Perfect" },
{ "4:3", NULL },
{ "1:1", "1:1 PAR (Pixel Perfect)" },
{ "4:3", "4:3 DAR" },
{ "NTSC", NULL },
{ "PAL", NULL },
{ NULL, NULL },
},
"Auto"
},
{
"bsnes_ppu_show_overscan",
"Crop Overscan",
"bsnes_ppu_overscan_v",
"Crop Vertical Overscan",
NULL,
"Remove the borders at the top and bottom of the screen, typically unused by games and hidden by the bezel of a standard-definition television.",
"Remove N lines from the top and bottom of the screen to simulate the bezel of a television of the SNES era.",
NULL,
"video",
{
{ "OFF", "8 Pixels" },
{ "ON", "disabled" },
{ "16", "16 Lines" },
{ "12", "12 Lines" },
{ "8", "8 Lines" },
{ "0", "0 Lines" },
{ NULL, NULL },
},
"OFF"
"8"
},
{
"bsnes_blur_emulation",
Expand Down Expand Up @@ -694,7 +696,7 @@ struct retro_core_option_v2_definition option_defs_us[] = {
"bsnes_hide_sgb_border",
"Hide SGB Border",
NULL,
"Hide the border when playing Super Game Boy games. Only works when 'Crop Overscan' is enabled.",
"Hide the border when playing Super Game Boy games.",
NULL,
"sgb",
{
Expand Down
45 changes: 18 additions & 27 deletions bsnes/target-libretro/program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,42 +336,33 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
}

auto Program::videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {
if (!overscan)
{
uint multiplier = height / 240;
if ((sgb_border_disabled) && (program->gameBoy.program))
{
data += 47 * (pitch >> 1) * multiplier;
}
else
{
data += 8 * (pitch >> 1) * multiplier;
}
if (program->gameBoy.program)
{
height -= 16.1 * multiplier;
}
else
{
height -= 16 * multiplier;
}
uint multiplier = height / 240;

if (sgb_border_disabled && program->gameBoy.program) {
// Clean up these magic numbers one day: GB width/height is 160/144.
// GB content starts 47 lines from the top and 48 colums from the left
data += (47 * (pitch >> 1) + 48) * multiplier;
width = 160 * multiplier;
height = 144 * multiplier;
}

if ((!overscan) & (sgb_border_disabled) && (program->gameBoy.program))
{
data += 48;
width -= 96;
height -= 79;
else if (overscan) {
data += overscan * (pitch >> 1) * multiplier;
height -= (overscan << 1) * multiplier;
}

uint filterWidth = width, filterHeight = height;

filterSize(filterWidth, filterHeight);

// Scale the NTSC filter properly for HD Mode 7
if ((scale > 1) && (filterWidth == 602))
if (filterWidth == 602)
{
filterWidth = 301 * scale;
if (scale > 1) {
filterWidth = 301 * scale;
}
else if (sgb_border_disabled && program->gameBoy.program) {
filterWidth = 378 * scale;
}
}

filterRender(palette, videoOut, filterWidth << 2, (const uint16_t*)data, pitch, width, height);
Expand Down
2 changes: 1 addition & 1 deletion bsnes/target-libretro/program.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct Program : Emulator::Platform

string base_name;

bool overscan{false};
int overscan{8};

public:
struct Game {
Expand Down

15 comments on commit e285c58

@joepogo
Copy link

@joepogo joepogo commented on e285c58 Aug 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this @carmiker! Does this mean we can use shaders with the accurate bsnes ppu option without the distortion?

Currently accurate ppu doubles the resolution.

@carmiker
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this @carmiker! Does this mean we can use shaders with the accurate bsnes ppu option without the distortion?

Currently accurate ppu doubles the resolution.

Not yet, but this paves the way. The internal rendering needs to be changed for the shader issue to be resolved, but I do know what the problem is and how to fix it -- the question is free time ;).

@joepogo
Copy link

@joepogo joepogo commented on e285c58 Aug 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, amazing man!! And thank you for all you've done thus far! 👍👊

Hopefully it'll get sorted, this is awesome news!! It's one of those little annoying quirks about bsnes.

I prefer accurate ppu but not distorted shaders lol.

You should merge most your changes into bsnes from jge. I love what you've done with the jge core. It's almost a complete overhaul. 😁

I added a request for bsnes/mainline retroarch to re-add support for powerfest 94 and campus challenge 1992. Near removed multi bin/track support after Higan v106. Would it be hard to re-add that back?

@carmiker
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bsnes-jg will eventually be ported to libretro, but first its internal API needs to be cleaned up to make the porting process clean.
The competition boards are complicated. They've been on my list for awhile, but it requires the right kind of motivation. One day, though.

@joepogo
Copy link

@joepogo joepogo commented on e285c58 Aug 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last oddity I meant to mention regarding the competition carts.

Powerfest 94 works perfectly in the bsnes-mercury core on retroarch, but campus challenge 92 does not. I've never been able to get it to work, just a black screen.

In theory if powerfest works correctly, I don't know why campus challenge doesn't. I wonder if it something related to the bml file?

Both use 4 bin files loaded in succession from a bml file. Both use the scoring system. I've noticed that the bml file differs greatly between both.

@carmiker
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the code from where both competition carts worked, just have to translate it to the modern codebase when I have the energy.

Staying on topic here, I have followed this commit up with two small commits that should fix the shader problem.

@joepogo
Copy link

@joepogo joepogo commented on e285c58 Aug 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @carmiker!

Thanks for all you do and for taking the time to correspond on these issues.

@joepogo
Copy link

@joepogo joepogo commented on e285c58 Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @carmiker , I didn't want to clog this up any further but I had a question I'd figured I'd ask you since you're fixing bsnes libretro video code.

Does the fast PPU on bsnes libretro have some issue with shaders and games like trials of Mana where the resolutions switch?

When using the crt-geom deluxe shader on fast PPU the screen goes from blurry to sharp when transitioning from text screens back to gameplay.

Funny thing is, when using this exact same shader on the bsnes libretro accurate PPU it works perfectly and also on SNES9x libretro with no issues at all.

Now that you fixed it where shaders work on the accurate PPU core I was able to test this out. 😁

Is the bsnes fast PPU core not accounting for vertical resolutions correctly or something like the accurate ppu is? In theory I figured it'd work on both PPU settings but it seems something is different.

I opened an issue on this but figured I'd also seek help on the issue.

Apparently other emulators have had this issue. Here is one example:

agg23/openfpga-SNES#11

https://retropie.org.uk/forum/topic/18381/secret-of-mana-2-very-strange-blurry-to-sharp-problem

@carmiker
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joepogo the "fast" PPU adjusts the resolution such that the shader needs to adapt in the way you're describing. This is actually correct behaviour given what the base resolution is. Remember, the real SNES was never meant to run at such resolutions and you are going to get strange behaviour when using such hacks.

For the case of Seiken Densetsu 3, this is because the dialog boxes use double horizontal resolution. The SNES can switch video modes mid-frame, this is an example of it doing that, and it is also why dialog boxes in that game take up the full screen width. This game is actually one of the test cases I used when working on the video code, for this very reason. This is also why it makes sense for bsnes to always render at double horizontal resolution -- so that it can mix scanlines that are 256 pixels wide with ones that are 512 pixels wide without trouble. In cases where they are 256 wide, you just draw every pixel twice. The frontend handles the rest.

@joepogo
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@carmiker I wasn't sure where to post this but wow!! Thanks for bringing bsnes jge to libretro!! I'm keeping my eye out for the core to become to available through the online updater but I don't see it just yet.

One question, is it possible to backport a few of your commits to the libretro bsnes as well like the two campus challenge and powerfest games?

I can't wait to give bsnes jge a go once it becomes available! Does your jge build have an accurate and performance ppu?

@carmiker
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joepogo the bsnes-jg libretro core should become available in the next day or two.

It is possible to bring better support for the competition carts to the non-jg bsnes core, all it requires is to update the BML databases and add some routines to handle multi-ROM .sfc files. The codebase has significantly changed though, so doing the file loading details in the nall-based bsnes might be challenging.

bsnes-jg only has the accurate PPU. This is by design, as I want my fork to focus on accuracy and maintainability. It offers the original SNES/SFC experience with the only enhancement being sinc interpolation for the SPC700 (which is mostly accurate to real hardware, though this is another topic). Think of it as a low fat version of bsnes -- I was nearly tempted to call it bsnes-lophat ;).

@joepogo
Copy link

@joepogo joepogo commented on e285c58 Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Thank you as always for your responses and time @carmiker !

It's awesome to see your lophat :p build, it sounds like exactly what I'm looking for. 👊 Thank you for bringing it to libretro!

Yea, it'd still be cool to see some of your backports to the mainline branch of libretro bsnes too. Hopefully it won't be too much to do so, especially the challenge carts.

It's also easy to merge these with whatever gets added to the actual mainline branch of just bsnes.

I hope they figure out the wolverine/controller polling issue as well.

@joepogo
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @carmiker , just making sure I'm not doing something wrong.

The jge core now shows up for you to download in the updater but trying to load the core fails and when I try to load any snes game there's no option to load it with jge.

I was gonna try to load the bml file for the two competition carts but there's no option to load it with jge either.

@carmiker
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joepogo you will need to update the .info file. It may come tomorrow, as that was merged this morning. Maybe it's already there if you try to force an update. Just use a concatenated ROM with bsnes-jg, it has no support for rom folders or loading .bml manifests.

@joepogo
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll keep trying to update it. Whenever I try to load a zipped snes file it only gives me the options to load it on the other snes cores.

I was trying to initially load the folders that I had the competition carts but I found the zipped versions.

Will bsnes jge ever have the ability to load msu1 games as well?

Sorry for all the questions, just learning how to work through all of this. Appreciate your patience and time. :)

Please sign in to comment.