TASEmulators/BizHawk

3DS Encore save state failed with Bizhawk API

Closed this issue · 8 comments

Summary

Exception when saving state via SaveState API with a c# external tool

Repro

  1. Calling EmuClient.SaveState while loading a 3DS game
  2. Bizhawk crash. Exception is logged in IDE
    public ApiContainer? _maybeAPIContainer { get; set; }

    private ApiContainer APIs
        => _maybeAPIContainer!;
        //...

	 APIs.EmuClient.Pause();
        APIs.EmuClient.SaveState(SAVE_PATH + @"\"+ SAVE_PREFIX + "-" + DateTime.Now.ToString("yyyyMMddHHmm"));
        APIs.EmuClient.SaveRam();
        APIs.EmuClient.Unpause();

Output

System.Exception: Failed to set context to current! SDL error: wglMakeCurrent(): La ressource demandée est en cours d’utilisation.
   à BizHawk.Bizware.Graphics.SDL2OpenGLContext.MakeContextCurrent() dans /src/BizHawk.Bizware.Graphics/OpenGL/SDL2OpenGLContext.cs:ligne 174
   à BizHawk.Client.EmuHawk.OpenGLProvider.ActivateGLContext(Object context) dans /src/BizHawk.Client.EmuHawk/GraphicsImplementations/OpenGLProvider.cs:ligne 23
   à BizHawk.Emulation.Cores.Consoles.Nintendo.N3DS.Encore.ActivateGLContextCallback(IntPtr context) dans /src/BizHawk.Emulation.Cores/Consoles/Nintendo/3DS/Encore.cs:ligne 190
   à Bizhawk.BizInvokeProxyLibEncore.Encore_StartSaveState(IntPtr )
   à BizHawk.Emulation.Cores.Consoles.Nintendo.N3DS.Encore.SaveStateBinary(BinaryWriter writer) dans /src/BizHawk.Emulation.Cores/Consoles/Nintendo/3DS/Encore.IStatable.cs:ligne 16
   à BizHawk.Client.Common.SavestateFile.<Create>b__6_4(BinaryWriter bw) dans /src/BizHawk.Client.Common/savestates/SavestateFile.cs:ligne 58
   à BizHawk.Client.Common.ZipStateSaver.<>c__DisplayClass6_0.<PutLump>b__0(Stream s) dans /src/BizHawk.Client.Common/savestates/ZipStateSaver.cs:ligne 46
   à BizHawk.Client.Common.FrameworkZipWriter.WriteItem(String name, Action`1 callback, Boolean zstdCompress) dans /src/BizHawk.Client.Common/FrameworkZipWriter.cs:ligne 40
   à BizHawk.Client.Common.ZipStateSaver.PutLump(BinaryStateLump lump, Action`1 callback, Boolean zstdCompress) dans /src/BizHawk.Client.Common/savestates/ZipStateSaver.cs:ligne 38
   à BizHawk.Client.Common.ZipStateSaver.PutLump(BinaryStateLump lump, Action`1 callback) dans /src/BizHawk.Client.Common/savestates/ZipStateSaver.cs:ligne 48
   à BizHawk.Client.Common.SavestateFile.Create(String filename, SaveStateConfig config) dans /src/BizHawk.Client.Common/savestates/SavestateFile.cs:ligne 60
   à BizHawk.Client.EmuHawk.MainForm.SaveState(String path, String userFriendlyStateName, Boolean fromLua, Boolean suppressOSD) dans /src/BizHawk.Client.EmuHawk/MainForm.cs:ligne 4327
   à BizHawk.Client.Common.EmuClientApi.SaveState(String name) dans /src/BizHawk.Client.Common/Api/Classes/EmuClientApi.cs:ligne 151
   à BizHawkTpp.SaveService.Save()

Host env.

  • BizHawk 2.9.2; Win10 Pro 1903; Intel/Nvidia (dev rev a803407)

Does this happen consistently?

Please copy the version from Help > About..., obviously you didn't mean to write 2.5.2.

Yup, always. Save states made using the UI works.

You're right, i'm on 2.9.2, last version from master branch (rev a803407)

Is this savestate possibly occurring in a separate thread? The error seems to indicate you are in a separate thread and it's when the main thread has the Encore OpenGL context active (and therefore other threads cannot make it active). You should not be calling SaveState outside of the main thread (this is more an OpenGL limitation if anything; even then savestates APIs (along with any other API touching emulator state) should not be considered safe outside of the main thread).

Hi,
Yep, i'm on a separate thread. I'm actually calling the SaveState method every 10 minutes to make an autosave. I did not encounter any issue with NDS or GBA emulation, but I assume that it does not use OpenGL

Just to be sure, there is no workaround ?

Doing savestates with other cores have very high risks of causing crashes (especially any waterbox cores like NDS due to non-thread safe code in its handling) and whatever savestates produced would very likely be garbage / not load correctly. Your code is fundamentally wrong here.

Can't we make the call on the correct thread regardless of where the API was called from?

The API user could do that with the WinForms BeginInvoke/Invoke API.

Good to know. Thanks a lot @CasualPokePlayer, @YoshiRulz !

If API methods are not threadsafe, I close the issue and will work with BeginInvoke and Invoke methods