Skip to content

Commit

Permalink
Debugger: Tile editor - Improved behavior when editing tiles from a t…
Browse files Browse the repository at this point in the history
…ilemap in games that bankswitch vram in the middle of the screen

-Also allows cropping the edit window when the size doesn't fit (e.g using 4x4 near the bottom of the screen can result in a 4x2 edit window, etc.)
-Fixed a display issue when the number of edited columns & rows didn't match
  • Loading branch information
SourMesen committed Dec 14, 2024
1 parent e2ea843 commit 86cac32
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 12 deletions.
60 changes: 59 additions & 1 deletion UI/Debugger/Utilities/ToolRefreshHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace Mesen.Debugger.Utilities
{
Expand Down Expand Up @@ -39,13 +40,70 @@ public LastRefreshInfo(Window host)
private static ConcurrentDictionary<Window, ToolInfo> _activeWindows = new();
private static int _nextId = 0;

public static void ExecuteAt(int scanline, int cycle, CpuType cpuType, Action callback)
{
//Execute callback at the specified scanline/cycle
//If the callback is not called by the core within 300ms
//call it from the UI as a fallback
object lockObject = new();
bool done = false;

int viewerId = ToolRefreshHelper.GetNextId();
NotificationListener? listener = new();
listener.OnNotification += (NotificationEventArgs e) => {
switch(e.NotificationType) {
case ConsoleNotificationType.ViewerRefresh:
if(e.Parameter.ToInt32() == viewerId) {
lock(callback) {
if(!done) {
Task.Run(() => {
//DebugApi.RemoveViewerId() must not be called inside the notification callback (which is
//the same thread as the emulation thread). Otherwise, the viewer timing collection the
//debugger is iterating on will be modified, causing a crash.
DebugApi.RemoveViewerId(viewerId, cpuType);
});

listener.Dispose();
listener = null;
callback();
done = true;
}
}
}
break;
}
};

DebugApi.SetViewerUpdateTiming(viewerId, scanline, cycle, cpuType);

Task.Run(async () => {
await Task.Delay(300);
if(listener != null) {
lock(callback) {
if(!done) {
//Give up after 300ms and call the callback
DebugApi.RemoveViewerId(viewerId, cpuType);
callback();
listener.Dispose();
done = true;
}
}
}
});
}

private static int GetNextId()
{
return Interlocked.Increment(ref _nextId);
}

private static int RegisterWindow(Window wnd, RefreshTimingConfig cfg, CpuType cpuType)
{
if(_activeWindows.ContainsKey(wnd)) {
throw new Exception("Register window called twice");
}

int newId = Interlocked.Increment(ref _nextId);
int newId = GetNextId();
wnd.Closing += OnWindowClosingHandler;
wnd.Closed += OnWindowClosedHandler;

Expand Down
6 changes: 5 additions & 1 deletion UI/Debugger/ViewModels/SpriteViewerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,11 @@ private ContextMenuAction GetEditTileAction(Window wnd)
sprite.RealWidth / size.Width,
sprite.Format,
sprite.Palette + paletteOffset,
wnd);
wnd,
CpuType,
RefreshTiming.Config.RefreshScanline,
RefreshTiming.Config.RefreshCycle
);
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion UI/Debugger/ViewModels/TileEditorViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ private unsafe void RefreshViewer()
for(int y = 0; y < _rowCount; y++) {
for(int x = 0; x < _columnCount; x++) {
fixed(UInt32* ptr = _tileBuffer) {
AddressInfo addr = _tileAddresses[y * _rowCount + x];
AddressInfo addr = _tileAddresses[y * _columnCount + x];
byte[] sourceData = DebugApi.GetMemoryValues(addr.Type, (uint)addr.Address, (uint)(addr.Address + bytesPerTile - 1));
DebugApi.GetTileView(_cpuType, GetOptions(x, y), sourceData, sourceData.Length, PaletteColors, (IntPtr)ptr);
UInt32* viewer = (UInt32*)framebuffer.FrameBuffer.Address;
Expand Down
11 changes: 10 additions & 1 deletion UI/Debugger/ViewModels/TileViewerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,16 @@ private void EditTileGrid(int columnCount, int rowCount, Window wnd)
addresses.Add(new AddressInfo() { Address = GetTileAddress(new PixelPoint(p.X + col*GridSizeX, p.Y + row*GridSizeY)), Type = Config.Source });
}
}
TileEditorWindow.OpenAtTile(addresses, columnCount, Config.Format, SelectedPalette, wnd);
TileEditorWindow.OpenAtTile(
addresses,
columnCount,
Config.Format,
SelectedPalette,
wnd,
CpuType,
RefreshTiming.Config.RefreshScanline,
RefreshTiming.Config.RefreshCycle
);
}

private void DrawNesChrPageDelimiters()
Expand Down
24 changes: 22 additions & 2 deletions UI/Debugger/ViewModels/TilemapViewerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,13 @@ private void EditTileGrid(int columnCount, int rowCount, Window wnd)
for(int col = 0; col < columnCount; col++) {
DebugTilemapTileInfo? tile = DebugApi.GetTilemapTileInfo((uint)(p.X + GridSizeX*col), (uint)(p.Y + GridSizeY*row), CpuType, GetOptions(SelectedTab), _data.Vram, _data.PpuState, _data.PpuToolsState);
if(tile == null) {
return;
if(col == 0) {
rowCount = row;
break;
} else {
columnCount = col;
continue;
}
}

if(palette == -1) {
Expand All @@ -714,8 +720,22 @@ private void EditTileGrid(int columnCount, int rowCount, Window wnd)
addresses.Add(new AddressInfo() { Address = tile.Value.TileAddress, Type = memType });
}
}

if(rowCount <= 0 || columnCount <= 0) {
return;
}

palette = Math.Max(0, palette);
TileEditorWindow.OpenAtTile(addresses, columnCount, _data.TilemapInfo.Format, palette, wnd);
TileEditorWindow.OpenAtTile(
addresses,
columnCount,
_data.TilemapInfo.Format,
palette,
wnd,
CpuType,
RefreshTiming.Config.RefreshScanline,
RefreshTiming.Config.RefreshCycle
);
}

private void DrawMode7Overlay()
Expand Down
24 changes: 18 additions & 6 deletions UI/Debugger/Windows/TileEditorWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,22 @@ private void InitializeComponent()
AvaloniaXamlLoader.Load(this);
}

public static void OpenAtTile(AddressInfo tileAddr, TileFormat tileFormat, int selectedPalette, Window parent)
public static void OpenAtTile(List<AddressInfo> tileAddresses, int columnCount, TileFormat tileFormat, int selectedPalette, Window parent, CpuType cpuType, int scanline, int cycle)
{
OpenAtTile(new List<AddressInfo>() { tileAddr }, 1, tileFormat, selectedPalette, parent);
if(EmuApi.IsPaused()) {
//If paused, use the current state - this might mismatch if viewer doesn't have "refresh on pause" enabled
InternalOpenAtTile(tileAddresses, columnCount, tileFormat, selectedPalette, parent);
} else {
//While running, delay the tile viewer's opening until we can grab the memory mappings
//at the same time as the viewer was refreshed the last time
//This allows mid-screen PPU bank switching to open up properly/reliably in the tile editor
ToolRefreshHelper.ExecuteAt(scanline, cycle, cpuType, () => {
InternalOpenAtTile(tileAddresses, columnCount, tileFormat, selectedPalette, parent);
});
}
}

public static void OpenAtTile(List<AddressInfo> tileAddresses, int columnCount, TileFormat tileFormat, int selectedPalette, Window parent)
private static void InternalOpenAtTile(List<AddressInfo> tileAddresses, int columnCount, TileFormat tileFormat, int selectedPalette, Window parent)
{
for(int i = 0; i < tileAddresses.Count; i++) {
AddressInfo addr = tileAddresses[i];
Expand All @@ -138,9 +148,11 @@ public static void OpenAtTile(List<AddressInfo> tileAddresses, int columnCount,
return;
}

TileEditorViewModel model = new(tileAddresses, columnCount, tileFormat, selectedPalette);
TileEditorWindow wnd = DebugWindowManager.CreateDebugWindow<TileEditorWindow>(() => new TileEditorWindow(model));
wnd.ShowCentered((Control)parent);
Dispatcher.UIThread.Post(() => {
TileEditorViewModel model = new(tileAddresses, columnCount, tileFormat, selectedPalette);
TileEditorWindow wnd = DebugWindowManager.CreateDebugWindow<TileEditorWindow>(() => new TileEditorWindow(model));
wnd.ShowCentered((Control)parent);
});
}

public void ProcessNotification(NotificationEventArgs e)
Expand Down

0 comments on commit 86cac32

Please sign in to comment.