bsnes-plus icon indicating copy to clipboard operation
bsnes-plus copied to clipboard

Sync video on non-60Hz displays

Open Selicre opened this issue 2 years ago • 4 comments

My monitor is set to 120Hz. The emulator assumes that the refresh rate is set to 60Hz and tries to paint a frame every vsync, which makes the game run at >60fps. Audio sync works, but is very jittery.

I would check if sufficient time has elapsed since the last frame, and only then advance the emulation.

Selicre avatar Dec 13 '21 03:12 Selicre

Right now, the recommended (and default) setting is to have audio sync enabled and video sync disabled, which limits the emulation speed to something that doesn't depend on your monitor's refresh rate.

At some point, mainline bsnes redid the video/audio sync system to handle vsync in a better/more flexible way, but it needs to be backported at some point.

devinacker avatar Dec 13 '21 04:12 devinacker

Unfortunately, only audio sync jitters to the point where just playing the game is uncomfortable. I've managed to fix it by adding this timer to Interface::video_refresh:

  typedef std::chrono::duration<double> fsec;
  auto now = std::chrono::steady_clock::now();
  fsec frameTime = now - lastTimePoint;
  fsec target(0.016);
  while (frameTime < target) {
    // video.refresh() and everything else goes here
    now = std::chrono::steady_clock::now();
    frameTime = now - lastTimePoint;
  }

However, it has an odd bug where the emulator won't close properly if it's running. I'm not sure why that's the case, so I can't really PR that.

Selicre avatar Dec 13 '21 04:12 Selicre

That's also going to break the speedup key and emulation speed settings. A proper solution is going to require something better than just jamming a busy loop into GUI code like that.

devinacker avatar Dec 13 '21 04:12 devinacker

The solution that works for me is to figure out the screen's refresh rate and divide the core refresh rate by it as well as collect remainders to determine when to skip or run an extra frame. I do it something like this:

int frames_to_run = 0;
int collector = 0;
while (1) {
    frames_to_run = core_refresh / screen_refresh;
    collector += core_refresh % screen_refresh;

    if (collector >= core_refresh) {
        ++frames_to_run;
        collector -= core_refresh;
    }

    for (int i = 0; i < frames_to_to_run; ++i)
        execute_emulation_frame();
}

If you want to do fast forward you can just add to the number of frames you want to run. You will also need to resample audio, which should be happening anyway so that you can use vsync and still have smooth audio. bsnes, in my experience, lends itself well to libsamplerate.

carmiker avatar Dec 20 '21 23:12 carmiker