Mesen2/Core/Debugger/PpuTools.h
Sour 8ad34343ba Debugger: Prevent UI freeze when viewer updates pile up and end up taking 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)
2024-12-10 18:59:13 +09:00

446 lines
12 KiB
C++

#pragma once
#include "pch.h"
#include "Debugger/DebugTypes.h"
#include "Shared/NotificationManager.h"
#include "Shared/Emulator.h"
#include "Shared/ColorUtilities.h"
class Debugger;
struct ViewerRefreshConfig
{
uint16_t Scanline;
uint16_t Cycle;
};
enum class SpriteVisibility : uint8_t
{
Visible = 0,
Offscreen = 1,
Disabled = 2
};
enum class NullableBoolean : int8_t
{
Undefined = -1,
False = 0,
True = 1
};
enum class DebugSpritePriority
{
Undefined = -1,
Number0 = 0,
Number1 = 1,
Number2 = 2,
Number3 = 3,
Foreground = 4,
Background = 5
};
struct DebugSpriteInfo
{
int32_t TileIndex;
int32_t TileAddress;
int32_t PaletteAddress;
TileFormat Format;
int16_t SpriteIndex;
int16_t X;
int16_t Y;
int16_t RawX;
int16_t RawY;
int16_t Bpp;
int16_t Palette;
DebugSpritePriority Priority;
uint16_t Width;
uint16_t Height;
NullableBoolean HorizontalMirror;
NullableBoolean VerticalMirror;
NullableBoolean MosaicEnabled;
NullableBoolean BlendingEnabled;
NullableBoolean WindowMode;
NullableBoolean TransformEnabled;
NullableBoolean DoubleSize;
int8_t TransformParamIndex;
SpriteVisibility Visibility;
bool UseExtendedVram;
NullableBoolean UseSecondTable;
uint32_t TileCount;
uint32_t TileAddresses[8 * 8];
public:
void Init()
{
TileIndex = -1;
TileAddress = -1;
PaletteAddress = -1;
Format = {};
SpriteIndex = -1;
X = -1;
Y = -1;
RawX = -1;
RawY = -1;
Bpp = 2;
Palette = -1;
Priority = DebugSpritePriority::Undefined;
Width = 0;
Height = 0;
HorizontalMirror = NullableBoolean::Undefined;
VerticalMirror = NullableBoolean::Undefined;
MosaicEnabled = NullableBoolean::Undefined;
TransformEnabled = NullableBoolean::Undefined;
BlendingEnabled = NullableBoolean::Undefined;
WindowMode = NullableBoolean::Undefined;
DoubleSize = NullableBoolean::Undefined;
TransformParamIndex = -1;
Visibility = SpriteVisibility::Offscreen;
UseExtendedVram = false;
UseSecondTable = NullableBoolean::Undefined;
TileCount = 0;
}
};
enum class TilemapMirroring
{
None,
Horizontal,
Vertical,
SingleScreenA,
SingleScreenB,
FourScreens,
};
struct DebugTilemapInfo
{
uint32_t Bpp;
TileFormat Format;
TilemapMirroring Mirroring;
uint32_t TileWidth;
uint32_t TileHeight;
uint32_t ScrollX;
uint32_t ScrollWidth;
uint32_t ScrollY;
uint32_t ScrollHeight;
uint32_t RowCount;
uint32_t ColumnCount;
uint32_t TilemapAddress;
uint32_t TilesetAddress;
int8_t Priority = -1;
};
struct DebugTilemapTileInfo
{
int32_t Row = -1;
int32_t Column = -1;
int32_t Width = -1;
int32_t Height = -1;
int32_t TileMapAddress = -1;
int32_t TileIndex = -1;
int32_t TileAddress = -1;
int32_t PixelData = -1;
int32_t PaletteIndex = -1;
int32_t PaletteAddress = -1;
int32_t BasePaletteIndex = -1;
int32_t AttributeAddress = -1;
int16_t AttributeData = -1;
NullableBoolean HorizontalMirroring = NullableBoolean::Undefined;
NullableBoolean VerticalMirroring = NullableBoolean::Undefined;
NullableBoolean HighPriority = NullableBoolean::Undefined;
};
struct DebugSpritePreviewInfo
{
uint32_t Width;
uint32_t Height;
uint32_t SpriteCount;
int32_t CoordOffsetX;
int32_t CoordOffsetY;
uint32_t VisibleX;
uint32_t VisibleY;
uint32_t VisibleWidth;
uint32_t VisibleHeight;
bool WrapBottomToTop;
bool WrapRightToLeft;
};
enum class RawPaletteFormat
{
Indexed,
Rgb555,
Rgb333,
Rgb222,
Rgb444,
Bgr444
};
struct DebugPaletteInfo
{
MemoryType PaletteMemType;
uint32_t PaletteMemOffset;
bool HasMemType;
uint32_t ColorCount;
uint32_t BgColorCount;
uint32_t SpriteColorCount;
uint32_t SpritePaletteOffset;
uint32_t ColorsPerPalette;
RawPaletteFormat RawFormat;
uint32_t RawPalette[512];
uint32_t RgbPalette[512];
};
class PpuTools
{
protected:
static constexpr uint32_t _spritePreviewSize = 128*128;
static constexpr uint32_t _grayscaleColorsBpp1[2] = { 0xFF000000, 0xFFFFFFFF };
static constexpr uint32_t _grayscaleColorsBpp2[4] = { 0xFF000000, 0xFF666666, 0xFFBBBBBB, 0xFFFFFFFF };
static constexpr uint32_t _grayscaleColorsBpp4[16] = {
0xFF000000, 0xFF303030, 0xFF404040, 0xFF505050, 0xFF606060, 0xFF707070, 0xFF808080, 0xFF909090,
0xFF989898, 0xFFA0A0A0, 0xFFAAAAAA, 0xFFBBBBBB, 0xFFCCCCCC, 0xFFDDDDDD, 0xFFEEEEEE, 0xFFFFFFFF
};
Emulator* _emu;
Debugger* _debugger;
unordered_map<uint32_t, ViewerRefreshConfig> _updateTimings;
void BlendColors(uint8_t output[4], uint8_t input[4]);
template<TileFormat format> __forceinline uint32_t GetRgbPixelColor(const uint32_t* colors, uint8_t colorIndex, uint8_t palette);
template<TileFormat format> __forceinline uint8_t GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, uint32_t rowStart, uint8_t pixelIndex);
bool IsTileHidden(MemoryType memType, uint32_t addr, GetTileViewOptions& options);
uint32_t GetBackgroundColor(TileBackground bgColor, const uint32_t* colors, uint8_t paletteIndex = 0, uint8_t bpp = 0);
uint32_t GetSpriteBackgroundColor(SpriteBackground bgColor, const uint32_t* colors, bool useDarkerColor);
void GetSetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t& color, bool forGet);
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);
virtual DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) = 0;
virtual FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) = 0;
virtual DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) = 0;
virtual DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) = 0;
virtual void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) = 0;
int32_t GetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y);
void SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color);
virtual void SetPaletteColor(int32_t colorIndex, uint32_t color) = 0;
virtual void SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle);
void RemoveViewer(uint32_t viewerId);
void UpdateViewers(uint16_t scanline, uint16_t cycle);
__forceinline bool HasOpenedViewer()
{
return _updateTimings.size() > 0;
}
template<TileFormat format>
void InternalGetTileView(GetTileViewOptions options, uint8_t* source, uint32_t srcSize, const uint32_t* colors, uint32_t* outBuffer);
};
template<TileFormat format> uint32_t PpuTools::GetRgbPixelColor(const uint32_t* colors, uint8_t colorIndex, uint8_t palette)
{
switch(format) {
case TileFormat::DirectColor:
return ColorUtilities::Rgb555ToArgb(
((((colorIndex & 0x07) << 1) | (palette & 0x01)) << 1) |
(((colorIndex & 0x38) | ((palette & 0x02) << 1)) << 4) |
(((colorIndex & 0xC0) | ((palette & 0x04) << 3)) << 7)
);
case TileFormat::NesBpp2:
case TileFormat::Bpp2:
return colors[palette * 4 + colorIndex];
case TileFormat::Bpp4:
case TileFormat::SmsBpp4:
case TileFormat::GbaBpp4:
case TileFormat::WsBpp4Packed:
case TileFormat::PceSpriteBpp4:
case TileFormat::PceSpriteBpp2Sp01:
case TileFormat::PceSpriteBpp2Sp23:
case TileFormat::PceBackgroundBpp2Cg0:
case TileFormat::PceBackgroundBpp2Cg1:
return colors[palette * 16 + colorIndex];
case TileFormat::Bpp8:
case TileFormat::GbaBpp8:
case TileFormat::Mode7:
case TileFormat::Mode7ExtBg:
return colors[palette * 256 + colorIndex];
case TileFormat::Mode7DirectColor:
return ColorUtilities::Rgb555ToArgb(((colorIndex & 0x07) << 2) | ((colorIndex & 0x38) << 4) | ((colorIndex & 0xC0) << 7));
case TileFormat::SmsSgBpp1:
return colors[palette * 2 + colorIndex];
default:
throw std::runtime_error("unsupported format");
}
}
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;
switch(format) {
case TileFormat::PceSpriteBpp4:
case TileFormat::PceSpriteBpp2Sp01:
case TileFormat::PceSpriteBpp2Sp23:
shift = 15 - pixelIndex;
if(shift >= 8) {
shift -= 8;
rowStart++;
}
break;
default:
break;
}
switch(format) {
case TileFormat::PceSpriteBpp4:
color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 32) & ramMask] >> shift) & 0x01) << 1);
color |= (((ram[(rowStart + 64) & ramMask] >> shift) & 0x01) << 2);
color |= (((ram[(rowStart + 96) & ramMask] >> shift) & 0x01) << 3);
return color;
case TileFormat::PceSpriteBpp2Sp01:
color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 32) & ramMask] >> shift) & 0x01) << 1);
return color;
case TileFormat::PceSpriteBpp2Sp23:
color = (((ram[(rowStart + 64) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 96) & ramMask] >> shift) & 0x01) << 1);
return color;
case TileFormat::PceBackgroundBpp2Cg0:
color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1);
return color;
case TileFormat::PceBackgroundBpp2Cg1:
color = (((ram[(rowStart + 16) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 17) & ramMask] >> shift) & 0x01) << 1);
return color;
case TileFormat::Bpp2:
color = (((ram[rowStart & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1);
return color;
case TileFormat::NesBpp2:
color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 8) & ramMask] >> shift) & 0x01) << 1);
return color;
case TileFormat::Bpp4:
color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1);
color |= (((ram[(rowStart + 16) & ramMask] >> shift) & 0x01) << 2);
color |= (((ram[(rowStart + 17) & ramMask] >> shift) & 0x01) << 3);
return color;
case TileFormat::Bpp8:
case TileFormat::DirectColor:
color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1);
color |= (((ram[(rowStart + 16) & ramMask] >> shift) & 0x01) << 2);
color |= (((ram[(rowStart + 17) & ramMask] >> shift) & 0x01) << 3);
color |= (((ram[(rowStart + 32) & ramMask] >> shift) & 0x01) << 4);
color |= (((ram[(rowStart + 33) & ramMask] >> shift) & 0x01) << 5);
color |= (((ram[(rowStart + 48) & ramMask] >> shift) & 0x01) << 6);
color |= (((ram[(rowStart + 49) & ramMask] >> shift) & 0x01) << 7);
return color;
case TileFormat::Mode7:
case TileFormat::Mode7DirectColor:
return ram[(rowStart + pixelIndex * 2 + 1) & ramMask];
case TileFormat::Mode7ExtBg:
return ram[(rowStart + pixelIndex * 2 + 1) & ramMask] & 0x7F;
case TileFormat::SmsBpp4:
color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0);
color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1);
color |= (((ram[(rowStart + 2) & ramMask] >> shift) & 0x01) << 2);
color |= (((ram[(rowStart + 3) & ramMask] >> shift) & 0x01) << 3);
return color;
case TileFormat::SmsSgBpp1:
color = ((ram[rowStart & ramMask] >> shift) & 0x01);
return color;
case TileFormat::GbaBpp4: {
uint8_t pixelOffset = (7 - shift);
uint32_t addr = (rowStart + (pixelOffset >> 1));
if(addr <= ramMask) {
if(pixelOffset & 0x01) {
return ram[addr] >> 4;
} else {
return ram[addr] & 0x0F;
}
} else {
return 0;
}
}
case TileFormat::GbaBpp8: {
uint8_t pixelOffset = (7 - shift);
uint32_t addr = rowStart + pixelOffset;
if(addr <= ramMask) {
return ram[addr];
} else {
return 0;
}
}
case TileFormat::WsBpp4Packed: {
uint8_t pixelOffset = (7 - shift);
uint32_t addr = (rowStart + (pixelOffset >> 1));
if(addr <= ramMask) {
if(pixelOffset & 0x01) {
return ram[addr] & 0x0F;
} else {
return ram[addr] >> 4;
}
} else {
return 0;
}
}
default:
throw std::runtime_error("unsupported format");
}
}