C# changes for melonDS OpenGL support

also add in a way to debug OpenGL stuff (only built when uncommenting a define)
also do various cleanup here
This commit is contained in:
CasualPokePlayer 2023-09-24 04:27:18 -07:00
parent f71f2fafc6
commit f63e50c91b
8 changed files with 235 additions and 88 deletions

View File

@ -1,5 +1,12 @@
// #define DEBUG_OPENGL
using System;
#if DEBUG_OPENGL
using System.Runtime.InteropServices;
using Silk.NET.OpenGL.Legacy;
#endif
using static SDL2.SDL;
namespace BizHawk.Bizware.Graphics
@ -42,6 +49,12 @@ namespace BizHawk.Bizware.Graphics
SDL_SetHint(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, "0");
}
#if DEBUG_OPENGL
private static readonly DebugProc _debugProc = DebugCallback;
private static void DebugCallback(GLEnum source, GLEnum type, int id, GLEnum severity, int length, IntPtr message, IntPtr userParam)
=> Console.WriteLine($"{source} {type} {severity}: {Marshal.PtrToStringAnsi(message, length)}");
#endif
private IntPtr _sdlWindow;
private IntPtr _glContext;
@ -64,6 +77,13 @@ namespace BizHawk.Bizware.Graphics
throw new($"Could not set GL Context Flags! SDL Error: {SDL_GetError()}");
}
#if DEBUG_OPENGL
if (SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_FLAGS, (int)SDL_GLcontext.SDL_GL_CONTEXT_DEBUG_FLAG) != 0)
{
throw new($"Could not set GL Debug Flag! SDL Error: {SDL_GetError()}");
}
#endif
var profile = coreProfile
? SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE
: SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
@ -78,6 +98,17 @@ namespace BizHawk.Bizware.Graphics
{
throw new($"Could not create GL Context! SDL Error: {SDL_GetError()}");
}
#if DEBUG_OPENGL
if (GetGLProcAddress("glDebugMessageCallback") != IntPtr.Zero)
{
using var gl = GL.GetApi(GetGLProcAddress);
unsafe
{
gl.DebugMessageCallback(_debugProc, null);
}
}
#endif
}
public SDL2OpenGLContext(IntPtr nativeWindowhandle, int majorVersion, int minorVersion, bool coreProfile, bool forwardCompatible)

View File

@ -58,6 +58,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public bool DSi;
public bool ClearNAND;
public bool LoadDSiWare;
public bool IsWinApi;
public NDS.NDSSyncSettings.ThreeDeeRendererType ThreeDeeRenderer;
public RenderSettings RenderSettings;
}
@ -168,21 +169,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
[UnmanagedFunctionPointer(CC)]
public delegate IntPtr GetGLProcAddressCallback(string proc);
[StructLayout(LayoutKind.Sequential)]
public struct GLCallbackInterface
{
public RequestGLContextCallback RequestGLContext;
public ReleaseGLContextCallback ReleaseGLContext;
public ActivateGLContextCallback ActivateGLContext;
public GetGLProcAddressCallback GetGLProcAddress;
public IntPtr[] AllCallbacksInArray(ICallingConventionAdapter adapter)
{
return new Delegate[] { RequestGLContext, ReleaseGLContext, ActivateGLContext, GetGLProcAddress }
.Select(adapter.GetFunctionPointerForDelegate).ToArray();
}
}
public enum LogLevel : int
{
Debug,
@ -197,10 +183,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
[BizImport(CC)]
public abstract IntPtr Init(
ref InitConfig loadData,
/* ref ConfigCallbackInterface */ IntPtr[] configCallbackInterface,
/* ref FileCallbackInterface */ IntPtr[] fileCallbackInterface,
// /* ref GLCallbackInterface */ IntPtr[] glCallbackInterface, // TODO
LogCallback logCallback);
IntPtr[] configCallbackInterface, /* ref ConfigCallbackInterface */
IntPtr[] fileCallbackInterface, /* ref FileCallbackInterface */
LogCallback logCallback,
GetGLProcAddressCallback getGLProcAddressCallback);
[BizImport(CC)]
public abstract void PutSaveRam(byte[] data, uint len);
@ -271,5 +257,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
[BizImport(CC)]
public abstract void GetNANDData(byte[] buf);
[BizImport(CC)]
public abstract int GetGLTexture();
[BizImport(CC)]
public abstract void ReadFrameBuffer(int[] buffer);
}
}

View File

@ -26,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public void SetCpuRegister(string register, int value)
{
if (register.Length != 7 && register.Length != 8)
if (register.Length is not (7 or 8))
{
throw new InvalidOperationException("Wrong String Length???");
}
@ -50,13 +50,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public long TotalExecutedCycles => CycleCount + _core.GetCallbackCycleOffset();
public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks;
public IMemoryCallbackSystem MemoryCallbacks => _memoryCallbacks;
private readonly MemoryCallbackSystem _memorycallbacks = new(new[] { "System Bus" });
private readonly MemoryCallbackSystem _memoryCallbacks = new(new[] { "System Bus" });
private LibMelonDS.MemoryCallback _readcb;
private LibMelonDS.MemoryCallback _writecb;
private LibMelonDS.MemoryCallback _execcb;
private LibMelonDS.MemoryCallback _readCallback;
private LibMelonDS.MemoryCallback _writeCallback;
private LibMelonDS.MemoryCallback _execCallback;
private void InitMemoryCallbacks()
{
@ -72,18 +72,18 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
};
}
_readcb = CreateCallback(MemoryCallbackFlags.AccessRead, () => MemoryCallbacks.HasReads);
_writecb = CreateCallback(MemoryCallbackFlags.AccessWrite, () => MemoryCallbacks.HasWrites);
_execcb = CreateCallback(MemoryCallbackFlags.AccessExecute, () => MemoryCallbacks.HasExecutes);
_readCallback = CreateCallback(MemoryCallbackFlags.AccessRead, () => MemoryCallbacks.HasReads);
_writeCallback = CreateCallback(MemoryCallbackFlags.AccessWrite, () => MemoryCallbacks.HasWrites);
_execCallback = CreateCallback(MemoryCallbackFlags.AccessExecute, () => MemoryCallbacks.HasExecutes);
_memorycallbacks.ActiveChanged += SetMemoryCallbacks;
_memoryCallbacks.ActiveChanged += SetMemoryCallbacks;
}
private void SetMemoryCallbacks()
{
_core.SetMemoryCallback(0, MemoryCallbacks.HasReads ? _readcb : null);
_core.SetMemoryCallback(1, MemoryCallbacks.HasWrites ? _writecb : null);
_core.SetMemoryCallback(2, MemoryCallbacks.HasExecutes ? _execcb : null);
_core.SetMemoryCallback(0, MemoryCallbacks.HasReads ? _readCallback : null);
_core.SetMemoryCallback(1, MemoryCallbacks.HasWrites ? _writeCallback : null);
_core.SetMemoryCallback(2, MemoryCallbacks.HasExecutes ? _execCallback : null);
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
public class MelonDSGLTextureProvider : IGLTextureProvider
{
private readonly IVideoProvider _vp;
private readonly LibMelonDS _core;
private readonly Action _activateGLContextCallback;
private readonly int[] _vbuf = new int[256 * 16 * 384 * 16];
internal bool VideoDirty;
internal MelonDSGLTextureProvider(IVideoProvider vp, LibMelonDS core, Action activateGLContextCallback)
{
_vp = vp;
_core = core;
_activateGLContextCallback = activateGLContextCallback;
}
public int GetGLTexture()
=> _core.GetGLTexture();
public int[] GetVideoBuffer()
{
if (VideoDirty)
{
_activateGLContextCallback();
_core.ReadFrameBuffer(_vbuf);
VideoDirty = false;
}
return _vbuf;
}
public int VirtualWidth => 256;
public int VirtualHeight => 384;
public int BufferWidth => _vp.BufferWidth;
public int BufferHeight => _vp.BufferHeight;
public int VsyncNumerator => _vp.VsyncNumerator;
public int VsyncDenominator => _vp.VsyncDenominator;
public int BackgroundColor => _vp.BackgroundColor;
}
}

View File

@ -12,19 +12,24 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
if (IsDSiWare)
{
_core.DSiWareSavsLength(DSiTitleId.Lower, out var publicSavSize, out var privateSavSize, out var bannerSavSize);
if (publicSavSize + privateSavSize + bannerSavSize == 0) return null;
if (publicSavSize + privateSavSize + bannerSavSize == 0)
{
return null;
}
_exe.AddTransientFile(Array.Empty<byte>(), "public.sav");
_exe.AddTransientFile(Array.Empty<byte>(), "private.sav");
_exe.AddTransientFile(Array.Empty<byte>(), "banner.sav");
_core.ExportDSiWareSavs(DSiTitleId.Lower);
var publicSav = _exe.RemoveTransientFile("public.sav");
var privateSav = _exe.RemoveTransientFile("private.sav");
var bannerSav = _exe.RemoveTransientFile("banner.sav");
if (publicSav.Length != publicSavSize || privateSav.Length != privateSavSize ||
bannerSav.Length != bannerSavSize)
if (publicSav.Length != publicSavSize || privateSav.Length != privateSavSize || bannerSav.Length != bannerSavSize)
{
throw new InvalidOperationException("Unexpected size difference in DSiWare sav files!");
}
var ret = new byte[publicSavSize + privateSavSize + bannerSavSize];
publicSav.AsSpan().CopyTo(ret.AsSpan().Slice(0, publicSavSize));
privateSav.AsSpan().CopyTo(ret.AsSpan().Slice(publicSavSize, privateSavSize));

View File

@ -83,7 +83,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
Marshal.WriteByte(buffer, numToCopy, 0);
}
private void GetArraySettingCallback(LibMelonDS.ConfigEntry configEntry, IntPtr buffer)
private static void GetArraySettingCallback(LibMelonDS.ConfigEntry configEntry, IntPtr buffer)
{
if (configEntry != LibMelonDS.ConfigEntry.Firm_MAC)
{
@ -195,18 +195,20 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
return ret;
}
public NDSSettings Clone() => MemberwiseClone() as NDSSettings;
public NDSSettings Clone()
=> (NDSSettings)MemberwiseClone();
public static bool NeedsScreenResize(NDSSettings x, NDSSettings y)
{
bool ret = false;
var ret = false;
ret |= x.ScreenLayout != y.ScreenLayout;
ret |= x.ScreenGap != y.ScreenGap;
ret |= x.ScreenRotation != y.ScreenRotation;
return ret;
}
public NDSSettings() => SettingsUtil.SetDefaultValues(this);
public NDSSettings()
=> SettingsUtil.SetDefaultValues(this);
}
private static readonly DateTime minDate = new(2000, 1, 1);
@ -217,20 +219,41 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public enum ThreeDeeRendererType : int
{
Software,
//OpenGL_Classic,
[Display(Name = "OpenGL Classic")]
OpenGL_Classic,
// [Display(Name = "OpenGL Compute")]
//OpenGL_Compute,
}
[DisplayName("3D Renderer")]
[Description("Renderer used for 3D. OpenGL Classic requires at least OpenGL 3.2, OpenGL Compute requires at least OpenGL 4.3. Forced to Software when recording a movie.")]
// [Description("Renderer used for 3D. OpenGL Classic requires at least OpenGL 3.2, OpenGL Compute requires at least OpenGL 4.3. Forced to Software when recording a movie.")]
[Description("Renderer used for 3D. OpenGL Classic requires at least OpenGL 3.2. Forced to Software when recording a movie.")]
[DefaultValue(ThreeDeeRendererType.Software)]
[TypeConverter(typeof(DescribableEnumConverter))]
public ThreeDeeRendererType ThreeDeeRenderer { get; set; }
[DisplayName("Threaded 3D Rendering")]
[Description("Offloads 3D rendering to a separate thread. Only used for the software 3D renderer.")]
[DisplayName("Threaded Software 3D Rendering")]
[Description("Offloads 3D rendering to a separate thread. Only used for the software renderer.")]
[DefaultValue(true)]
public bool ThreadedRendering { get; set; }
[JsonIgnore]
private int _glScaleFactor;
[DisplayName("OpenGL Scale Factor")]
[Description("Factor at which OpenGL upscales the final image. Not used for the software renderer.")]
[DefaultValue(1)]
public int GLScaleFactor
{
get => _glScaleFactor;
set => _glScaleFactor = Math.Max(1, Math.Min(16, value));
}
[DisplayName("OpenGL Better Polygons")]
[Description("Enhances polygon quality with OpenGL. Not used for the software renderer.")]
[DefaultValue(false)]
public bool GLBetterPolygons { get; set; }
[JsonIgnore]
private DateTime _initaltime;
@ -406,16 +429,21 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
set => _firmwaremessage = value.Substring(0, Math.Min(26, value.Length));
}
public NDSSyncSettings Clone() => MemberwiseClone() as NDSSyncSettings;
public NDSSyncSettings Clone()
=> (NDSSyncSettings)MemberwiseClone();
public static bool NeedsReboot(NDSSyncSettings x, NDSSyncSettings y) => !DeepEquality.DeepEquals(x, y);
public static bool NeedsReboot(NDSSyncSettings x, NDSSyncSettings y)
=> !DeepEquality.DeepEquals(x, y);
public NDSSyncSettings() => SettingsUtil.SetDefaultValues(this);
public NDSSyncSettings()
=> SettingsUtil.SetDefaultValues(this);
}
public NDSSettings GetSettings() => _settings.Clone();
public NDSSettings GetSettings()
=> _settings.Clone();
public NDSSyncSettings GetSyncSettings() => _syncSettings.Clone();
public NDSSyncSettings GetSyncSettings()
=> _syncSettings.Clone();
public PutSettingsDirtyBits PutSettings(NDSSettings o)
{
@ -433,28 +461,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
private static int SanitizeBirthdayDay(int day, NDSSyncSettings.Month fwMonth)
{
int maxdays;
switch (fwMonth)
var maxdays = fwMonth switch
{
case NDSSyncSettings.Month.February:
{
maxdays = 29;
break;
}
case NDSSyncSettings.Month.April:
case NDSSyncSettings.Month.June:
case NDSSyncSettings.Month.September:
case NDSSyncSettings.Month.November:
{
maxdays = 30;
break;
}
default:
{
maxdays = 31;
break;
}
}
NDSSyncSettings.Month.February => 29,
NDSSyncSettings.Month.April or NDSSyncSettings.Month.June or NDSSyncSettings.Month.September or NDSSyncSettings.Month.November => 30,
_ => 31
};
return Math.Max(1, Math.Min(day, maxdays));
}

View File

@ -8,10 +8,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public partial class NDS
{
private ITraceable Tracer { get; }
private readonly LibMelonDS.TraceCallback _tracecb;
private readonly LibMelonDS.TraceCallback _traceCallback;
private unsafe void MakeTrace(LibMelonDS.TraceMask type, uint opcode, IntPtr r, IntPtr disasm, uint cyclesOff)
{
// ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
var cpu = type switch
{
LibMelonDS.TraceMask.ARM7_THUMB => "ARM7 (Thumb)",

View File

@ -62,14 +62,22 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
private static void LogCallback(LibMelonDS.LogLevel level, string message)
=> Console.Write($"[{level}] {message}");
private readonly MelonDSGLTextureProvider _glTextureProvider;
private readonly IOpenGLProvider _openGLProvider;
private readonly object _glContext;
private readonly LibMelonDS.GetGLProcAddressCallback _getGLProcAddressCallback;
private IntPtr GetGLProcAddressCallback(string proc)
=> _openGLProvider.GetGLProcAddress(proc);
[CoreConstructor(VSystemID.Raw.NDS)]
public NDS(CoreLoadParameters<NDSSettings, NDSSyncSettings> lp)
: base(lp.Comm, new()
{
DefaultWidth = 256,
DefaultHeight = 384,
MaxWidth = 256,
MaxHeight = 384,
MaxWidth = 256 * 16,
MaxHeight = 384 * 16,
MaxSamples = 1024,
DefaultFpsNumerator = 33513982,
DefaultFpsDenominator = 560190,
@ -93,12 +101,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
throw new InvalidOperationException("Wrong number of ROMs!");
}
var gbacartpresent = roms.Count == 2;
InitMemoryCallbacks();
_tracecb = MakeTrace;
_threadstartcb = ThreadStartCallback;
_traceCallback = MakeTrace;
_threadStartCallback = ThreadStartCallback;
_configCallbackInterface.GetBoolean = GetBooleanSettingCallback;
_configCallbackInterface.GetInteger = GetIntegerSettingCallback;
@ -110,6 +116,35 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
_logCallback = LogCallback;
_openGLProvider = CoreComm.OpenGLProvider;
_getGLProcAddressCallback = GetGLProcAddressCallback;
if (lp.DeterministicEmulationRequested)
{
_activeSyncSettings.ThreeDeeRenderer = NDSSyncSettings.ThreeDeeRendererType.Software;
}
if (_activeSyncSettings.ThreeDeeRenderer != NDSSyncSettings.ThreeDeeRendererType.Software)
{
// ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
var (majorGlVersion, minorGlVersion) = _activeSyncSettings.ThreeDeeRenderer switch
{
NDSSyncSettings.ThreeDeeRendererType.OpenGL_Classic => (3, 2),
// NDSSyncSettings.ThreeDeeRendererType.OpenGL_Compute => (4, 3),
_ => throw new InvalidOperationException($"Invalid {nameof(NDSSyncSettings.ThreeDeeRenderer)}")
};
if (!_openGLProvider.SupportsGLVersion(majorGlVersion, minorGlVersion))
{
lp.Comm.Notify($"OpenGL {majorGlVersion}.{minorGlVersion} is not supported on this machine, falling back to software renderer", null);
_activeSyncSettings.ThreeDeeRenderer = NDSSyncSettings.ThreeDeeRendererType.Software;
}
else
{
_glContext = _openGLProvider.RequestGLContext(majorGlVersion, minorGlVersion, true, false);
}
}
_core = PreInit<LibMelonDS>(new()
{
Filename = "melonDS.wbx",
@ -122,11 +157,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
}, new Delegate[]
{
_readcb, _writecb, _execcb, _tracecb, _threadstartcb,
_readCallback, _writeCallback, _execCallback, _traceCallback, _threadStartCallback,
_configCallbackInterface.GetBoolean, _configCallbackInterface.GetInteger,
_configCallbackInterface.GetString, _configCallbackInterface.GetArray,
_fileCallbackInterface.GetLength, _fileCallbackInterface.GetData,
_logCallback
_logCallback, _getGLProcAddressCallback
});
_activeSyncSettings.UseRealBIOS |= IsDSi;
@ -164,22 +199,23 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
else
{
AddCoreFile("nds.rom", roms[0]);
if (gbacartpresent)
if (roms.Count == 2)
{
AddCoreFile("gba.rom", roms[1]);
}
}
LibMelonDS.InitConfig initConfig;
initConfig.SkipFW = _activeSyncSettings.SkipFirmware;
initConfig.HasGBACart = gbacartpresent;
initConfig.SkipFW = _activeSyncSettings.SkipFirmware && !IsDSi;
initConfig.HasGBACart = roms.Count == 2;
initConfig.DSi = IsDSi;
initConfig.ClearNAND = _activeSyncSettings.ClearNAND || lp.DeterministicEmulationRequested;
initConfig.LoadDSiWare = IsDSiWare;
initConfig.IsWinApi = !OSTailoredCode.IsUnixHost;
initConfig.ThreeDeeRenderer = _activeSyncSettings.ThreeDeeRenderer;
initConfig.RenderSettings.SoftThreaded = _activeSyncSettings.ThreadedRendering;
initConfig.RenderSettings.GLScaleFactor = 1; // TODO
initConfig.RenderSettings.GLBetterPolygons = false; // TODO
initConfig.RenderSettings.GLScaleFactor = _activeSyncSettings.GLScaleFactor;
initConfig.RenderSettings.GLBetterPolygons = _activeSyncSettings.GLBetterPolygons;
_activeSyncSettings.FirmwareOverride |= !_activeSyncSettings.UseRealBIOS || lp.DeterministicEmulationRequested;
@ -205,7 +241,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
ref initConfig,
_configCallbackInterface.AllCallbacksInArray(_adapter),
_fileCallbackInterface.AllCallbacksInArray(_adapter),
_logCallback);
_logCallback,
_getGLProcAddressCallback);
if (error != IntPtr.Zero)
{
using (_exe.EnterExit())
@ -233,7 +270,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
_frameThreadAction = CallingConventionAdapters
.GetWaterboxUnsafeUnwrapped()
.GetDelegateForFunctionPointer<Action>(_frameThreadPtr);
_core.SetThreadStartCallback(_threadstartcb);
_core.SetThreadStartCallback(_threadStartCallback);
}
_disassembler = new(_core);
@ -242,6 +279,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
const string TRACE_HEADER = "ARM9+ARM7: Opcode address, opcode, registers (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, SP, LR, PC, Cy, CpuMode)";
Tracer = new TraceBuffer(TRACE_HEADER);
_serviceProvider.Register(Tracer);
if (_glContext != null)
{
_glTextureProvider = new(this, _core, () => _openGLProvider.ActivateGLContext(_glContext));
_serviceProvider.Register<IVideoProvider>(_glTextureProvider);
}
}
private static (ulong Full, uint Upper, uint Lower) GetDSiTitleId(IReadOnlyList<byte> file)
@ -361,7 +404,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
{
_core.SetTraceCallback(Tracer.IsEnabled() ? _tracecb : null, _settings.GetTraceMask());
if (_glContext != null)
{
_openGLProvider.ActivateGLContext(_glContext);
}
_core.SetTraceCallback(Tracer.IsEnabled() ? _traceCallback : null, _settings.GetTraceMask());
return new LibMelonDS.FrameInfo
{
Time = GetRtcTime(!DeterministicEmulation),
@ -376,7 +425,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
private readonly IntPtr _frameThreadPtr;
private readonly Action _frameThreadAction;
private readonly LibMelonDS.ThreadStartCallback _threadstartcb;
private readonly LibMelonDS.ThreadStartCallback _threadStartCallback;
private readonly Thread _frameThread;
private readonly SemaphoreSlim _frameThreadStartEvent = new(0, 1);
@ -391,6 +440,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
_frameThread?.Join();
_frameThreadStartEvent.Dispose();
_frameThreadEndEvent.Dispose();
if (_glContext != null)
{
_openGLProvider.ReleaseGLContext(_glContext);
}
base.Dispose();
}
@ -425,12 +480,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
_frameThreadEndEvent.Wait();
_renderThreadRanThisFrame = false;
}
if (_glTextureProvider != null)
{
_glTextureProvider.VideoDirty = true;
}
}
protected override void LoadStateBinaryInternal(BinaryReader reader)
{
SetMemoryCallbacks();
_core.SetThreadStartCallback(_threadstartcb);
_core.SetThreadStartCallback(_threadStartCallback);
if (_frameThreadPtr != _core.GetFrameThreadProc())
{
throw new InvalidOperationException("_frameThreadPtr mismatch");