libretro / dosbox-svn

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Refresh rate changes will hang external MIDI devices

realnc opened this issue · comments

I'm not sure if this fixable at all, because a refresh rate change needs to change environment with the RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO flag, which is very expensive.

The issue with this is that games that utilize two resolutions and switch between them will hang external MIDI modules like the MT-32 or Roland Sound Canvas (either real devices or emulated ones like Munt or SCVA.) This also happens when exiting the game, in which case you'll get indefinitely hanging notes.

This should be fixed in RetroArch and not in the core.
The SET_AV_INFO switching behavior is controlled by that.

Ideally MIDI shouldn't need to be reinitialized at all for this but I think RA just reinits everything.

On a sidenote, I plan to do a ECE style core (may call it differently since it wouldn't be identical to ECE) and that would include fluydsynth which would alleviate the problem somewhat.

What are the implications of not doing a RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO? I changed the code as follows, and I still get pixel-perfect sync but without the annoying video reinit:

diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp
index c920cd0a..9996c789 100644
--- a/libretro/libretro.cpp
+++ b/libretro/libretro.cpp
@@ -1350,7 +1350,7 @@ void retro_run (void)
 
     /* Dynamic resolution switching */
     if (RDOSGFXwidth != currentWidth || RDOSGFXheight != currentHeight ||
-        (fabs(currentFPS - render.src.fps) > 0.05f && render.src.fps != 0 && variable_refresh))
+        (variable_refresh && render.src.fps != 0 && currentFPS != render.src.fps))
     {
         struct retro_system_av_info new_av_info;
         retro_get_system_av_info(&new_av_info);
@@ -1358,27 +1358,22 @@ void retro_run (void)
         new_av_info.geometry.base_width = RDOSGFXwidth;
         new_av_info.geometry.base_height = RDOSGFXheight;
 
-        if (fabs(currentFPS - render.src.fps) > 0.05f && variable_refresh)
+        if (variable_refresh)
         {
             new_av_info.timing.fps = render.src.fps;
             new_av_info.timing.sample_rate = (double)MIXER_RETRO_GetFrequency();
-
-            environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO , &new_av_info);
             if (log_cb)
                 log_cb(RETRO_LOG_INFO,"[dosbox] refresh rate changed %f => %f\n", currentFPS, render.src.fps);
-
             currentFPS = render.src.fps;
         }
-        else
-        {
-            environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &new_av_info);
-            if (log_cb)
-                log_cb(RETRO_LOG_INFO,"[dosbox] resolution changed %dx%d => %dx%d\n",
-                    currentWidth, currentHeight, RDOSGFXwidth, RDOSGFXheight);
 
-            currentWidth = RDOSGFXwidth;
-            currentHeight = RDOSGFXheight;
-        }
+        environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &new_av_info);
+        if (log_cb)
+            log_cb(RETRO_LOG_INFO,"[dosbox] resolution changed %dx%d => %dx%d\n",
+                   currentWidth, currentHeight, RDOSGFXwidth, RDOSGFXheight);
+
+        currentWidth = RDOSGFXwidth;
+        currentHeight = RDOSGFXheight;
     }
 
     bool updated = false;

It shouldn't work, GEOMETRY as the name implies changes... GEOMETRY if it works it's probably a RA bug, I'm just following the SPEC.

#define RETRO_ENVIRONMENT_SET_GEOMETRY 37
                                           /* const struct retro_game_geometry * --
                                            * This environment call is similar to SET_SYSTEM_AV_INFO for changing
                                            * video parameters, but provides a guarantee that drivers will not be
                                            * reinitialized.
                                            * This can only be called from within retro_run().
                                            *
                                            * The purpose of this call is to allow a core to alter nominal
                                            * width/heights as well as aspect ratios on-the-fly, which can be
                                            * useful for some emulators to change in run-time.
                                            *
                                            * max_width/max_height arguments are ignored and cannot be changed
                                            * with this call as this could potentially require a reinitialization or a
                                            * non-constant time operation.
                                            * If max_width/max_height are to be changed, SET_SYSTEM_AV_INFO is required.
                                            *
                                            * A frontend must guarantee that this environment call completes in
                                            * constant time.
                                            */

Yes, it seems like a bug. I had disabled the "sync to exact content framerate" option in RA by accident, and (weirdly) this results in perfect sync with the above patch. Enabling that option breaks sync and timing.

It's a pretty awesome bug though, I must say. I'll be using it personally since it fixes the problem completely 😁

@twinaphex any words on this.
It actually works. You can change refresh rate without re initializing everything, so what's the point of AV_INFO other than changing the resampler rate?

Bug, or should it grant a change in the spec?

On a second thought... it shouldn't change the spec... a bug in one frontend shouldn't mean the spec should be changed. Also it may work on one driver or platform and not in another.

Bug should be fixed but.. if it works on all drivers maybe the SET_AV_INFO env should be reimplemented in RA to not reinit unless really neccesary. I figure the only really necessary use cases would be to increase MAX_WIDTH/HEIGHT and/or change resampler rate.

It may only work because DOSBox is in fact running decoupled from the libretro run loop, so I figure a test core that can change refresh rate on the fly should be written to test this.

@realnc does the FPS counter reflect the change with your patch? can't test right now.
IE: is it working at 70 or whatever it switches to?

Yes, the FPS counter shows the correct value when the game switches to a different refresh.

MIDI generally hangs on AV_INFO changes, or on exit, that's a RA issue, I feel like the underlying issue here is solved

I would like to keep this bug open though, and close it when/if RA or libretro change or add an API to do an AV_INFO change without driver re-init. For as long as this doesn't happen, it's going to be an issue for this core. It's not just MIDI. It's also the screen flashing and digital sound is also freezing for the duration of the switch.

This was merged:

libretro/RetroArch#9715

So we can finally close this one!