From 0b432994dff6990efdfa9c5f71a902f8bcc5c9e8 Mon Sep 17 00:00:00 2001 From: nattthebear Date: Thu, 14 Jan 2021 19:17:40 -0500 Subject: [PATCH] Rework GB GPU memory areas API (#2566) * Rework GB GPU memory areas API All cores can easily implement it now with no copying or awkward garbage. Also fix the scanline callback and printer callback in Sameboy, which had been broken for some time. Fixes #2564 --- .../tools/GB/GBGPUView.cs | 79 +++++++---- src/BizHawk.Common/IsExternalInit.cs | 4 + .../Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs | 1 - .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 1 - .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 7 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 134 +++++++++++------- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 1 - .../Nintendo/Gameboy/Gambatte.IEmulator.cs | 5 - .../Consoles/Nintendo/Gameboy/Gambatte.cs | 34 +++-- .../Nintendo/Gameboy/IGameboyCommon.cs | 39 ++--- .../Consoles/Nintendo/Gameboy/Sameboy.cs | 91 +++++++++--- 11 files changed, 249 insertions(+), 147 deletions(-) create mode 100644 src/BizHawk.Common/IsExternalInit.cs diff --git a/src/BizHawk.Client.EmuHawk/tools/GB/GBGPUView.cs b/src/BizHawk.Client.EmuHawk/tools/GB/GBGPUView.cs index a0ed7769ab..ec76160c67 100644 --- a/src/BizHawk.Client.EmuHawk/tools/GB/GBGPUView.cs +++ b/src/BizHawk.Client.EmuHawk/tools/GB/GBGPUView.cs @@ -32,13 +32,24 @@ namespace BizHawk.Client.EmuHawk // g' = 8.25g // b' = 8.25b - - private GPUMemoryAreas _memory; - private bool _cgb; // set once at start private int _lcdc; // set at each callback - private IntPtr _tilesPal; // current palette to use on tiles + /// + /// Whether the tiles are being drawn with the sprite or bg palettes + /// + private bool _tilesPalIsSprite; + /// + /// How far (in bytes, I guess?) we should offset into the tiles palette + /// + private int _tilesPalOffset; + + private IntPtr ComputeTilesPalFromMemory(IGPUMemoryAreas m) + { + var ret = _tilesPalIsSprite ? m.Sppal : m.Bgpal; + ret += _tilesPalOffset; + return ret; + } private Color _spriteback; @@ -86,9 +97,6 @@ namespace BizHawk.Client.EmuHawk { _cgb = Gb.IsCGBMode(); _lcdc = 0; - _memory = Gb.GetGPU(); - - _tilesPal = _memory.Bgpal; label4.Enabled = _cgb; bmpViewBG.Clear(); @@ -420,17 +428,19 @@ namespace BizHawk.Client.EmuHawk private void ScanlineCallback(byte lcdc) { - using (_memory.EnterExit()) + using (var memory = Gb.LockGPU()) { - var bgPal = _memory.Bgpal; - var spPal = _memory.Sppal; - var oam = _memory.Oam; - var vram = _memory.Vram; + var bgPal = memory.Bgpal; + var spPal = memory.Sppal; + var oam = memory.Oam; + var vram = memory.Vram; + var tilesPal = ComputeTilesPalFromMemory(memory); _lcdc = lcdc; // set alpha on all pixels #if false - // TODO: RE: Spriteback, you can't muck with Sameboy in this way due to how SGB reads stuff...? + // TODO: This probably shouldn't be done on any cores at all. Let the tool make a separate copy of palettes if it needs alpha, + // or compel the cores to send data with alpha already set. What was this actually solving? unsafe { int* p = (int*)_bgpal; @@ -484,11 +494,11 @@ namespace BizHawk.Client.EmuHawk // tile display // TODO: user selects palette to use, instead of fixed palette 0 // or possibly "smart" where, if a tile is in use, it's drawn with one of the palettes actually being used with it? - DrawTiles(bmpViewTiles1.Bmp, vram, _tilesPal); + DrawTiles(bmpViewTiles1.Bmp, vram, tilesPal); bmpViewTiles1.Refresh(); if (_cgb) { - DrawTiles(bmpViewTiles2.Bmp, vram + 0x2000, _tilesPal); + DrawTiles(bmpViewTiles2.Bmp, vram + 0x2000, tilesPal); bmpViewTiles2.Refresh(); } @@ -666,10 +676,10 @@ namespace BizHawk.Client.EmuHawk private unsafe void PaletteMouseover(int x, int y, bool sprite) { - using (_memory.EnterExit()) + using (var memory = Gb.LockGPU()) { - var bgPal = _memory.Bgpal; - var spPal = _memory.Sppal; + var bgPal = memory.Bgpal; + var spPal = memory.Sppal; bmpViewDetails.ChangeBitmapSize(8, 10); if (bmpViewDetails.Height != 80) @@ -712,9 +722,10 @@ namespace BizHawk.Client.EmuHawk private unsafe void TileMouseover(int x, int y, bool secondBank) { - using (_memory.EnterExit()) + using (var memory = Gb.LockGPU()) { - var vram = _memory.Vram; + var vram = memory.Vram; + var tilesPal = ComputeTilesPalFromMemory(memory); // todo: draw with a specific palette bmpViewDetails.ChangeBitmapSize(8, 8); @@ -730,7 +741,7 @@ namespace BizHawk.Client.EmuHawk : $"Tile #{tileIndex} @{tileOffset + 0x8000:x4}"); var lockData = bmpViewDetails.Bmp.LockBits(new Rectangle(0, 0, 8, 8), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); - DrawTile((byte*)vram + tileOffset + (secondBank ? 8192 : 0), (int*)lockData.Scan0, lockData.Stride / sizeof(int), (int*)_tilesPal); + DrawTile((byte*)vram + tileOffset + (secondBank ? 8192 : 0), (int*)lockData.Scan0, lockData.Stride / sizeof(int), (int*)tilesPal); bmpViewDetails.Bmp.UnlockBits(lockData); labelDetails.Text = sb.ToString(); bmpViewDetails.Refresh(); @@ -739,10 +750,10 @@ namespace BizHawk.Client.EmuHawk private unsafe void TileMapMouseover(int x, int y, bool win) { - using (_memory.EnterExit()) + using (var memory = Gb.LockGPU()) { - var _bgpal = _memory.Bgpal; - var _vram = _memory.Vram; + var _bgpal = memory.Bgpal; + var _vram = memory.Vram; bmpViewDetails.ChangeBitmapSize(8, 8); if (bmpViewDetails.Height != 64) @@ -784,11 +795,11 @@ namespace BizHawk.Client.EmuHawk private unsafe void SpriteMouseover(int x, int y) { - using (_memory.EnterExit()) + using (var memory = Gb.LockGPU()) { - var spPal = _memory.Sppal; - var oam = _memory.Oam; - var vram = _memory.Vram; + var spPal = memory.Sppal; + var oam = memory.Oam; + var vram = memory.Vram; bool tall = _lcdc.Bit(2); x /= 8; @@ -974,13 +985,21 @@ namespace BizHawk.Client.EmuHawk private void bmpView_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) + { SetFreeze(); + } else if (e.Button == MouseButtons.Left) { if (sender == bmpViewBGPal) - _tilesPal = _memory.Bgpal + e.X / 16 * 16; + { + _tilesPalIsSprite = false; + _tilesPalOffset = e.X / 16 * 16; + } else if (sender == bmpViewSPPal) - _tilesPal = _memory.Sppal + e.X / 16 * 16; + { + _tilesPalIsSprite = true; + _tilesPalOffset = e.X / 16 * 16; + } } } diff --git a/src/BizHawk.Common/IsExternalInit.cs b/src/BizHawk.Common/IsExternalInit.cs new file mode 100644 index 0000000000..3618710c91 --- /dev/null +++ b/src/BizHawk.Common/IsExternalInit.cs @@ -0,0 +1,4 @@ +namespace System.Runtime.CompilerServices +{ + public static class IsExternalInit {} +} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs index 8d67c0e6b6..11d7467766 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_GB_PPU.cs @@ -528,7 +528,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (Core._scanlineCallback != null) { - Core.GetGPU(); Core._scanlineCallback(LCDC); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index f2a5cc993b..c029726e7c 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -529,7 +529,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (Core._scanlineCallback != null) { - Core.GetGPU(); Core._scanlineCallback(LCDC); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 45f60edd2f..b592789aa3 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -81,7 +81,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (_scanlineCallbackLine == -1) { - GetGPU(); + LockGPU(); _scanlineCallback(ppu.LCDC); } } @@ -425,11 +425,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void Dispose() { - Marshal.FreeHGlobal(iptr0); - Marshal.FreeHGlobal(iptr1); - Marshal.FreeHGlobal(iptr2); - Marshal.FreeHGlobal(iptr3); - audio.DisposeSound(); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index d9de376f47..2ba33a66b1 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -224,11 +224,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cpu.SetCallbacks(ReadMemory, PeekMemory, PeekMemory, WriteMemory); HardReset(); - iptr0 = Marshal.AllocHGlobal(VRAM.Length + 1); - iptr1 = Marshal.AllocHGlobal(OAM.Length + 1); - iptr2 = Marshal.AllocHGlobal(ppu.color_palette.Length * 8 * 8 + 1); - iptr3 = Marshal.AllocHGlobal(ppu.color_palette.Length * 8 * 8 + 1); - _scanlineCallback = null; DeterministicEmulation = true; @@ -236,54 +231,93 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool IsCGBMode() => is_GBC; - public IntPtr iptr0 = IntPtr.Zero; - public IntPtr iptr1 = IntPtr.Zero; - public IntPtr iptr2 = IntPtr.Zero; - public IntPtr iptr3 = IntPtr.Zero; - - private GPUMemoryAreas _gpuMemory + /// + /// Produces a palette in the form that certain frontend inspection tools. + /// May or may not return a reference to the core's own palette, so please don't mutate. + /// + private uint[] SynthesizeFrontendBGPal() { - get + if (is_GBC) { - Marshal.Copy(VRAM, 0, iptr0, VRAM.Length); - Marshal.Copy(OAM, 0, iptr1, OAM.Length); - - if (is_GBC) - { - int[] cp2 = new int[32]; - int[] cp = new int[32]; - for (int i = 0; i < 32; i++) - { - cp2[i] = (int)ppu.OBJ_palette[i]; - cp[i] = (int)ppu.BG_palette[i]; - } - - Marshal.Copy(cp2, 0, iptr2, ppu.OBJ_palette.Length); - Marshal.Copy(cp, 0, iptr3, ppu.BG_palette.Length); - } - else - { - int[] cp2 = new int[8]; - for (int i = 0; i < 4; i++) - { - cp2[i] = (int)ppu.color_palette[(ppu.obj_pal_0 >> (i * 2)) & 3]; - cp2[i + 4] = (int)ppu.color_palette[(ppu.obj_pal_1 >> (i * 2)) & 3]; - } - Marshal.Copy(cp2, 0, iptr2, cp2.Length); - - int[] cp = new int[4]; - for (int i = 0; i < 4; i++) - { - cp[i] = (int)ppu.color_palette[(ppu.BGP >> (i * 2)) & 3]; - } - Marshal.Copy(cp, 0, iptr3, cp.Length); - } - - return new GPUMemoryAreas(iptr0, iptr1, iptr2, iptr3); + return ppu.BG_palette; } - } + else + { + var scratch = new uint[4]; + for (int i = 0; i < 4; i++) + { + scratch[i] = ppu.color_palette[(ppu.BGP >> (i * 2)) & 3]; + } + return scratch; + } + } - public GPUMemoryAreas GetGPU() => _gpuMemory; + /// + /// Produces a palette in the form that certain frontend inspection tools. + /// May or may not return a reference to the core's own palette, so please don't mutate. + /// + private uint[] SynthesizeFrontendSPPal() + { + if (is_GBC) + { + return ppu.OBJ_palette; + } + else + { + var scratch = new uint[8]; + for (int i = 0; i < 4; i++) + { + scratch[i] = ppu.color_palette[(ppu.obj_pal_0 >> (i * 2)) & 3]; + scratch[i + 4] = ppu.color_palette[(ppu.obj_pal_1 >> (i * 2)) & 3]; + } + return scratch; + } + } + + public IGPUMemoryAreas LockGPU() + { + return new GPUMemoryAreas( + VRAM, + OAM, + SynthesizeFrontendSPPal(), + SynthesizeFrontendBGPal() + ); + } + + private class GPUMemoryAreas : IGPUMemoryAreas + { + public IntPtr Vram { get; } + + public IntPtr Oam { get; init; } + + public IntPtr Sppal { get; init; } + + public IntPtr Bgpal { get; init; } + + private readonly List _handles = new(); + + public GPUMemoryAreas(byte[] vram, byte[] oam, uint[] sppal, uint[] bgpal) + { + Vram = AddHandle(vram); + Oam = AddHandle(oam); + Sppal = AddHandle(sppal); + Bgpal = AddHandle(bgpal); + } + + private IntPtr AddHandle(object target) + { + var handle = GCHandle.Alloc(target, GCHandleType.Pinned); + _handles.Add(handle); + return handle.AddrOfPinnedObject(); + } + + public void Dispose() + { + foreach (var h in _handles) + h.Free(); + _handles.Clear(); + } + } public ScanlineCallback _scanlineCallback; public int _scanlineCallbackLine = 0; @@ -295,7 +329,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (line == -2) { - GetGPU(); + LockGPU(); _scanlineCallback(ppu.LCDC); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 07d3b470f6..13f415d464 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -151,7 +151,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (Core._scanlineCallback != null) { - Core.GetGPU(); Core._scanlineCallback(LCDC); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs index d96826f861..ab9c2bb405 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs @@ -99,11 +99,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy GambatteState = IntPtr.Zero; } - _vram = IntPtr.Zero; - _oam = IntPtr.Zero; - _sppal = IntPtr.Zero; - _bgpal = IntPtr.Zero; - DisposeSound(); } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index ed9eee806d..49747d8c74 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -382,13 +382,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy } } - public IntPtr _vram = IntPtr.Zero; - public IntPtr _bgpal = IntPtr.Zero; - public IntPtr _sppal = IntPtr.Zero; - public IntPtr _oam = IntPtr.Zero; - - public GPUMemoryAreas GetGPU() - { + public IGPUMemoryAreas LockGPU() + { + var _vram = IntPtr.Zero; + var _bgpal = IntPtr.Zero; + var _sppal = IntPtr.Zero; + var _oam = IntPtr.Zero; int unused = 0; if (!LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.vram, ref _vram, ref unused) || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.bgpal, ref _bgpal, ref unused) @@ -397,7 +396,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { throw new InvalidOperationException("Unexpected error in gambatte_getmemoryarea"); } - return new GPUMemoryAreas(_vram, _oam, _sppal, _bgpal); + return new GPUMemoryAreas + { + Vram = _vram, + Oam = _oam, + Sppal = _sppal, + Bgpal = _bgpal, + }; + } + + private class GPUMemoryAreas : IGPUMemoryAreas + { + public IntPtr Vram { get; init; } + + public IntPtr Oam { get; init; } + + public IntPtr Sppal { get; init; } + + public IntPtr Bgpal { get; init; } + + public void Dispose() {} } /// diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/IGameboyCommon.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/IGameboyCommon.cs index d12ba2b19a..762414be88 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/IGameboyCommon.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/IGameboyCommon.cs @@ -17,7 +17,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy public interface IGameboyCommon : ISpecializedEmulatorService { bool IsCGBMode(); - GPUMemoryAreas GetGPU(); + + /// + /// Acquire GPU memory for inspection. The returned object must be disposed as soon as the frontend + /// tool is done inspecting it, and the pointers become invalid once it is disposed. + /// + /// + IGPUMemoryAreas LockGPU(); /// /// set up callback @@ -32,32 +38,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy void SetPrinterCallback(PrinterCallback callback); } - public class GPUMemoryAreas : IMonitor + public interface IGPUMemoryAreas : IDisposable { - public IntPtr Vram { get; } - public IntPtr Oam { get; } - public IntPtr Sppal { get; } - public IntPtr Bgpal { get; } - - private readonly IMonitor _monitor; - - public GPUMemoryAreas(IntPtr vram, IntPtr oam, IntPtr sppal, IntPtr bgpal, IMonitor monitor = null) - { - Vram = vram; - Oam = oam; - Sppal = sppal; - Bgpal = bgpal; - _monitor = monitor; - } - - public void Enter() - { - _monitor?.Enter(); - } - - public void Exit() - { - _monitor?.Exit(); - } + IntPtr Vram { get; } + IntPtr Oam { get; } + IntPtr Sppal { get; } + IntPtr Bgpal { get; } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs index f730364af8..3e4307d904 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs @@ -35,6 +35,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy private readonly bool _cgb; private readonly bool _sgb; + private readonly IntPtr[] _cachedGpuPointers = new IntPtr[4]; + [CoreConstructor("SGB")] public Sameboy(byte[] rom, CoreComm comm, Settings settings, SyncSettings syncSettings, bool deterministic) : this(rom, comm, true, settings, syncSettings, deterministic) @@ -59,6 +61,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy SystemId = sgb ? "SGB" : "GB" }) { + _corePrinterCallback = PrinterCallbackRelay; + _coreScanlineCallback = ScanlineCallbackRelay; + _core = PreInit(new WaterboxOptions { Filename = "sameboy.wbx", @@ -69,7 +74,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy MmapHeapSizeKB = 1024, SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), - }); + }, new Delegate[] { _corePrinterCallback, _coreScanlineCallback }); _cgb = (rom[0x143] & 0xc0) == 0xc0 && !sgb; _sgb = sgb; @@ -96,11 +101,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy _exe.RemoveReadonlyFile("game.rom"); _exe.RemoveReadonlyFile("boot.rom"); - PostInit(); + _core.GetGpuMemory(_cachedGpuPointers); - var scratch = new IntPtr[4]; - _core.GetGpuMemory(scratch); - _gpuMemory = new GPUMemoryAreas(scratch[0], scratch[1], scratch[3], scratch[2], _exe); + PostInit(); DeterministicEmulation = deterministic || !_syncSettings.UseRealTime; InitializeRtc(_syncSettings.InitialTime); @@ -273,8 +276,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy protected override unsafe void FrameAdvancePost() { - if (_scanlineCallback != null && _scanlineCallbackLine == -1) - _scanlineCallback(_core.GetIoReg(0x40)); + if (_frontendScanlineCallback != null && _scanlineCallbackLine == -1) + _frontendScanlineCallback(_core.GetIoReg(0x40)); if (_sgb && !_settings.ShowSgbBorder) { @@ -299,27 +302,73 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy protected override void LoadStateBinaryInternal(BinaryReader reader) { UpdateCoreScanlineCallback(false); - _core.SetPrinterCallback(_printerCallback); + _core.SetPrinterCallback(_corePrinterCallback); } public bool IsCGBMode() => _cgb; - private readonly GPUMemoryAreas _gpuMemory; + public IGPUMemoryAreas LockGPU() + { + _exe.Enter(); + try + { + return new GPUMemoryAreas(_exe) + { + Vram = _cachedGpuPointers[0], + Oam = _cachedGpuPointers[1], + Sppal = _cachedGpuPointers[3], + Bgpal = _cachedGpuPointers[2] + }; + } + catch + { + _exe.Exit(); + throw; + } + } - public GPUMemoryAreas GetGPU() => _gpuMemory; - private ScanlineCallback _scanlineCallback; + private class GPUMemoryAreas : IGPUMemoryAreas + { + private IMonitor _monitor; + public IntPtr Vram { get; init; } + + public IntPtr Oam { get; init; } + + public IntPtr Sppal { get; init; } + + public IntPtr Bgpal { get; init; } + + public GPUMemoryAreas(IMonitor monitor) + { + _monitor = monitor; + } + + public void Dispose() + { + _monitor?.Exit(); + _monitor = null; + } + } + + private readonly ScanlineCallback _coreScanlineCallback; + private ScanlineCallback _frontendScanlineCallback; private int _scanlineCallbackLine; + private void ScanlineCallbackRelay(byte lcdc) + { + _frontendScanlineCallback?.Invoke(lcdc); + } + public void SetScanlineCallback(ScanlineCallback callback, int line) { - _scanlineCallback = callback; + _frontendScanlineCallback = callback; _scanlineCallbackLine = line; UpdateCoreScanlineCallback(true); } private void UpdateCoreScanlineCallback(bool now) { - if (_scanlineCallback == null) + if (_frontendScanlineCallback == null) { _core.SetScanlineCallback(null, -1); } @@ -327,25 +376,31 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy { if (_scanlineCallbackLine >= 0 && _scanlineCallbackLine <= 153) { - _core.SetScanlineCallback(_scanlineCallback, _scanlineCallbackLine); + _core.SetScanlineCallback(_coreScanlineCallback, _scanlineCallbackLine); } else { _core.SetScanlineCallback(null, -1); if (_scanlineCallbackLine == -2 && now) { - _scanlineCallback(_core.GetIoReg(0x40)); + _frontendScanlineCallback(_core.GetIoReg(0x40)); } } } } - private PrinterCallback _printerCallback; + private readonly PrinterCallback _corePrinterCallback; + private PrinterCallback _frontendPrinterCallback; + + private void PrinterCallbackRelay(IntPtr image, byte height, byte top_margin, byte bottom_margin, byte exposure) + { + _frontendPrinterCallback?.Invoke(image, height, top_margin, bottom_margin, exposure); + } public void SetPrinterCallback(PrinterCallback callback) { - _printerCallback = callback; - _core.SetPrinterCallback(callback); + _frontendPrinterCallback = callback; + _core.SetPrinterCallback(callback != null ? _corePrinterCallback : null); } } }