Debugger: Tilemap viewer - Mode 7 overlay

This commit is contained in:
Sour 2022-08-06 11:29:50 -04:00
parent d1e11a1fde
commit 7df142e420
13 changed files with 165 additions and 2 deletions

View file

@ -188,6 +188,8 @@ protected:
public:
PpuTools(Debugger* debugger, Emulator *emu);
virtual void GetPpuToolsState(BaseState& state) {};
virtual DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) = 0;
void GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, const uint32_t* palette, uint32_t *outBuffer);

View file

@ -12,6 +12,21 @@ static constexpr uint8_t layerBpp[8][4] = {
SnesPpuTools::SnesPpuTools(Debugger* debugger, Emulator *emu) : PpuTools(debugger, emu)
{
_state = {};
}
void SnesPpuTools::GetPpuToolsState(BaseState& state)
{
(SnesPpuToolsState&)state = _state;
}
void SnesPpuTools::SetPpuScanlineState(uint16_t scanline, uint8_t mode, int32_t mode7startX, int32_t mode7startY, int32_t mode7endX, int32_t mode7endY)
{
_state.ScanlineBgMode[scanline] = mode;
_state.Mode7StartX[scanline] = mode7startX;
_state.Mode7StartY[scanline] = mode7startY;
_state.Mode7EndX[scanline] = mode7endX;
_state.Mode7EndY[scanline] = mode7endY;
}
DebugTilemapInfo SnesPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer)

View file

@ -6,14 +6,28 @@ class Debugger;
class Emulator;
struct BaseState;
struct SnesPpuToolsState
{
uint8_t ScanlineBgMode[239];
int32_t Mode7StartX[239];
int32_t Mode7StartY[239];
int32_t Mode7EndX[239];
int32_t Mode7EndY[239];
};
class SnesPpuTools final : public PpuTools
{
private:
SnesPpuToolsState _state;
void GetSpriteInfo(DebugSpriteInfo& sprite, uint16_t spriteIndex, GetSpritePreviewOptions& options, SnesPpuState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette);
public:
SnesPpuTools(Debugger* debugger, Emulator *emu);
void GetPpuToolsState(BaseState& state) override;
void SetPpuScanlineState(uint16_t scanline, uint8_t mode, int32_t mode7startX, int32_t mode7startY, int32_t mode7endX, int32_t mode7endY);
DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) override;
FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override;
DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override;

View file

@ -7,6 +7,8 @@
#include "SNES/InternalRegisters.h"
#include "SNES/SnesControlManager.h"
#include "SNES/SnesDmaController.h"
#include "SNES/Debugger/SnesPpuTools.h"
#include "Debugger/Debugger.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/Video/VideoDecoder.h"
@ -19,6 +21,7 @@
#include "Utilities/HexUtilities.h"
#include "Utilities/Serializer.h"
//TODO remove
#ifdef _MSC_VER
#pragma warning ( disable : 4127 ) //conditional expression is constant
#endif
@ -443,6 +446,10 @@ bool SnesPpu::ProcessEndOfScanline(uint16_t& hClock)
memset(_mainScreenFlags, 0, sizeof(_mainScreenFlags));
memset(_subScreenPriority, 0, sizeof(_subScreenPriority));
if(!_skipRender && _emu->IsDebugging()) {
DebugProcessMode7Overlay();
}
}
_scanline++;
@ -1141,6 +1148,14 @@ void SnesPpu::RenderTilemapMode7()
xStep = -xStep;
yStep = -yStep;
}
if(_drawStartX == 0) {
//Keep start/end values - used by tilemap viewer
_debugMode7StartX = xValue;
_debugMode7StartY = yValue;
_debugMode7EndX = xValue + xStep * 256;
_debugMode7EndY = yValue + yStep * 256;
}
xValue += xStep * _drawStartX;
yValue += yStep * _drawStartX;
@ -1501,6 +1516,19 @@ void SnesPpu::DebugSendFrame()
_emu->GetVideoDecoder()->UpdateFrame(frame, false, false);
}
void SnesPpu::DebugProcessMode7Overlay()
{
//Store mode 7 related information in ppu tools to allow tilemap viewer to display mode 7 overlay
SnesPpuTools* ppuTools = ((SnesPpuTools*)_emu->InternalGetDebugger()->GetPpuTools(CpuType::Snes));
ppuTools->SetPpuScanlineState(_scanline, _state.ForcedBlank ? 0 : _state.BgMode, _debugMode7StartX, _debugMode7StartY, _debugMode7EndX, _debugMode7EndY);
if(_scanline == _vblankStartScanline - 1) {
for(int i = _scanline + 1; i < 239; i++) {
ppuTools->SetPpuScanlineState(i, 0, 0, 0, 0, 0);
}
}
_debugMode7StartX = _debugMode7StartY = _debugMode7EndX = _debugMode7EndY = 0;
}
bool SnesPpu::IsHighResOutput()
{
return _useHighResOutput;

View file

@ -116,6 +116,11 @@ private:
uint8_t _spritePaletteCopy[256] = {};
uint8_t _spriteColorsCopy[256] = {};
int32_t _debugMode7StartX = 0;
int32_t _debugMode7StartY = 0;
int32_t _debugMode7EndX = 0;
int32_t _debugMode7EndY = 0;
void RenderSprites(const uint8_t priorities[4]);
template<bool hiResMode>
@ -204,6 +209,8 @@ private:
void RandomizeState();
__noinline void DebugProcessMode7Overlay();
public:
SnesPpu(Emulator* emu, SnesConsole* console);
virtual ~SnesPpu();

View file

@ -226,6 +226,7 @@ public:
void StopDebugger();
DebuggerRequest GetDebugger(bool autoInit = false);
bool IsDebugging();
Debugger* InternalGetDebugger() { return _debugger.get(); }
thread::id GetEmulationThreadId();
bool IsEmulationThread();

View file

@ -150,6 +150,7 @@ extern "C"
DllExport void __stdcall GetTileView(CpuType cpuType, GetTileViewOptions options, uint8_t* source, uint32_t srcSize, uint32_t* colors, uint32_t* buffer) { WithToolVoid(GetPpuTools(cpuType), GetTileView(options, source, srcSize, colors, buffer)); }
DllExport void __stdcall GetPpuToolsState(CpuType cpuType, BaseState& state) { return WithToolVoid(GetPpuTools(cpuType), GetPpuToolsState(state)); }
DllExport DebugTilemapInfo __stdcall GetTilemap(CpuType cpuType, GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t* outputBuffer) { return WithTool(DebugTilemapInfo, GetPpuTools(cpuType), GetTilemap(options, state, vram, palette, outputBuffer)); }
DllExport FrameInfo __stdcall GetTilemapSize(CpuType cpuType, GetTilemapOptions options, BaseState& state) { return WithTool(FrameInfo, GetPpuTools(cpuType), GetTilemapSize(options, state)); }
DllExport DebugTilemapTileInfo __stdcall GetTilemapTileInfo(uint32_t x, uint32_t y, CpuType cpuType, GetTilemapOptions options, uint8_t* vram, BaseState& state) { return WithTool(DebugTilemapTileInfo, GetPpuTools(cpuType), GetTilemapTileInfo(x, y, vram, options, state)); }

View file

@ -151,7 +151,8 @@ namespace Mesen.Debugger.Controls
AffectsRender<PictureViewer>(
SourceProperty, ZoomProperty, GridSizeXProperty, GridSizeYProperty,
ShowGridProperty, SelectionRectProperty, OverlayRectProperty,
HighlightRectsProperty, MouseOverRectProperty, GridHighlightProperty
HighlightRectsProperty, MouseOverRectProperty, GridHighlightProperty,
OverlayLinesProperty
);
SourceProperty.Changed.AddClassHandler<PictureViewer>((x, e) => {
@ -408,7 +409,7 @@ namespace Mesen.Debugger.Controls
if(OverlayLines?.Count > 0) {
foreach(PictureViewerLine line in OverlayLines) {
Pen pen = new Pen(line.Color.ToUint32(), 2, line.DashStyle);
Pen pen = new Pen(line.Color.ToUint32(), line.Width ?? 2, line.DashStyle);
context.DrawLine(pen, line.Start * Zoom, line.End * Zoom);
}
}
@ -491,6 +492,7 @@ namespace Mesen.Debugger.Controls
public Point Start;
public Point End;
public Color Color;
public int? Width;
public IDashStyle? DashStyle;
}
}

View file

@ -45,6 +45,7 @@ namespace Mesen.Debugger.ViewModels
[Reactive] public TilemapViewerTab SelectedTab { get; set; }
[Reactive] public Rect ScrollOverlayRect { get; private set; } = Rect.Empty;
[Reactive] public List<PictureViewerLine>? OverlayLines { get; private set; } = null;
public List<object> FileMenuActions { get; } = new();
public List<object> ViewMenuActions { get; } = new();
@ -53,6 +54,7 @@ namespace Mesen.Debugger.ViewModels
private PictureViewer _picViewer;
private UInt64 _masterClock;
private BaseState? _ppuState;
private BaseState? _ppuToolsState;
private byte[] _prevVram = Array.Empty<byte>();
private byte[] _vram = Array.Empty<byte>();
private UInt32[] _rgbPalette = Array.Empty<UInt32>();
@ -374,6 +376,7 @@ namespace Mesen.Debugger.ViewModels
BaseState ppuState = DebugApi.GetPpuState(CpuType);
_ppuState = ppuState;
_ppuToolsState = DebugApi.GetPpuToolsState(CpuType);
_prevVram = _vram;
_vram = DebugApi.GetMemoryState(GetVramMemoryType());
_accessCounters = DebugApi.GetMemoryAccessCounts(GetVramMemoryType());
@ -439,8 +442,11 @@ namespace Mesen.Debugger.ViewModels
_tilemapInfo.ScrollWidth,
_tilemapInfo.ScrollHeight
);
DrawMode7Overlay();
} else {
ScrollOverlayRect = Rect.Empty;
OverlayLines = null;
}
});
}
@ -563,6 +569,46 @@ namespace Mesen.Debugger.ViewModels
}
}
private void DrawMode7Overlay()
{
if(_ppuToolsState is SnesPpuToolsState toolsState && _ppuState is SnesPpuState ppuState) {
List<PictureViewerLine> lines = new();
HslColor baseColor = ColorHelper.RgbToHsl(Color.FromRgb(255, 0, 255));
for(int i = 0; i < 239; i++) {
if(toolsState.ScanlineBgMode[i] == 7) {
Color lineColor = ColorHelper.HslToRgb(baseColor);
Color alphaColor = Color.FromArgb(0xA0, lineColor.R, lineColor.G, lineColor.B);
int startX = toolsState.Mode7StartX[i] >> 8;
int startY = toolsState.Mode7StartY[i] >> 8;
int endX = toolsState.Mode7EndX[i] >> 8;
int endY = toolsState.Mode7EndY[i] >> 8;
lines.Add(new PictureViewerLine() { Start = new Point(startX, startY), End = new Point(endX, endY), Width = 1, Color = alphaColor });
if(!ppuState.Mode7.LargeMap) {
void Translate(ref int start, ref int end, int offset, Func<int, bool> predicate) {
while(predicate(start) || predicate(end)) {
start += offset;
end += offset;
lines.Add(new PictureViewerLine() { Start = new Point(startX, startY), End = new Point(endX, endY), Width = 1, Color = alphaColor });
}
}
Translate(ref startX, ref endX, 1024, x => x < 0);
Translate(ref startY, ref endY, 1024, x => x < 0);
Translate(ref startX, ref endX, -1024, x => x >= 1024);
Translate(ref startY, ref endY, -1024, x => x >= 1024);
}
}
baseColor.H--;
}
OverlayLines = lines;
} else {
OverlayLines = null;
}
}
public void OnGameLoaded()
{
_inGameLoaded = true;

View file

@ -108,6 +108,7 @@
AltGridSizeX="16"
AltGridSizeY="16"
ShowAltGrid="{CompiledBinding Config.ShowAltGrid}"
OverlayLines="{CompiledBinding OverlayLines}"
/>
</DockPanel>
</Window>

View file

@ -136,6 +136,25 @@ namespace Mesen.Interop
};
}
[DllImport(DllPath)] private static extern void GetPpuToolsState(CpuType cpuType, IntPtr state);
public unsafe static T GetPpuToolsState<T>(CpuType cpuType) where T : struct, BaseState
{
byte* ptr = stackalloc byte[Marshal.SizeOf(typeof(T))];
DebugApi.GetPpuToolsState(cpuType, (IntPtr)ptr);
return Marshal.PtrToStructure<T>((IntPtr)ptr);
}
public static BaseState GetPpuToolsState(CpuType cpuType)
{
return cpuType switch {
CpuType.Snes => GetPpuToolsState<SnesPpuToolsState>(cpuType),
CpuType.Nes => GetPpuToolsState<EmptyPpuToolsState>(cpuType),
CpuType.Gameboy => GetPpuToolsState<EmptyPpuToolsState>(cpuType),
CpuType.Pce => GetPpuToolsState<EmptyPpuToolsState>(cpuType),
_ => throw new Exception("Unsupport cpu type")
};
}
[DllImport(DllPath)] private static extern void SetPpuState(IntPtr state, CpuType cpuType);
public unsafe static void SetPpuState(BaseState state, CpuType cpuType)
{

View file

@ -958,6 +958,28 @@ namespace Mesen.Interop
public AluState Alu;
}
public struct EmptyPpuToolsState : BaseState
{
}
public struct SnesPpuToolsState : BaseState
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 239)]
public byte[] ScanlineBgMode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 239)]
public Int32[] Mode7StartX;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 239)]
public Int32[] Mode7StartY;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 239)]
public Int32[] Mode7EndX;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 239)]
public Int32[] Mode7EndY;
}
public struct NesPpuStatusFlags
{
[MarshalAs(UnmanagedType.I1)] public bool SpriteOverflow;

View file

@ -47,6 +47,11 @@ public:
_shared = ptr;
}
T* get()
{
return _ptr;
}
operator bool() const
{
return _ptr != nullptr;