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
This commit is contained in:
parent
e71506ac6a
commit
0b432994df
|
@ -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
|
||||
/// <summary>
|
||||
/// Whether the tiles are being drawn with the sprite or bg palettes
|
||||
/// </summary>
|
||||
private bool _tilesPalIsSprite;
|
||||
/// <summary>
|
||||
/// How far (in bytes, I guess?) we should offset into the tiles palette
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
public static class IsExternalInit {}
|
||||
}
|
|
@ -528,7 +528,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
|
|||
{
|
||||
if (Core._scanlineCallback != null)
|
||||
{
|
||||
Core.GetGPU();
|
||||
Core._scanlineCallback(LCDC);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -529,7 +529,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
|
|||
{
|
||||
if (Core._scanlineCallback != null)
|
||||
{
|
||||
Core.GetGPU();
|
||||
Core._scanlineCallback(LCDC);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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<GCHandle> _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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,7 +151,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
|
|||
{
|
||||
if (Core._scanlineCallback != null)
|
||||
{
|
||||
Core.GetGPU();
|
||||
Core._scanlineCallback(LCDC);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -17,7 +17,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
public interface IGameboyCommon : ISpecializedEmulatorService
|
||||
{
|
||||
bool IsCGBMode();
|
||||
GPUMemoryAreas GetGPU();
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IGPUMemoryAreas LockGPU();
|
||||
|
||||
/// <summary>
|
||||
/// 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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<LibSameboy>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue