do some more cleanup with rcheevos stuff, also rework rcheevos read map. having a dict full of delegates ends up allocating >26GiB of memory for New 3DS (i.e. 256MiB of memory), so instead have a map of _memFunction indexes corresponding to address, thereby making memory usage equal to the memory mapping size (so for New 3DS this will only take 256MiB of memory). it comes with some limitations, none of which matter for now

This commit is contained in:
CasualPokePlayer 2023-12-11 03:59:10 -08:00
parent 6b0b6f2106
commit 3bcc6ee977
4 changed files with 81 additions and 59 deletions

View File

@ -15,16 +15,17 @@ namespace BizHawk.Client.EmuHawk
public partial class RAIntegration : RetroAchievements
{
private readonly RAInterface RA;
static RAIntegration()
{
if (OSTailoredCode.IsUnixHost)
{
// RAIntegration is Windows only
return;
}
try
{
if (OSTailoredCode.IsUnixHost)
{
throw new NotSupportedException("RAIntegration is Windows only!");
}
AttachDll();
}
catch
@ -56,25 +57,24 @@ namespace BizHawk.Client.EmuHawk
private void RebuildMenu()
{
var numItems = RA.GetPopupMenuItems(_menuItems);
var tsmiddi = _raDropDownItems;
tsmiddi.Clear();
_raDropDownItems.Clear();
{
var tsi = new ToolStripMenuItem("Shutdown RetroAchievements");
tsi.Click += (_, _) => _shutdownRACallback();
tsmiddi.Add(tsi);
_raDropDownItems.Add(tsi);
tsi = new ToolStripMenuItem("Autostart RetroAchievements")
tsi = new("Autostart RetroAchievements")
{
Checked = _getConfig().RAAutostart,
CheckOnClick = true,
};
tsi.CheckedChanged += (_, _) => _getConfig().RAAutostart ^= true;
tsmiddi.Add(tsi);
_raDropDownItems.Add(tsi);
var tss = new ToolStripSeparator();
tsmiddi.Add(tss);
_raDropDownItems.Add(tss);
}
for (int i = 0; i < numItems; i++)
for (var i = 0; i < numItems; i++)
{
if (_menuItems[i].Label != IntPtr.Zero)
{
@ -88,12 +88,12 @@ namespace BizHawk.Client.EmuHawk
RA.InvokeDialog(id);
_mainForm.UpdateWindowTitle();
};
tsmiddi.Add(tsi);
_raDropDownItems.Add(tsi);
}
else
{
var tss = new ToolStripSeparator();
tsmiddi.Add(tss);
_raDropDownItems.Add(tss);
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
@ -55,30 +56,37 @@ namespace BizHawk.Client.EmuHawk
return file.GetStream().Position;
}
private static UIntPtr ReadFileCallback(IntPtr file_handle, IntPtr buffer, UIntPtr requested_bytes)
private static nuint ReadFileCallback(IntPtr file_handle, IntPtr buffer, nuint requested_bytes)
{
var handle = GCHandle.FromIntPtr(file_handle);
var file = (HawkFile)handle.Target;
var stream = file.GetStream();
// this is poop without spans
const int TMP_BUFFER_LEN = 65536;
var tmp = new byte[TMP_BUFFER_LEN];
var stream = file.GetStream();
var remainingBytes = (ulong)requested_bytes;
while (remainingBytes != 0)
var tmp = ArrayPool<byte>.Shared.Rent(TMP_BUFFER_LEN);
try
{
var numRead = stream.Read(tmp, 0, (int)Math.Min(remainingBytes, TMP_BUFFER_LEN));
if (numRead == 0) // reached end of stream
var remainingBytes = requested_bytes;
while (remainingBytes != 0)
{
break;
var numRead = stream.Read(tmp, 0, (int)Math.Min(remainingBytes, TMP_BUFFER_LEN));
if (numRead == 0) // reached end of stream
{
break;
}
Marshal.Copy(tmp, 0, buffer, numRead);
buffer += numRead;
remainingBytes -= (uint)numRead;
}
Marshal.Copy(tmp, 0, buffer, numRead);
buffer += numRead;
remainingBytes -= (ulong)numRead;
return requested_bytes - remainingBytes;
}
finally
{
ArrayPool<byte>.Shared.Return(tmp);
}
return new((ulong)requested_bytes - remainingBytes);
}
private static void CloseFileCallback(IntPtr file_handle)
@ -189,7 +197,7 @@ namespace BizHawk.Client.EmuHawk
_buf2448 = new byte[2448];
}
public int ReadSector(int lba, IntPtr buffer, ulong requestedBytes)
public int ReadSector(int lba, IntPtr buffer, nuint requestedBytes)
{
if (lba < _track.LBA || lba >= _track.NextTrack.LBA)
{
@ -221,11 +229,11 @@ namespace BizHawk.Client.EmuHawk
return GCHandle.ToIntPtr(handle);
}
private static UIntPtr ReadSectorCallback(IntPtr track_handle, uint sector, IntPtr buffer, UIntPtr requested_bytes)
private static nuint ReadSectorCallback(IntPtr track_handle, uint sector, IntPtr buffer, nuint requested_bytes)
{
var handle = GCHandle.FromIntPtr(track_handle);
var track = (RCTrack)handle.Target;
return new((uint)track.ReadSector((int)sector, buffer, (ulong)requested_bytes));
return (uint)track.ReadSector((int)sector, buffer, requested_bytes);
}
private static void CloseTrackCallback(IntPtr track_handle)
@ -302,7 +310,7 @@ namespace BizHawk.Client.EmuHawk
{
static string ResolvePath(string path)
{
if (Disc.IsValidExtension(Path.GetExtension(path)))
if (path.IndexOf('|') == -1 && Disc.IsValidExtension(Path.GetExtension(path)))
{
return path; // nothing to do in this case
}
@ -334,7 +342,7 @@ namespace BizHawk.Client.EmuHawk
static ConsoleID IdentifyConsole(string path)
{
if (Disc.IsValidExtension(Path.GetExtension(path)))
if (path.IndexOf('|') == -1 && Disc.IsValidExtension(Path.GetExtension(path)))
{
using var disc = DiscExtensions.CreateAnyType(path, Console.WriteLine);
if (disc is null)

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
@ -42,8 +41,9 @@ namespace BizHawk.Client.EmuHawk
private readonly LibRCheevos.rc_runtime_event_handler_t _eventcb;
private readonly LibRCheevos.rc_runtime_peek_t _peekcb;
private readonly LibRCheevos.rc_runtime_validate_address_t _validatecb;
private readonly Dictionary<uint, (ReadMemoryFunc Func, uint Start)> _readMap = new();
private byte[] _readMap = Array.Empty<byte>();
private ToolStripMenuItem _hardcoreModeMenuItem;
private bool _hardcoreMode;
@ -231,6 +231,7 @@ namespace BizHawk.Client.EmuHawk
_eventcb = EventHandlerCallback;
_peekcb = PeekCallback;
_validatecb = ValidateCallback;
var config = _getConfig();
CheevosActive = config.RACheevosActive;
@ -327,6 +328,9 @@ namespace BizHawk.Client.EmuHawk
config.RAAllowUnofficialCheevos = AllowUnofficialCheevos;
}
private bool ValidateCallback(uint address)
=> address < _readMap.Length && _readMap[address] != 0xFF;
public override void Restart()
{
if (_firstRestart)
@ -363,24 +367,32 @@ namespace BizHawk.Client.EmuHawk
_consoleId = SystemIdToConsoleId();
// init the read map
_readMap.Clear();
_readMap = Array.Empty<byte>();
if (Emu.HasMemoryDomains())
{
_memFunctions = CreateMemoryBanks(_consoleId, Domains, Emu.CanDebug() ? Emu.AsDebuggable() : null);
if (_memFunctions.Count > 255)
{
throw new InvalidOperationException("_memFunctions must have less than 256 memory banks");
}
// this is kind of poop, it would prevent having >2GiB total banksize
// but no system needs that right now, the largest is just New 3DS at 256MiB
_readMap = new byte[_memFunctions.Sum(mfun => mfun.BankSize)];
uint addr = 0;
foreach (var memFunctions in _memFunctions)
for (var i = 0; i < _memFunctions.Count; i++)
{
if (memFunctions.ReadFunc is not null)
_memFunctions[i].StartAddress = addr;
var mapValue = _memFunctions[i].ReadFunc is not null ? i : 0xFF;
for (var j = 0; j < _memFunctions[i].BankSize; j++)
{
for (uint i = 0; i < memFunctions.BankSize; i++)
{
_readMap.Add(addr + i, (memFunctions.ReadFunc, addr));
}
_readMap[addr + j] = (byte)mapValue;
}
addr = checked(addr + memFunctions.BankSize);
addr = checked(addr + _memFunctions[i].BankSize);
}
}
@ -426,11 +438,7 @@ namespace BizHawk.Client.EmuHawk
}
// validate addresses now that we have cheevos init
// ReSharper disable once ConvertToLocalFunction
#pragma warning disable IDE0039
LibRCheevos.rc_runtime_validate_address_t peekcb = address => _readMap.ContainsKey(address);
#pragma warning restore IDE0039
_lib.rc_runtime_validate_addresses(_runtime, _eventcb, peekcb);
_lib.rc_runtime_validate_addresses(_runtime, _eventcb, _validatecb);
_gameInfoForm.Restart(_gameData.Title, _gameData.TotalCheevoPoints(HardcoreMode), CurrentRichPresence ?? "N/A");
_cheevoListForm.Restart(_gameData.GameID == 0 ? Array.Empty<Cheevo>() : _gameData.CheevoEnumerable, GetCheevoProgress);
@ -615,20 +623,25 @@ namespace BizHawk.Client.EmuHawk
}
}
private uint PeekCallback(uint address, uint num_bytes, IntPtr ud)
private uint Peek(uint address)
{
uint Peek(uint addr)
=> _readMap.TryGetValue(addr, out var reader) ? reader.Func(addr - reader.Start) : 0u;
return num_bytes switch
if (address < _readMap.Length && _readMap[address] != 0xFF)
{
1 => Peek(address),
2 => Peek(address) | (Peek(address + 1) << 8),
4 => Peek(address) | (Peek(address + 1) << 8) | (Peek(address + 2) << 16) | (Peek(address + 3) << 24),
_ => throw new InvalidOperationException($"Requested {num_bytes} in {nameof(PeekCallback)}"),
};
var memFuncs = _memFunctions[_readMap[address]];
return memFuncs.ReadFunc(address - memFuncs.StartAddress);
}
return 0;
}
private uint PeekCallback(uint address, uint num_bytes, IntPtr ud) => num_bytes switch
{
1 => Peek(address),
2 => Peek(address) | (Peek(address + 1) << 8),
4 => Peek(address) | (Peek(address + 1) << 8) | (Peek(address + 2) << 16) | (Peek(address + 3) << 24),
_ => throw new InvalidOperationException($"Requested {num_bytes} in {nameof(PeekCallback)}"),
};
public override void OnFrameAdvance()
{
if (!LoggedIn || !_activeModeUnlocksRequest.IsCompleted)

View File

@ -112,6 +112,7 @@ namespace BizHawk.Client.EmuHawk
public WriteMemoryFunc WriteFunc { get; protected init; }
public ReadMemoryBlockFunc ReadBlockFunc { get; protected init; }
public uint StartAddress; // this is set for our rcheevos impl
public readonly uint BankSize;
public RAMemGuard MemGuard { get; set; }