Skip to content

Commit

Permalink
Debugger: Prevent UI freeze when viewer updates pile up and end up ta…
Browse files Browse the repository at this point in the history
…king up 100% of the UI thread's time

Prevent queueing another update while the previous update is still running, which should prevent most issues.
Also slightly improves performance for the SNES tilemap viewer (which was causing the freeze when displaying an 8bpp 1024x1024 tilemap)
  • Loading branch information
SourMesen committed Dec 10, 2024
1 parent 8d49d0e commit 8ad3434
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 145 deletions.
2 changes: 1 addition & 1 deletion Core/Debugger/PpuTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ template<TileFormat format> uint32_t PpuTools::GetRgbPixelColor(const uint32_t*
}
}

template<TileFormat format> uint8_t PpuTools::GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, uint32_t rowStart, uint8_t pixelIndex)
template<TileFormat format> __forceinline uint8_t PpuTools::GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, uint32_t rowStart, uint8_t pixelIndex)
{
uint8_t shift = (7 - pixelIndex);
uint8_t color;
Expand Down
22 changes: 21 additions & 1 deletion Core/SNES/Debugger/SnesPpuTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Shared/SettingTypes.h"
#include "SNES/SnesPpu.h"
#include "Shared/ColorUtilities.h"
#include "Shared/MessageManager.h"

static constexpr uint8_t layerBpp[8][4] = {
{ 2,2,2,2 }, { 4,4,2,0 }, { 4,4,0,0 }, { 8,4,0,0 }, { 8,2,0,0 }, { 4,2,0,0 }, { 4,0,0,0 }, { 8,8,0,0 }
Expand Down Expand Up @@ -138,6 +139,24 @@ DebugTilemapInfo SnesPpuTools::GetTilemap(GetTilemapOptions options, BaseState&

template<TileFormat format>
void SnesPpuTools::RenderTilemap(GetTilemapOptions& options, int rowCount, LayerConfig& layer, int columnCount, uint8_t* vram, int tileHeight, int tileWidth, bool largeTileHeight, bool largeTileWidth, uint8_t bpp, uint32_t* outBuffer, FrameInfo outputSize, const uint32_t* palette, uint16_t basePaletteOffset)
{
if(largeTileHeight) {
if(largeTileWidth) {
RenderTilemap<format, true, true>(options, rowCount, layer, columnCount, vram, tileHeight, tileWidth, bpp, outBuffer, outputSize, palette, basePaletteOffset);
} else {
RenderTilemap<format, true, false>(options, rowCount, layer, columnCount, vram, tileHeight, tileWidth, bpp, outBuffer, outputSize, palette, basePaletteOffset);
}
} else {
if(largeTileWidth) {
RenderTilemap<format, false, true>(options, rowCount, layer, columnCount, vram, tileHeight, tileWidth, bpp, outBuffer, outputSize, palette, basePaletteOffset);
} else {
RenderTilemap<format, false, false>(options, rowCount, layer, columnCount, vram, tileHeight, tileWidth, bpp, outBuffer, outputSize, palette, basePaletteOffset);
}
}
}

template<TileFormat format, bool largeTileHeight, bool largeTileWidth>
void SnesPpuTools::RenderTilemap(GetTilemapOptions& options, int rowCount, LayerConfig& layer, int columnCount, uint8_t* vram, int tileHeight, int tileWidth, uint8_t bpp, uint32_t* outBuffer, FrameInfo outputSize, const uint32_t* palette, uint16_t basePaletteOffset)
{
uint8_t colorMask = 0xFF;
bool grayscale = options.DisplayMode == TilemapDisplayMode::Grayscale;
Expand All @@ -159,10 +178,11 @@ void SnesPpuTools::RenderTilemap(GetTilemapOptions& options, int rowCount, Layer

for(int y = 0; y < tileHeight; y++) {
uint8_t yOffset = vMirror ? (7 - (y & 0x07)) : (y & 0x07);
uint8_t yTileOffset = (largeTileHeight ? ((y & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0);

for(int x = 0; x < tileWidth; x++) {
uint16_t tileOffset = (
(largeTileHeight ? ((y & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) +
yTileOffset +
(largeTileWidth ? ((x & 0x08) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0)
);

Expand Down
1 change: 1 addition & 0 deletions Core/SNES/Debugger/SnesPpuTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class SnesPpuTools final : public PpuTools

template<TileFormat format> void RenderMode7Tilemap(GetTilemapOptions& options, uint8_t* vram, uint32_t* outBuffer, const uint32_t* palette);
template<TileFormat format> void RenderTilemap(GetTilemapOptions& options, int rowCount, LayerConfig& layer, int columnCount, uint8_t* vram, int tileHeight, int tileWidth, bool largeTileHeight, bool largeTileWidth, uint8_t bpp, uint32_t* outBuffer, FrameInfo outputSize, const uint32_t* palette, uint16_t basePaletteOffset);
template<TileFormat format, bool largeTileHeight, bool largeTileWidth> void RenderTilemap(GetTilemapOptions& options, int rowCount, LayerConfig& layer, int columnCount, uint8_t* vram, int tileHeight, int tileWidth, uint8_t bpp, uint32_t* outBuffer, FrameInfo outputSize, const uint32_t* palette, uint16_t basePaletteOffset);

DebugTilemapInfo RenderScreenView(uint8_t layer, uint32_t* outBuffer);
void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t* outBuffer);
Expand Down
40 changes: 26 additions & 14 deletions UI/Debugger/ViewModels/EventViewerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class EventViewerViewModel : DisposableViewModel
[Reactive] public List<ContextMenuAction> ToolbarItems { get; private set; } = new();

private PictureViewer _picViewer;
private bool _refreshPending;

[Obsolete("For designer only")]
public EventViewerViewModel() : this(CpuType.Nes, new PictureViewer(), null!, null) { }
Expand Down Expand Up @@ -269,24 +270,35 @@ public void RefreshData(bool forAutoRefresh = false)

public void RefreshUi(bool forAutoRefresh)
{
if(_refreshPending) {
return;
}

_refreshPending = true;
Dispatcher.UIThread.Post(() => {
if(Disposed) {
return;
}
InternalRefreshUi(forAutoRefresh);
_refreshPending = false;
});
}

InitBitmap();
using(var bitmapLock = ViewerBitmap.Lock()) {
DebugApi.GetEventViewerOutput(CpuType, bitmapLock.FrameBuffer.Address, (uint)(ViewerBitmap.Size.Width * ViewerBitmap.Size.Height * sizeof(UInt32)));
}
private void InternalRefreshUi(bool forAutoRefresh)
{
if(Disposed) {
return;
}

if(ShowListView) {
DateTime now = DateTime.Now;
if(!forAutoRefresh || (now - _lastListRefresh).TotalMilliseconds >= 66) {
_lastListRefresh = now;
ListView.RefreshList();
}
InitBitmap();
using(var bitmapLock = ViewerBitmap.Lock()) {
DebugApi.GetEventViewerOutput(CpuType, bitmapLock.FrameBuffer.Address, (uint)(ViewerBitmap.Size.Width * ViewerBitmap.Size.Height * sizeof(UInt32)));
}

if(ShowListView) {
DateTime now = DateTime.Now;
if(!forAutoRefresh || (now - _lastListRefresh).TotalMilliseconds >= 66) {
_lastListRefresh = now;
ListView.RefreshList();
}
});
}
}

private PixelPoint GetEventLocation(DebugEventInfo evt)
Expand Down
120 changes: 66 additions & 54 deletions UI/Debugger/ViewModels/SpriteViewerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public class SpriteViewerViewModel : DisposableViewModel, ICpuTypeModel, IMouseO

private DebugSpriteInfo[] _spriteList = Array.Empty<DebugSpriteInfo>();
private UInt32[] _spritePreviews = Array.Empty<UInt32>();
private bool _refreshPending;

[Obsolete("For designer only")]
public SpriteViewerViewModel() : this(CpuType.Snes, new PictureViewer(), new Grid(), new Control(), null) { }
Expand Down Expand Up @@ -530,76 +531,87 @@ public void RefreshData()

private void RefreshTab()
{
if(_refreshPending) {
return;
}

_refreshPending = true;
Dispatcher.UIThread.Post(() => {
if(Disposed) {
return;
}
InternalRefreshTab();
_refreshPending = false;
});
}

lock(_updateLock) {
_coreData.CopyTo(_data);
}
private void InternalRefreshTab()
{
if(Disposed) {
return;
}

if(_data.PpuState == null || _data.Palette == null || _data.PpuToolsState == null) {
return;
}
lock(_updateLock) {
_coreData.CopyTo(_data);
}

if(_data.PpuState == null || _data.Palette == null || _data.PpuToolsState == null) {
return;
}

GetSpritePreviewOptions options = new GetSpritePreviewOptions() {
Background = Config.Background
};
GetSpritePreviewOptions options = new GetSpritePreviewOptions() {
Background = Config.Background
};

DebugSpritePreviewInfo previewInfo = DebugApi.GetSpritePreviewInfo(CpuType, options, _data.PpuState, _data.PpuToolsState);
InitBitmap((int)previewInfo.Width, (int)previewInfo.Height);
DebugSpritePreviewInfo previewInfo = DebugApi.GetSpritePreviewInfo(CpuType, options, _data.PpuState, _data.PpuToolsState);
InitBitmap((int)previewInfo.Width, (int)previewInfo.Height);

UInt32[] palette = _data.Palette.Value.GetRgbPalette();
UInt32[] palette = _data.Palette.Value.GetRgbPalette();

LeftClipSize = Config.ShowOffscreenRegions ? 0 : (int)previewInfo.VisibleX;
RightClipSize = Config.ShowOffscreenRegions ? 0 : (int)(previewInfo.Width - (previewInfo.VisibleWidth + previewInfo.VisibleX));
TopClipSize = Config.ShowOffscreenRegions ? 0 : (int)previewInfo.VisibleY;
BottomClipSize = Config.ShowOffscreenRegions ? 0 : (int)(previewInfo.Height - (previewInfo.VisibleHeight + previewInfo.VisibleY));
LeftClipSize = Config.ShowOffscreenRegions ? 0 : (int)previewInfo.VisibleX;
RightClipSize = Config.ShowOffscreenRegions ? 0 : (int)(previewInfo.Width - (previewInfo.VisibleWidth + previewInfo.VisibleX));
TopClipSize = Config.ShowOffscreenRegions ? 0 : (int)previewInfo.VisibleY;
BottomClipSize = Config.ShowOffscreenRegions ? 0 : (int)(previewInfo.Height - (previewInfo.VisibleHeight + previewInfo.VisibleY));

using(var framebuffer = ViewerBitmap.Lock(true)) {
DebugApi.GetSpriteList(ref _spriteList, ref _spritePreviews, CpuType, options, _data.PpuState, _data.PpuToolsState, _data.Vram, _data.SpriteRam, palette, framebuffer.FrameBuffer.Address);
}
using(var framebuffer = ViewerBitmap.Lock(true)) {
DebugApi.GetSpriteList(ref _spriteList, ref _spritePreviews, CpuType, options, _data.PpuState, _data.PpuToolsState, _data.Vram, _data.SpriteRam, palette, framebuffer.FrameBuffer.Address);
}

InitPreviews(_spriteList, _spritePreviews, previewInfo);

InitPreviews(_spriteList, _spritePreviews, previewInfo);

if(Config.ShowOutline) {
List<Rect> spriteRects = new List<Rect>();
foreach(SpritePreviewModel sprite in SpritePreviews) {
(Rect mainRect, Rect alt1, Rect alt2, Rect alt3) = sprite.GetPreviewRect();
spriteRects.Add(mainRect);
if(alt1 != default) {
spriteRects.Add(alt1);
}
if(alt2 != default) {
spriteRects.Add(alt2);
}
if(alt3 != default) {
spriteRects.Add(alt3);
}
if(Config.ShowOutline) {
List<Rect> spriteRects = new List<Rect>();
foreach(SpritePreviewModel sprite in SpritePreviews) {
(Rect mainRect, Rect alt1, Rect alt2, Rect alt3) = sprite.GetPreviewRect();
spriteRects.Add(mainRect);
if(alt1 != default) {
spriteRects.Add(alt1);
}
if(alt2 != default) {
spriteRects.Add(alt2);
}
if(alt3 != default) {
spriteRects.Add(alt3);
}
ViewerBitmap.HighlightRects = spriteRects;
} else {
ViewerBitmap.HighlightRects = null;
}
ViewerBitmap.HighlightRects = spriteRects;
} else {
ViewerBitmap.HighlightRects = null;
}

ViewerBitmap.Invalidate();
ViewerBitmap.Invalidate();

int selectedIndex = SelectedSprite?.SpriteIndex ?? -1;
if(selectedIndex >= 0 && selectedIndex < SpritePreviews.Count) {
SelectedSprite = SpritePreviews[selectedIndex];
UpdateSelectionPreview();
} else {
SelectedSprite = null;
}
int selectedIndex = SelectedSprite?.SpriteIndex ?? -1;
if(selectedIndex >= 0 && selectedIndex < SpritePreviews.Count) {
SelectedSprite = SpritePreviews[selectedIndex];
UpdateSelectionPreview();
} else {
SelectedSprite = null;
}

ListView.RefreshList();
ListView.RefreshList();

UpdateTooltips();
UpdateSelection(SelectedSprite);
UpdateTooltips();
UpdateSelection(SelectedSprite);

UpdateMouseOverRect();
});
UpdateMouseOverRect();
}

private void UpdateSelectionPreview()
Expand Down
50 changes: 31 additions & 19 deletions UI/Debugger/ViewModels/TileViewerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class TileViewerViewModel : DisposableViewModel, ICpuTypeModel, IMouseOve
private object _updateLock = new();
private byte[] _coreSourceData = Array.Empty<byte>();
private byte[] _sourceData = Array.Empty<byte>();
private bool _refreshPending;

[Obsolete("For designer only")]
public TileViewerViewModel() : this(CpuType.Snes, new PictureViewer(), null) { }
Expand Down Expand Up @@ -465,31 +466,42 @@ private void RefreshPalette()

private void RefreshTab()
{
if(_refreshPending) {
return;
}

_refreshPending = true;
Dispatcher.UIThread.Post(() => {
if(Disposed) {
return;
}
InternalRefreshTab();
_refreshPending = false;
});
}

private void InternalRefreshTab()
{
if(Disposed) {
return;
}

InitBitmap();
InitBitmap();

lock(_updateLock) {
Array.Resize(ref _sourceData, _coreSourceData.Length);
Array.Copy(_coreSourceData, _sourceData, _coreSourceData.Length);
}
lock(_updateLock) {
Array.Resize(ref _sourceData, _coreSourceData.Length);
Array.Copy(_coreSourceData, _sourceData, _coreSourceData.Length);
}

using(var framebuffer = ViewerBitmap.Lock()) {
DebugApi.GetTileView(CpuType, GetOptions(), _sourceData, _sourceData.Length, PaletteColors, framebuffer.FrameBuffer.Address);
}
using(var framebuffer = ViewerBitmap.Lock()) {
DebugApi.GetTileView(CpuType, GetOptions(), _sourceData, _sourceData.Length, PaletteColors, framebuffer.FrameBuffer.Address);
}

if(IsNesChrModeEnabled) {
DrawNesChrPageDelimiters();
} else {
PageDelimiters = null;
}
if(IsNesChrModeEnabled) {
DrawNesChrPageDelimiters();
} else {
PageDelimiters = null;
}

UpdatePreviewPanel();
LoadSelectedPreset(true);
});
UpdatePreviewPanel();
LoadSelectedPreset(true);
}

private int GetTileAddress(PixelPoint pixelPosition)
Expand Down
Loading

0 comments on commit 8ad3434

Please sign in to comment.