mirror of
https://github.com/stenzek/duckstation.git
synced 2026-11-13 17:50:19 +01:00
ImGuiOverlays: Polish component debug windows
This commit is contained in:
9 files changed
+1193
-751
No files matched your search
@@ -6,6 +6,7 @@
|
||||
// To generate: "<your emoji>".encode("utf-8") in Python 3.
|
||||
// List to use: https://www.freecodecamp.org/news/all-emojis-emoji-list-for-copy-and-paste/
|
||||
|
||||
#define ICON_EMOJI_MIDDLE_DOT "\xc2\xb7"
|
||||
#define ICON_EMOJI_WARNING "\xe2\x9a\xa0"
|
||||
#define ICON_EMOJI_CHECKMARK_BUTTON "\xe2\x9c\x85"
|
||||
#define ICON_EMOJI_CROSS_MARK_BUTTON "\xe2\x9d\x8c"
|
||||
|
||||
+266
-279
@@ -8,6 +8,7 @@
|
||||
#include "dma.h"
|
||||
#include "fullscreenui.h"
|
||||
#include "host.h"
|
||||
#include "imgui_overlays.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "mdec.h"
|
||||
#include "settings.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "common/xorshift_prng.h"
|
||||
|
||||
#include "IconsEmoji.h"
|
||||
#include "IconsFontAwesome.h"
|
||||
#include "fmt/format.h"
|
||||
#include "imgui.h"
|
||||
|
||||
@@ -502,7 +504,6 @@ struct CDROMState
|
||||
|
||||
std::map<u32, std::pair<u32, std::string>> file_map;
|
||||
bool file_map_created = false;
|
||||
bool show_current_file = false;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -584,7 +585,6 @@ void CDROM::Shutdown()
|
||||
{
|
||||
s_state.file_map.clear();
|
||||
s_state.file_map_created = false;
|
||||
s_state.show_current_file = false;
|
||||
|
||||
s_state.drive_event.Deactivate();
|
||||
s_state.async_interrupt_event.Deactivate();
|
||||
@@ -1035,8 +1035,8 @@ bool CDROM::InsertMedia(std::unique_ptr<CDImage>& media, DiscRegion region, std:
|
||||
if (s_state.drive_state != DriveState::ShellOpening && !(s_state.manual_lid_control & MANUAL_LID_CONTROL_OPEN))
|
||||
StartMotor();
|
||||
|
||||
if (s_state.show_current_file)
|
||||
CreateFileMap();
|
||||
s_state.file_map.clear();
|
||||
s_state.file_map_created = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1054,8 +1054,8 @@ std::unique_ptr<CDImage> CDROM::RemoveMedia(bool for_disc_swap)
|
||||
INFO_LOG("Removing CD...");
|
||||
std::unique_ptr<CDImage> image = CDROMAsyncReader::RemoveMedia();
|
||||
|
||||
if (s_state.show_current_file)
|
||||
CreateFileMap();
|
||||
s_state.file_map.clear();
|
||||
s_state.file_map_created = false;
|
||||
|
||||
s_state.last_sector_header_valid = false;
|
||||
|
||||
@@ -4235,6 +4235,12 @@ void CDROM::CheckForSectorBufferReadComplete()
|
||||
|
||||
void CDROM::CreateFileMap()
|
||||
{
|
||||
if (!System::IsValid() || !ImGuiManager::IsCDROMDebugWindowVisible(g_settings.debug_window_visibility) ||
|
||||
s_state.file_map_created)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_state.file_map.clear();
|
||||
s_state.file_map_created = true;
|
||||
|
||||
@@ -4302,301 +4308,282 @@ const std::string* CDROM::LookupFileMap(u32 lba, u32* start_lba, u32* end_lba)
|
||||
|
||||
void CDROM::DrawDebugWindow(float scale)
|
||||
{
|
||||
static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f};
|
||||
if (!s_state.file_map_created)
|
||||
Host::RunOnCoreThread(static_cast<void (*)()>(&CDROM::CreateFileMap));
|
||||
|
||||
// draw voice states
|
||||
if (ImGui::CollapsingHeader("Media", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
const float left = ImGui::GetCursorPosX();
|
||||
const float button_width =
|
||||
ImGui::CalcTextSize(ICON_FA_COPY " Copy State").x + ImGui::GetStyle().FramePadding.x * 2.0f;
|
||||
ImGui::SetCursorPosX(left + ImGui::GetContentRegionAvail().x - button_width);
|
||||
const bool copy_state = ImGui::Button(ICON_FA_COPY " Copy State");
|
||||
if (copy_state)
|
||||
ImGui::LogToClipboard();
|
||||
ImGui::SameLine(left);
|
||||
ImGui::PushFont(nullptr, std::ceil(18.0f * scale), 0.0f);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const float drive_speed =
|
||||
(s_state.last_sector_read_ticks > 0) ?
|
||||
((static_cast<float>(System::GetTicksPerSecond()) / static_cast<float>(SINGLE_SPEED_SECTORS_PER_SECOND)) /
|
||||
static_cast<float>(s_state.last_sector_read_ticks)) :
|
||||
0.0f;
|
||||
if (!CDROMAsyncReader::HasMedia())
|
||||
ImGui::TextUnformatted("No disc");
|
||||
else if ((s_state.drive_state == DriveState::Reading || s_state.drive_state == DriveState::Playing) &&
|
||||
drive_speed > 0.0f)
|
||||
ImGui::Text("%s at %.2fx", s_drive_state_names[static_cast<u8>(s_state.drive_state)], drive_speed);
|
||||
else
|
||||
ImGui::TextUnformatted(s_drive_state_names[static_cast<u8>(s_state.drive_state)]);
|
||||
ImGui::PopFont();
|
||||
ImGui::Spacing();
|
||||
|
||||
constexpr ImGuiTableFlags table_flags =
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_PadOuterX | ImGuiTableFlags_SizingStretchSame;
|
||||
static constexpr auto label = [](const char* text) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImVec4 color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||
color.w *= 0.7f;
|
||||
ImGui::TextColored(color, "%s", text);
|
||||
ImGui::TableNextColumn();
|
||||
};
|
||||
static constexpr auto flag = [](const char* text, bool value) {
|
||||
label(text);
|
||||
ImGui::TextUnformatted(value ? "Yes" : "No");
|
||||
};
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(10.0f * scale, ImGui::GetStyle().CellPadding.y));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(1.0f, 1.0f, 1.0f, 0.035f));
|
||||
if (ImGui::BeginTable("layout", 2, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV))
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::SeparatorText("Media");
|
||||
if (CDROMAsyncReader::HasMedia())
|
||||
{
|
||||
const CDImage* const media = CDROMAsyncReader::GetMedia();
|
||||
const CDImage::Position disc_position = CDImage::Position::FromLBA(s_state.current_lba);
|
||||
const float start_y = ImGui::GetCursorPosY();
|
||||
|
||||
if (media->HasSubImages())
|
||||
const CDImage::Index* const index = media->GetIndexForDiscPosition(s_state.current_lba);
|
||||
if (ImGui::BeginTable("media", 2, table_flags))
|
||||
{
|
||||
ImGui::Text("Filename: %s [Subimage %u of %u] [%u buffered sectors]", media->GetPath().c_str(),
|
||||
media->GetCurrentSubImage() + 1u, media->GetSubImageCount(),
|
||||
CDROMAsyncReader::GetBufferedSectorCount());
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("Filename: %s [%u buffered sectors]", media->GetPath().c_str(),
|
||||
CDROMAsyncReader::GetBufferedSectorCount());
|
||||
}
|
||||
|
||||
ImGui::Text("Disc Position: MSF[%02u:%02u:%02u] LBA[%u]", disc_position.minute, disc_position.second,
|
||||
disc_position.frame, disc_position.ToLBA());
|
||||
|
||||
const CDImage::Index* index = media->GetIndexForDiscPosition(s_state.current_lba);
|
||||
if (!index || index->track_number > media->GetTrackCount())
|
||||
{
|
||||
ImGui::Text("Track Position: Lead-out");
|
||||
}
|
||||
else
|
||||
{
|
||||
const CDImage::Position track_position = CDImage::Position::FromLBA(
|
||||
s_state.current_lba - media->GetTrackStartPosition(Truncate8(index->track_number)));
|
||||
ImGui::Text("Track Position: Number[%u] MSF[%02u:%02u:%02u] LBA[%u]", index->track_number,
|
||||
track_position.minute, track_position.second, track_position.frame, track_position.ToLBA());
|
||||
}
|
||||
|
||||
ImGui::Text("Last Sector: %02X:%02X:%02X (Mode %u)", s_state.last_sector_header.minute,
|
||||
s_state.last_sector_header.second, s_state.last_sector_header.frame,
|
||||
s_state.last_sector_header.sector_mode);
|
||||
|
||||
if (s_state.show_current_file)
|
||||
{
|
||||
if (index && index->track_number == 1)
|
||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
label("Image");
|
||||
ImGui::TextWrapped("%s", media->GetPath().c_str());
|
||||
if (media->HasSubImages())
|
||||
{
|
||||
if (!s_state.file_map_created)
|
||||
label("Subimage");
|
||||
ImGui::Text("%u / %u", media->GetCurrentSubImage() + 1u, media->GetSubImageCount());
|
||||
}
|
||||
label("Buffered sectors");
|
||||
ImGui::Text("%u", CDROMAsyncReader::GetBufferedSectorCount());
|
||||
label("Disc position");
|
||||
ImGui::Text("%02u:%02u:%02u (LBA %u)", disc_position.minute, disc_position.second, disc_position.frame,
|
||||
disc_position.ToLBA());
|
||||
label("Track position");
|
||||
if (!index || index->track_number > media->GetTrackCount())
|
||||
{
|
||||
ImGui::TextUnformatted("Lead-out");
|
||||
}
|
||||
else
|
||||
{
|
||||
const CDImage::Position track_position = CDImage::Position::FromLBA(
|
||||
s_state.current_lba - media->GetTrackStartPosition(Truncate8(index->track_number)));
|
||||
ImGui::TextWrapped("%u " ICON_EMOJI_MIDDLE_DOT " %02u:%02u:%02u (LBA %u)", index->track_number,
|
||||
track_position.minute, track_position.second, track_position.frame,
|
||||
track_position.ToLBA());
|
||||
}
|
||||
label("Last sector");
|
||||
ImGui::Text("%02X:%02X:%02X (Mode %u)", s_state.last_sector_header.minute, s_state.last_sector_header.second,
|
||||
s_state.last_sector_header.frame, s_state.last_sector_header.sector_mode);
|
||||
label("Current file");
|
||||
if (!s_state.file_map_created)
|
||||
{
|
||||
ImGui::TextUnformatted("Loading...");
|
||||
}
|
||||
else if (index && index->track_number == 1)
|
||||
{
|
||||
u32 start_lba, end_lba;
|
||||
const u32 track_lba = s_state.current_lba - media->GetTrackStartPosition(1);
|
||||
const std::string* const file = LookupFileMap(track_lba, &start_lba, &end_lba);
|
||||
ImGui::TextWrapped("%s", file ? file->c_str() : "Unknown");
|
||||
if (file)
|
||||
{
|
||||
Host::RunOnCoreThread(static_cast<void (*)()>(&CDROM::CreateFileMap));
|
||||
return;
|
||||
}
|
||||
|
||||
u32 current_file_start_lba, current_file_end_lba;
|
||||
const u32 track_lba = s_state.current_lba - media->GetTrackStartPosition(Truncate8(index->track_number));
|
||||
const std::string* current_file = LookupFileMap(track_lba, ¤t_file_start_lba, ¤t_file_end_lba);
|
||||
if (current_file)
|
||||
{
|
||||
static constexpr auto readable_size = [](u32 val) {
|
||||
// based on
|
||||
// https://stackoverflow.com/questions/1449805/how-to-format-a-number-using-comma-as-thousands-separator-in-c
|
||||
// don't want to use locale...
|
||||
TinyString ret;
|
||||
TinyString temp;
|
||||
temp.append_format("{}", val);
|
||||
|
||||
u32 commas = 2u - (temp.length() % 3u);
|
||||
for (const char* p = temp.c_str(); *p != 0u; p++)
|
||||
{
|
||||
ret.append(*p);
|
||||
if (commas == 1)
|
||||
ret.append(',');
|
||||
commas = (commas + 1) % 3;
|
||||
}
|
||||
|
||||
DebugAssert(!ret.empty());
|
||||
ret.erase(-1);
|
||||
return ret;
|
||||
};
|
||||
ImGui::Text(
|
||||
"Current File: %s (%s of %s bytes)", current_file->c_str(),
|
||||
readable_size((track_lba - current_file_start_lba) * CDImage::DATA_SECTOR_SIZE).c_str(),
|
||||
readable_size((current_file_end_lba - current_file_start_lba + 1) * CDImage::DATA_SECTOR_SIZE).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("Current File: <Unknown>");
|
||||
label("File position");
|
||||
const TinyString position = TinyString::from_format(
|
||||
"{} / {} bytes", fmt::group_digits((track_lba - start_lba) * CDImage::DATA_SECTOR_SIZE),
|
||||
fmt::group_digits((end_lba - start_lba + 1) * CDImage::DATA_SECTOR_SIZE));
|
||||
ImGui::TextWrapped("%s", position.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("Current File: <Non-Data Track>");
|
||||
ImGui::TextUnformatted("Non-data track");
|
||||
}
|
||||
label("File map");
|
||||
ImGui::Text("%u entries", static_cast<u32>(s_state.file_map.size()));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted("No disc inserted");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("[%u files on disc]", static_cast<u32>(s_state.file_map.size()));
|
||||
ImGui::SeparatorText("Command / Interrupts");
|
||||
if (ImGui::BeginTable("command", 2, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
label("Command");
|
||||
if (HasPendingCommand())
|
||||
{
|
||||
ImGui::TextWrapped("%s (0x%02X, %d ticks)", GetCommandName(s_state.command), static_cast<u8>(s_state.command),
|
||||
s_state.command_event.IsActive() ? s_state.command_event.GetTicksUntilNextExecution() : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float end_y = ImGui::GetCursorPosY();
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 140.0f * scale);
|
||||
ImGui::SetCursorPosY(start_y);
|
||||
if (ImGui::Button("Show Current File"))
|
||||
s_state.show_current_file = true;
|
||||
|
||||
ImGui::SetCursorPosY(end_y);
|
||||
ImGui::TextUnformatted("None");
|
||||
}
|
||||
label("Last read speed");
|
||||
if (drive_speed > 0.0f)
|
||||
ImGui::Text("%.2fx", drive_speed);
|
||||
else
|
||||
ImGui::TextUnformatted("None");
|
||||
label("Drive ticks remaining");
|
||||
ImGui::Text("%d", s_state.drive_event.IsActive() ? s_state.drive_event.GetTicksUntilNextExecution() : 0);
|
||||
label("Read speedup");
|
||||
if (g_settings.cdrom_read_speedup == 1)
|
||||
{
|
||||
ImGui::TextUnformatted("Disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsDriveIdle() && !CanUseReadSpeedup())
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Blocked");
|
||||
else
|
||||
ImGui::TextUnformatted("Enabled");
|
||||
}
|
||||
|
||||
label("Interrupt enable");
|
||||
ImGui::Text("0x%02X", s_state.interrupt_enable_register);
|
||||
label("Interrupt flag");
|
||||
ImGui::Text("0x%02X", s_state.interrupt_flag_register);
|
||||
label("Pending interrupt");
|
||||
if (HasPendingAsyncInterrupt())
|
||||
ImGui::Text("0x%02X", s_state.pending_async_interrupt);
|
||||
else
|
||||
ImGui::TextUnformatted("None");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
else
|
||||
|
||||
ImGui::SeparatorText("Status");
|
||||
if (ImGui::BeginTable("status", 2, table_flags))
|
||||
{
|
||||
ImGui::Text("No media inserted.");
|
||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
flag("ADPBUSY", s_state.status.ADPBUSY);
|
||||
flag("PRMEMPTY", s_state.status.PRMEMPTY);
|
||||
flag("PRMWRDY", s_state.status.PRMWRDY);
|
||||
flag("RSLRRDY", s_state.status.RSLRRDY);
|
||||
flag("DRQSTS", s_state.status.DRQSTS);
|
||||
flag("BUSYSTS", s_state.status.BUSYSTS);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SeparatorText("Secondary status");
|
||||
if (ImGui::BeginTable("secondary", 2, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
flag("Error", s_state.secondary_status.error);
|
||||
flag("Motor on", s_state.secondary_status.motor_on);
|
||||
flag("Seek error", s_state.secondary_status.seek_error);
|
||||
flag("ID error", s_state.secondary_status.id_error);
|
||||
flag("Shell open", s_state.secondary_status.shell_open);
|
||||
if (s_state.manual_lid_control & MANUAL_LID_CONTROL_ENABLED)
|
||||
{
|
||||
label("Manual lid");
|
||||
ImGui::TextUnformatted((s_state.manual_lid_control & MANUAL_LID_CONTROL_OPEN) ? "Open" : "Closed");
|
||||
}
|
||||
flag("Reading", s_state.secondary_status.reading);
|
||||
flag("Seeking", s_state.secondary_status.seeking);
|
||||
flag("Playing CDDA", s_state.secondary_status.playing_cdda);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Mode");
|
||||
if (ImGui::BeginTable("mode", 2, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
flag("CDDA", s_state.mode.cdda);
|
||||
flag("Auto pause", s_state.mode.auto_pause);
|
||||
flag("Report audio", s_state.mode.report_audio);
|
||||
flag("XA filter", s_state.mode.xa_filter);
|
||||
label("XA filter file / channel");
|
||||
ImGui::Text("%u / %u", s_state.xa_filter_file_number, s_state.xa_filter_channel_number);
|
||||
flag("Ignore bit", s_state.mode.ignore_bit);
|
||||
flag("Raw sectors", s_state.mode.read_raw_sector);
|
||||
flag("XA enable", s_state.mode.xa_enable);
|
||||
flag("Double speed", s_state.mode.double_speed);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("CD Audio");
|
||||
if (ImGui::BeginTable("audio", 2, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
label("Playing");
|
||||
if (s_state.drive_state == DriveState::Reading && s_state.mode.xa_enable)
|
||||
{
|
||||
ImGui::TextUnformatted("XA-ADPCM");
|
||||
label("XA file / channel");
|
||||
ImGui::Text("%u / %u", s_state.xa_current_file_number, s_state.xa_current_channel_number);
|
||||
label("Format");
|
||||
ImGui::TextWrapped("%s, %s, %u Hz", s_state.xa_current_codinginfo.IsStereo() ? "Stereo" : "Mono",
|
||||
s_state.xa_current_codinginfo.Is8BitADPCM() ? "8-bit" : "4-bit",
|
||||
s_state.xa_current_codinginfo.IsHalfSampleRate() ? 18900u : 37800u);
|
||||
}
|
||||
else if (s_state.drive_state == DriveState::Playing)
|
||||
{
|
||||
ImGui::Text("CDDA (track %X)", s_state.last_subq.track_number_bcd);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted("Inactive");
|
||||
}
|
||||
flag("Muted", s_state.muted);
|
||||
label("Audio FIFO");
|
||||
ImGui::Text("%u frames", s_state.audio_fifo.GetSize());
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (ImGui::BeginTable("volume", 3, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("Left output");
|
||||
ImGui::TableSetupColumn("Right output");
|
||||
ImGui::TableHeadersRow();
|
||||
for (u32 input = 0; input < 2; input++)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(input == 0 ? "Left input" : "Right input");
|
||||
for (u32 output = 0; output < 2; output++)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
const u8 volume = s_state.cd_audio_volume_matrix[input][output];
|
||||
ImGui::Text("%02X (%u%%)", volume, ZeroExtend32(volume) * 100 / 0x80);
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Status/Mode", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Columns(3);
|
||||
|
||||
ImGui::Text("Status");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Secondary Status");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Mode Status");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextColored(s_state.status.ADPBUSY ? active_color : inactive_color, "ADPBUSY: %s",
|
||||
s_state.status.ADPBUSY ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.error ? active_color : inactive_color, "Error: %s",
|
||||
s_state.secondary_status.error ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.cdda ? active_color : inactive_color, "CDDA: %s", s_state.mode.cdda ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextColored(s_state.status.PRMEMPTY ? active_color : inactive_color, "PRMEMPTY: %s",
|
||||
s_state.status.PRMEMPTY ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.motor_on ? active_color : inactive_color, "Motor On: %s",
|
||||
s_state.secondary_status.motor_on ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.auto_pause ? active_color : inactive_color, "Auto Pause: %s",
|
||||
s_state.mode.auto_pause ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextColored(s_state.status.PRMWRDY ? active_color : inactive_color, "PRMWRDY: %s",
|
||||
s_state.status.PRMWRDY ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.seek_error ? active_color : inactive_color, "Seek Error: %s",
|
||||
s_state.secondary_status.seek_error ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.report_audio ? active_color : inactive_color, "Report Audio: %s",
|
||||
s_state.mode.report_audio ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextColored(s_state.status.RSLRRDY ? active_color : inactive_color, "RSLRRDY: %s",
|
||||
s_state.status.RSLRRDY ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.id_error ? active_color : inactive_color, "ID Error: %s",
|
||||
s_state.secondary_status.id_error ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.xa_filter ? active_color : inactive_color, "XA Filter: %s (File %u Channel %u)",
|
||||
s_state.mode.xa_filter ? "Yes" : "No", s_state.xa_filter_file_number,
|
||||
s_state.xa_filter_channel_number);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextColored(s_state.status.DRQSTS ? active_color : inactive_color, "DRQSTS: %s",
|
||||
s_state.status.DRQSTS ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.shell_open ? active_color : inactive_color, "Shell Open: %s",
|
||||
s_state.secondary_status.shell_open ? "Yes" : "No");
|
||||
if (s_state.manual_lid_control & MANUAL_LID_CONTROL_ENABLED)
|
||||
{
|
||||
const bool open = (s_state.manual_lid_control & MANUAL_LID_CONTROL_OPEN);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(open ? active_color : inactive_color, "Lid: %s", open ? "Open" : "Closed");
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.ignore_bit ? active_color : inactive_color, "Ignore Bit: %s",
|
||||
s_state.mode.ignore_bit ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::TextColored(s_state.status.BUSYSTS ? active_color : inactive_color, "BUSYSTS: %s",
|
||||
s_state.status.BUSYSTS ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.reading ? active_color : inactive_color, "Reading: %s",
|
||||
s_state.secondary_status.reading ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.read_raw_sector ? active_color : inactive_color, "Read Raw Sectors: %s",
|
||||
s_state.mode.read_raw_sector ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.seeking ? active_color : inactive_color, "Seeking: %s",
|
||||
s_state.secondary_status.seeking ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.xa_enable ? active_color : inactive_color, "XA Enable: %s",
|
||||
s_state.mode.xa_enable ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.secondary_status.playing_cdda ? active_color : inactive_color, "Playing CDDA: %s",
|
||||
s_state.secondary_status.playing_cdda ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.mode.double_speed ? active_color : inactive_color, "Double Speed: %s",
|
||||
s_state.mode.double_speed ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Columns(2);
|
||||
ImGui::SetColumnWidth(0, std::ceil(scale * 500.0f));
|
||||
|
||||
if (HasPendingCommand())
|
||||
{
|
||||
ImGui::TextColored(active_color, "Command: %s (0x%02X) (%d ticks remaining)", GetCommandName(s_state.command),
|
||||
static_cast<u8>(s_state.command),
|
||||
s_state.command_event.IsActive() ? s_state.command_event.GetTicksUntilNextExecution() : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextColored(inactive_color, "Command: None");
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Interrupt Enable Register: 0x%02X", s_state.interrupt_enable_register);
|
||||
|
||||
ImGui::NextColumn();
|
||||
const bool drive_idle = IsDriveIdle();
|
||||
if (drive_idle)
|
||||
{
|
||||
ImGui::TextColored(inactive_color, "Drive: Idle");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextColored(active_color, "Drive: %s (%d ticks remaining)",
|
||||
s_drive_state_names[static_cast<u8>(s_state.drive_state)],
|
||||
s_state.drive_event.IsActive() ? s_state.drive_event.GetTicksUntilNextExecution() : 0);
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Interrupt Flag Register: 0x%02X", s_state.interrupt_flag_register);
|
||||
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(drive_idle ? inactive_color : active_color, "Effective Drive Speed: %.2fx",
|
||||
(s_state.last_sector_read_ticks > 0) ? ((static_cast<float>(System::GetTicksPerSecond()) /
|
||||
static_cast<float>(SINGLE_SPEED_SECTORS_PER_SECOND)) /
|
||||
static_cast<float>(s_state.last_sector_read_ticks)) :
|
||||
0.0f);
|
||||
if (!drive_idle && g_settings.cdrom_read_speedup != 1 && !CanUseReadSpeedup())
|
||||
{
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), 200.0f));
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "SPEEDUP BLOCKED");
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
if (HasPendingAsyncInterrupt())
|
||||
{
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(inactive_color, "0x%02X pending", s_state.pending_async_interrupt);
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("CD Audio", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
if (s_state.drive_state == DriveState::Reading && s_state.mode.xa_enable)
|
||||
{
|
||||
ImGui::TextColored(active_color, "Playing: XA-ADPCM (File %u | Channel %u | %s | %s | %s)",
|
||||
s_state.xa_current_file_number, s_state.xa_current_channel_number,
|
||||
s_state.xa_current_codinginfo.IsStereo() ? "Stereo" : "Mono",
|
||||
s_state.xa_current_codinginfo.Is8BitADPCM() ? "8-bit" : "4-bit",
|
||||
s_state.xa_current_codinginfo.IsHalfSampleRate() ? "18900hz" : "37800hz");
|
||||
}
|
||||
else if (s_state.drive_state == DriveState::Playing)
|
||||
{
|
||||
ImGui::TextColored(active_color, "Playing: CDDA (Track %x)", s_state.last_subq.track_number_bcd);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextColored(inactive_color, "Playing: Inactive");
|
||||
}
|
||||
|
||||
ImGui::TextColored(s_state.muted ? inactive_color : active_color, "Muted: %s", s_state.muted ? "Yes" : "No");
|
||||
ImGui::Text("Left Output: Left Channel=%02X (%u%%), Right Channel=%02X (%u%%)",
|
||||
s_state.cd_audio_volume_matrix[0][0], ZeroExtend32(s_state.cd_audio_volume_matrix[0][0]) * 100 / 0x80,
|
||||
s_state.cd_audio_volume_matrix[1][0], ZeroExtend32(s_state.cd_audio_volume_matrix[1][0]) * 100 / 0x80);
|
||||
ImGui::Text("Right Output: Left Channel=%02X (%u%%), Right Channel=%02X (%u%%)",
|
||||
s_state.cd_audio_volume_matrix[0][1], ZeroExtend32(s_state.cd_audio_volume_matrix[0][1]) * 100 / 0x80,
|
||||
s_state.cd_audio_volume_matrix[1][1], ZeroExtend32(s_state.cd_audio_volume_matrix[1][1]) * 100 / 0x80);
|
||||
|
||||
ImGui::Text("Audio FIFO Size: %u frames", s_state.audio_fifo.GetSize());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
if (copy_state)
|
||||
ImGui::LogFinish();
|
||||
}
|
||||
+74
-60
@@ -26,6 +26,9 @@
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include "IconsEmoji.h"
|
||||
#include "IconsFontAwesome.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -988,71 +991,82 @@ TickCount DMA::TransferDeviceToMemory(u32 address, u32 increment, u32 word_count
|
||||
|
||||
void DMA::DrawDebugStateWindow(float scale)
|
||||
{
|
||||
static constexpr std::array column_names = {"#", "Req", "Addr", "Direction", "Chopping", "Mode",
|
||||
"Busy", "Enable", "Priority", "IRQ", "Flag"};
|
||||
static constexpr std::array sync_mode_names = {"Manual", "Request", "LinkedList", "Reserved"};
|
||||
static constexpr std::array column_names = {"Channel", "Request", "Address", "Direction",
|
||||
"Step", "Chop DMA/CPU", "Mode", "State",
|
||||
"Enabled", "Priority", "IRQ enable", "IRQ flag"};
|
||||
static constexpr std::array sync_mode_names = {"Manual", "Request", "Linked list", "Reserved"};
|
||||
|
||||
ImGui::Columns(static_cast<int>(column_names.size()));
|
||||
ImGui::SetColumnWidth(0, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(1, 50.0f * scale);
|
||||
ImGui::SetColumnWidth(2, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(3, 110.0f * scale);
|
||||
ImGui::SetColumnWidth(4, 100.0f * scale);
|
||||
ImGui::SetColumnWidth(5, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(6, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(7, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(8, 60.0f * scale);
|
||||
ImGui::SetColumnWidth(9, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(10, 80.0f * scale);
|
||||
const float left = ImGui::GetCursorPosX();
|
||||
const float button_width =
|
||||
ImGui::CalcTextSize(ICON_FA_COPY " Copy State").x + ImGui::GetStyle().FramePadding.x * 2.0f;
|
||||
ImGui::SetCursorPosX(left + ImGui::GetContentRegionAvail().x - button_width);
|
||||
|
||||
for (const char* title : column_names)
|
||||
const bool copy_state = ImGui::Button(ICON_FA_COPY " Copy State");
|
||||
if (copy_state)
|
||||
ImGui::LogToClipboard();
|
||||
|
||||
ImGui::SameLine(left);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("IRQ %s " ICON_EMOJI_MIDDLE_DOT " Master IRQ %s " ICON_EMOJI_MIDDLE_DOT " Bus error %s",
|
||||
s_state.DICR.master_flag ? "asserted" : "clear", s_state.DICR.master_enable ? "enabled" : "disabled",
|
||||
s_state.DICR.bus_error ? "asserted" : "clear");
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(8.0f * scale, ImGui::GetStyle().CellPadding.y));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(1.0f, 1.0f, 1.0f, 0.035f));
|
||||
if (ImGui::BeginTable("channels", static_cast<int>(column_names.size()),
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_PadOuterX | ImGuiTableFlags_SizingStretchProp))
|
||||
{
|
||||
ImGui::TextUnformatted(title);
|
||||
ImGui::NextColumn();
|
||||
for (const char* title : column_names)
|
||||
ImGui::TableSetupColumn(title);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
{
|
||||
const ChannelState& cs = s_state.channels[i];
|
||||
const Channel channel = static_cast<Channel>(i);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u %s", i, s_channel_names[i]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.request ? "Asserted" : "Clear");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%08X", cs.base_address);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.channel_control.copy_to_device ? "From RAM" : "To RAM");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.channel_control.address_step_reverse ? "-4" : "+4");
|
||||
ImGui::TableNextColumn();
|
||||
if (cs.channel_control.chopping_enable)
|
||||
{
|
||||
ImGui::Text("%u / %u cycles", 1u << cs.channel_control.chopping_dma_window_size.GetValue(),
|
||||
1u << cs.channel_control.chopping_cpu_window_size.GetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::TextUnformatted("Disabled");
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(sync_mode_names[static_cast<u8>(cs.channel_control.sync_mode.GetValue())]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s%s", cs.channel_control.enable_busy ? "Busy" : "Idle",
|
||||
cs.channel_control.start_trigger ? " (trigger)" : "");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.DPCR.GetMasterEnable(channel) ? "Enabled" : "Disabled");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u", s_state.DPCR.GetPriority(channel));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.DICR.GetIRQEnabled(channel) ? "Enabled" : "Disabled");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.DICR.GetIRQFlag(channel) ? "Asserted" : "Clear");
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
const ImVec4 active(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const ImVec4 inactive(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
{
|
||||
const ChannelState& cs = s_state.channels[i];
|
||||
|
||||
ImGui::TextColored(cs.channel_control.enable_busy ? active : inactive, "%u[%s]", i, s_channel_names[i]);
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(cs.request ? active : inactive, cs.request ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(cs.request ? active : inactive, "%08X", cs.base_address);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s%s", cs.channel_control.copy_to_device ? "FromRAM" : "ToRAM",
|
||||
cs.channel_control.address_step_reverse ? " Addr+" : " Addr-");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(cs.channel_control.chopping_enable ? active : inactive, "%s/%u/%u",
|
||||
cs.channel_control.chopping_enable ? "Yes" : "No",
|
||||
cs.channel_control.chopping_cpu_window_size.GetValue(),
|
||||
cs.channel_control.chopping_dma_window_size.GetValue());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", sync_mode_names[static_cast<u8>(cs.channel_control.sync_mode.GetValue())]);
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(cs.channel_control.enable_busy ? active : inactive, "%s%s",
|
||||
cs.channel_control.enable_busy ? "Busy" : "Idle",
|
||||
cs.channel_control.start_trigger ? " (Trigger)" : "");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.DPCR.GetMasterEnable(static_cast<Channel>(i)) ? active : inactive,
|
||||
s_state.DPCR.GetMasterEnable(static_cast<Channel>(i)) ? "Enabled" : "Disabled");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.DPCR.GetMasterEnable(static_cast<Channel>(i)) ? active : inactive, "%u",
|
||||
s_state.DPCR.GetPriority(static_cast<Channel>(i)));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.DICR.GetIRQEnabled(static_cast<Channel>(i)) ? active : inactive,
|
||||
s_state.DICR.GetIRQEnabled(static_cast<Channel>(i)) ? "Enabled" : "Disabled");
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(s_state.DICR.GetIRQFlag(static_cast<Channel>(i)) ? active : inactive,
|
||||
s_state.DICR.GetIRQFlag(static_cast<Channel>(i)) ? "IRQ" : "");
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
if (copy_state)
|
||||
ImGui::LogFinish();
|
||||
}
|
||||
|
||||
// Instantiate channel functions.
|
||||
|
||||
+227
-46
@@ -13,6 +13,7 @@
|
||||
#include "gpu_hw_texture_cache.h"
|
||||
#include "gpu_sw_rasterizer.h"
|
||||
#include "host.h"
|
||||
#include "imgui_overlays.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "performance_counters.h"
|
||||
#include "settings.h"
|
||||
@@ -46,6 +47,7 @@
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "IconsEmoji.h"
|
||||
#include "IconsFontAwesome.h"
|
||||
#include "fmt/format.h"
|
||||
#include "imgui.h"
|
||||
|
||||
@@ -2678,7 +2680,10 @@ void GPU::UpdateDisplay(bool submit_frame)
|
||||
submit_frame = (submit_frame && System::GetFramePresentationParameters(&frame));
|
||||
|
||||
GPUBackendUpdateDisplayCommand* cmd = GPUBackend::NewUpdateDisplayCommand();
|
||||
cmd->gpu_busy_pct = g_settings.display_show_gpu_stats ? UpdateOrGetGPUBusyPct() : 0;
|
||||
cmd->gpu_busy_pct =
|
||||
(g_settings.display_show_gpu_stats || ImGuiManager::IsGPUDebugWindowVisible(g_settings.debug_window_visibility)) ?
|
||||
UpdateOrGetGPUBusyPct() :
|
||||
0;
|
||||
if (!g_settings.gpu_show_vram) [[likely]]
|
||||
{
|
||||
FillUpdateDisplayCommand(cmd);
|
||||
@@ -4127,58 +4132,234 @@ void GPU::HandleCopyRectangleVRAMToVRAMCommand()
|
||||
|
||||
void GPU::DrawDebugStateWindow(float scale)
|
||||
{
|
||||
if (ImGui::CollapsingHeader("GPU", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
static constexpr std::array<const char*, 5> state_strings = {
|
||||
{"Idle", "Reading VRAM", "Writing VRAM", "Drawing Polyline"}};
|
||||
static constexpr const CRTCState& cs = s_locals.crtc_state;
|
||||
static constexpr std::array<const char*, 4> state_strings = {
|
||||
{"Idle", "Reading VRAM", "Writing VRAM", "Drawing Polyline"}};
|
||||
const char* clock = s_locals.console_is_pal ? (s_locals.GPUSTAT.pal_mode ? "PAL-on-PAL" : "NTSC-on-PAL") :
|
||||
(s_locals.GPUSTAT.pal_mode ? "PAL-on-NTSC" : "NTSC-on-NTSC");
|
||||
|
||||
ImGui::Text("State: %s", state_strings[static_cast<u8>(s_locals.blitter_state)]);
|
||||
ImGui::Text("Dither: %s", s_locals.GPUSTAT.dither_enable ? "Enabled" : "Disabled");
|
||||
ImGui::Text("Draw To Displayed Field: %s", s_locals.GPUSTAT.draw_to_displayed_field ? "Enabled" : "Disabled");
|
||||
ImGui::Text("Draw Set Mask Bit: %s", s_locals.GPUSTAT.set_mask_while_drawing ? "Yes" : "No");
|
||||
ImGui::Text("Draw To Masked Pixels: %s", s_locals.GPUSTAT.check_mask_before_draw ? "Yes" : "No");
|
||||
ImGui::Text("Reverse Flag: %s", s_locals.GPUSTAT.reverse_flag ? "Yes" : "No");
|
||||
ImGui::Text("Texture Page Upper Bank: %s", s_locals.GPUSTAT.texture_page_y_base_2 ? "Yes" : "No");
|
||||
ImGui::Text("PAL Mode: %s", s_locals.GPUSTAT.pal_mode ? "Yes" : "No");
|
||||
ImGui::Text("Interrupt Request: %s", s_locals.GPUSTAT.interrupt_request ? "Yes" : "No");
|
||||
ImGui::Text("DMA Request: %s", s_locals.GPUSTAT.dma_data_request ? "Yes" : "No");
|
||||
const char* standard = s_locals.GPUSTAT.pal_mode ? "PAL" : "NTSC";
|
||||
const char* scan_mode = s_locals.GPUSTAT.vertical_interlace ? (s_locals.GPUSTAT.pal_mode ? "576i" : "480i") :
|
||||
(s_locals.GPUSTAT.pal_mode ? "288p" : "240p");
|
||||
|
||||
const float left = ImGui::GetCursorPosX();
|
||||
const float button_width =
|
||||
ImGui::CalcTextSize(ICON_FA_COPY " Copy State").x + ImGui::GetStyle().FramePadding.x * 2.0f;
|
||||
ImGui::SetCursorPosX(left + ImGui::GetContentRegionAvail().x - button_width);
|
||||
const bool copy_state = ImGui::Button(ICON_FA_COPY " Copy State");
|
||||
if (copy_state)
|
||||
ImGui::LogToClipboard();
|
||||
|
||||
ImGui::SameLine(left);
|
||||
ImGui::PushFont(nullptr, std::ceil(18.0f * scale), 0.0f);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextWrapped("%s %s " ICON_EMOJI_MIDDLE_DOT " %u x %u " ICON_EMOJI_MIDDLE_DOT " %u-bit " ICON_EMOJI_MIDDLE_DOT
|
||||
" %.3f Hz " ICON_EMOJI_MIDDLE_DOT " %u%% Usage",
|
||||
standard, scan_mode, cs.display_width, cs.display_height,
|
||||
s_locals.GPUSTAT.display_area_color_depth_24 ? 24 : 15, ComputeVerticalFrequency(),
|
||||
s_locals.last_gpu_busy_pct);
|
||||
ImGui::PopFont();
|
||||
ImGui::Spacing();
|
||||
|
||||
constexpr ImGuiTableFlags table_flags =
|
||||
ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_PadOuterX | ImGuiTableFlags_RowBg;
|
||||
static constexpr auto label = [](const char* text) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImVec4 color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||
color.w *= 0.7f;
|
||||
ImGui::TextColored(color, "%s", text);
|
||||
ImGui::TableNextColumn();
|
||||
};
|
||||
|
||||
static constexpr auto flag = [](const char* text, bool value) {
|
||||
label(text);
|
||||
ImGui::TextUnformatted(value ? "Enabled" : "Disabled");
|
||||
};
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(10.0f * scale, ImGui::GetStyle().CellPadding.y));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(1.0f, 1.0f, 1.0f, 0.035f));
|
||||
if (!ImGui::BeginTable("layout", 2, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV))
|
||||
{
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
if (copy_state)
|
||||
ImGui::LogFinish();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("CRTC", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::SeparatorText("Drawing");
|
||||
if (ImGui::BeginTable("drawing", 2, table_flags))
|
||||
{
|
||||
const auto& cs = s_locals.crtc_state;
|
||||
ImGui::Text("Clock: %s", (s_locals.console_is_pal ? (s_locals.GPUSTAT.pal_mode ? "PAL-on-PAL" : "NTSC-on-PAL") :
|
||||
(s_locals.GPUSTAT.pal_mode ? "PAL-on-NTSC" : "NTSC-on-NTSC")));
|
||||
ImGui::Text("Horizontal Frequency: %.3f KHz", ComputeHorizontalFrequency() / 1000.0f);
|
||||
ImGui::Text("Vertical Frequency: %.3f Hz", ComputeVerticalFrequency());
|
||||
ImGui::Text("Dot Clock Divider: %u", cs.dot_clock_divider);
|
||||
ImGui::Text("Vertical Interlace: %s (%s field)", s_locals.GPUSTAT.vertical_interlace ? "Yes" : "No",
|
||||
cs.interlaced_field ? "odd" : "even");
|
||||
ImGui::Text("Current Scanline: %u (tick %u)", cs.current_scanline, cs.current_tick_in_scanline);
|
||||
ImGui::Text("Display Disable: %s", s_locals.GPUSTAT.display_disable ? "Yes" : "No");
|
||||
ImGui::Text("Displaying Odd Lines: %s", cs.active_line_lsb ? "Yes" : "No");
|
||||
ImGui::Text("Color Depth: %u-bit", s_locals.GPUSTAT.display_area_color_depth_24 ? 24 : 15);
|
||||
ImGui::Text("Start Offset in VRAM: (%u, %u)", cs.regs.X.GetValue(), cs.regs.Y.GetValue());
|
||||
ImGui::Text("Display Total: %u (%u) horizontal, %u vertical", cs.horizontal_total,
|
||||
cs.horizontal_total / cs.dot_clock_divider, cs.vertical_total);
|
||||
ImGui::Text("Configured Display Range: %u-%u (%u-%u), %u-%u", cs.regs.X1.GetValue(), cs.regs.X2.GetValue(),
|
||||
cs.regs.X1.GetValue() / cs.dot_clock_divider, cs.regs.X2.GetValue() / cs.dot_clock_divider,
|
||||
cs.regs.Y1.GetValue(), cs.regs.Y2.GetValue());
|
||||
ImGui::Text("Output Display Range: %u-%u (%u-%u), %u-%u", cs.horizontal_display_start, cs.horizontal_display_end,
|
||||
cs.horizontal_display_start / cs.dot_clock_divider, cs.horizontal_display_end / cs.dot_clock_divider,
|
||||
cs.vertical_display_start, cs.vertical_display_end);
|
||||
ImGui::Text("Cropping: %s", Settings::GetDisplayCropModeName(g_settings.display_crop_mode));
|
||||
ImGui::Text("Visible Display Range: %u-%u (%u-%u), %u-%u", cs.horizontal_visible_start, cs.horizontal_visible_end,
|
||||
cs.horizontal_visible_start / cs.dot_clock_divider, cs.horizontal_visible_end / cs.dot_clock_divider,
|
||||
cs.vertical_visible_start, cs.vertical_visible_end);
|
||||
ImGui::Text("Display Resolution: %ux%u", cs.display_width, cs.display_height);
|
||||
ImGui::Text("Display Origin: %u, %u", cs.display_origin_left, cs.display_origin_top);
|
||||
ImGui::Text("Displayed/Visible VRAM Portion: %ux%u @ (%u, %u)", cs.display_vram_width, cs.display_vram_height,
|
||||
cs.display_vram_left, cs.display_vram_top);
|
||||
ImGui::Text("Padding: Left=%d, Top=%d, Right=%d, Bottom=%d", cs.display_origin_left, cs.display_origin_top,
|
||||
label("Blitter state");
|
||||
ImGui::TextUnformatted(state_strings[static_cast<u8>(s_locals.blitter_state)]);
|
||||
label("Drawing area (inclusive)");
|
||||
ImGui::Text("(%u, %u) - (%u, %u)", s_locals.drawing_area.left, s_locals.drawing_area.top,
|
||||
s_locals.drawing_area.right, s_locals.drawing_area.bottom);
|
||||
label("Drawing offset");
|
||||
ImGui::Text("(%d, %d)", s_locals.drawing_offset.x, s_locals.drawing_offset.y);
|
||||
label("Semi-transparency");
|
||||
static constexpr std::array<const char*, 4> transparency_modes = {{"B/2 + F/2", "B + F", "B - F", "B + F/4"}};
|
||||
ImGui::TextUnformatted(transparency_modes[static_cast<u8>(s_locals.draw_mode_reg.transparency_mode.GetValue())]);
|
||||
flag("Dithering", s_locals.GPUSTAT.dither_enable);
|
||||
flag("Draw to displayed field", s_locals.GPUSTAT.draw_to_displayed_field);
|
||||
flag("Set mask bit", s_locals.GPUSTAT.set_mask_while_drawing);
|
||||
flag("Check mask bit", s_locals.GPUSTAT.check_mask_before_draw);
|
||||
flag("Reverse flag", s_locals.GPUSTAT.reverse_flag);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Texture");
|
||||
if (ImGui::BeginTable("texture", 2, table_flags))
|
||||
{
|
||||
label("Texture page");
|
||||
ImGui::Text("(%u, %u)", s_locals.draw_mode_reg.GetTexturePageBaseX(), s_locals.draw_mode_reg.GetTexturePageBaseY());
|
||||
flag("Texture page upper bank", s_locals.GPUSTAT.texture_page_y_base_2);
|
||||
label("Texture format");
|
||||
static constexpr std::array<const char*, 4> texture_modes = {
|
||||
{"4-bit indexed", "8-bit indexed", "16-bit direct", "Reserved (16-bit)"}};
|
||||
ImGui::TextUnformatted(texture_modes[static_cast<u8>(s_locals.draw_mode_reg.texture_mode.GetValue())]);
|
||||
label("CLUT");
|
||||
ImGui::Text("(%u, %u)", s_locals.texture_palette_reg.GetXBase(), s_locals.texture_palette_reg.GetYBase());
|
||||
label("Window AND (X / Y)");
|
||||
ImGui::Text("0x%02X / 0x%02X", s_locals.texture_window.and_x, s_locals.texture_window.and_y);
|
||||
label("Window OR (X / Y)");
|
||||
ImGui::Text("0x%02X / 0x%02X", s_locals.texture_window.or_x, s_locals.texture_window.or_y);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Timing");
|
||||
if (ImGui::BeginTable("timing", 2, table_flags))
|
||||
{
|
||||
label("Clock");
|
||||
ImGui::TextUnformatted(clock);
|
||||
flag("PAL mode", s_locals.GPUSTAT.pal_mode);
|
||||
label("Horizontal frequency");
|
||||
ImGui::Text("%.3f kHz", ComputeHorizontalFrequency() / 1000.0f);
|
||||
label("Vertical frequency");
|
||||
ImGui::Text("%.3f Hz", ComputeVerticalFrequency());
|
||||
label("Dot clock divider");
|
||||
ImGui::Text("%u", cs.dot_clock_divider);
|
||||
label("Horizontal total");
|
||||
ImGui::Text("%u ticks (%u dots)", cs.horizontal_total, cs.horizontal_total / cs.dot_clock_divider);
|
||||
label("Vertical total");
|
||||
ImGui::Text("%u lines", cs.vertical_total);
|
||||
flag("Interlace", s_locals.GPUSTAT.vertical_interlace);
|
||||
label("Current field");
|
||||
ImGui::TextUnformatted(cs.interlaced_field ? "Odd" : "Even");
|
||||
label("Current scanline");
|
||||
ImGui::Text("%u (tick %u)", cs.current_scanline, cs.current_tick_in_scanline);
|
||||
label("Displaying odd lines");
|
||||
ImGui::TextUnformatted(cs.active_line_lsb ? "Yes" : "No");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SeparatorText("Signals");
|
||||
if (ImGui::BeginTable("signals", 2, table_flags))
|
||||
{
|
||||
label("Interrupt request");
|
||||
ImGui::TextUnformatted(s_locals.GPUSTAT.interrupt_request ? "Asserted" : "Clear");
|
||||
label("DMA request");
|
||||
ImGui::TextUnformatted(s_locals.GPUSTAT.dma_data_request ? "Asserted" : "Clear");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Display");
|
||||
if (ImGui::BeginTable("display", 2, table_flags))
|
||||
{
|
||||
flag("Display output", !s_locals.GPUSTAT.display_disable);
|
||||
label("Color depth");
|
||||
ImGui::Text("%u-bit", s_locals.GPUSTAT.display_area_color_depth_24 ? 24 : 15);
|
||||
label("VRAM start");
|
||||
ImGui::Text("(%u, %u)", cs.regs.X.GetValue(), cs.regs.Y.GetValue());
|
||||
label("Resolution");
|
||||
ImGui::Text("%u x %u", cs.display_width, cs.display_height);
|
||||
label("Cropping");
|
||||
ImGui::TextUnformatted(Settings::GetDisplayCropModeName(g_settings.display_crop_mode));
|
||||
label("Display origin");
|
||||
ImGui::Text("(%u, %u)", cs.display_origin_left, cs.display_origin_top);
|
||||
label("Visible VRAM portion");
|
||||
ImGui::Text("%u x %u at (%u, %u)", cs.display_vram_width, cs.display_vram_height, cs.display_vram_left,
|
||||
cs.display_vram_top);
|
||||
label("Padding (L / T / R / B)");
|
||||
ImGui::Text("%u / %u / %d / %d", cs.display_origin_left, cs.display_origin_top,
|
||||
cs.display_width - cs.display_vram_width - cs.display_origin_left,
|
||||
cs.display_height - cs.display_vram_height - cs.display_origin_top);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
const GSVector4i configured(cs.regs.X1.GetValue(), cs.regs.Y1.GetValue(), cs.regs.X2.GetValue(),
|
||||
cs.regs.Y2.GetValue());
|
||||
const GSVector4i output(cs.horizontal_display_start, cs.vertical_display_start, cs.horizontal_display_end,
|
||||
cs.vertical_display_end);
|
||||
const GSVector4i visible(cs.horizontal_visible_start, cs.vertical_visible_start, cs.horizontal_visible_end,
|
||||
cs.vertical_visible_end);
|
||||
ImGui::SeparatorText("Display ranges");
|
||||
if (ImGui::BeginTable("ranges", 4, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Range");
|
||||
ImGui::TableSetupColumn("H ticks");
|
||||
ImGui::TableSetupColumn("H dots");
|
||||
ImGui::TableSetupColumn("V lines");
|
||||
ImGui::TableHeadersRow();
|
||||
static constexpr auto range_row = [](const char* text, const GSVector4i& rect) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(text);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d-%d", rect.x, rect.z);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u-%u", rect.x / cs.dot_clock_divider, rect.z / cs.dot_clock_divider);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d-%d", rect.y, rect.w);
|
||||
};
|
||||
range_row("Configured", configured);
|
||||
range_row("Output", output);
|
||||
range_row("Visible", visible);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (copy_state)
|
||||
ImGui::LogFinish();
|
||||
|
||||
{
|
||||
ImGui::Spacing();
|
||||
|
||||
ImDrawList* const dl = ImGui::GetWindowDrawList();
|
||||
const ImVec2 origin = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 size(ImGui::GetContentRegionAvail().x, 140.0f * scale);
|
||||
dl->AddRectFilled(origin, origin + size, ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
static constexpr auto draw_range = [](ImDrawList* const dl, float scale, const ImVec2& origin, const ImVec2& size,
|
||||
const GSVector4i& rect, ImU32 color, float thickness) {
|
||||
const ImVec2 start(origin.x + size.x * std::clamp(static_cast<float>(rect.x) / cs.horizontal_total, 0.0f, 1.0f),
|
||||
origin.y + size.y * std::clamp(static_cast<float>(rect.y) / cs.vertical_total, 0.0f, 1.0f));
|
||||
const ImVec2 end(origin.x + size.x * std::clamp(static_cast<float>(rect.z) / cs.horizontal_total, 0.0f, 1.0f),
|
||||
origin.y + size.y * std::clamp(static_cast<float>(rect.w) / cs.vertical_total, 0.0f, 1.0f));
|
||||
dl->AddRect(start, end, color, 0.0f, thickness * scale);
|
||||
};
|
||||
|
||||
static constexpr ImU32 configured_color = IM_COL32(234, 190, 100, 255);
|
||||
static constexpr ImU32 output_color = IM_COL32(110, 175, 235, 255);
|
||||
static constexpr ImU32 visible_color = IM_COL32(120, 215, 165, 255);
|
||||
|
||||
// Different outline widths keep coincident ranges visible.
|
||||
draw_range(dl, scale, origin, size, output, output_color, 5.0f);
|
||||
draw_range(dl, scale, origin, size, configured, configured_color, 3.0f);
|
||||
draw_range(dl, scale, origin, size, visible, visible_color, 1.0f);
|
||||
|
||||
ImGui::Dummy(size);
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(configured_color), "Configured");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(output_color), "Output");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(visible_color), "Visible");
|
||||
}
|
||||
ImGui::EndTable();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
GPUDump::Recorder* GPU::GetGPUDump()
|
||||
|
||||
@@ -112,12 +112,12 @@ static void UpdateInputOverlay(void* buffer);
|
||||
static constexpr size_t NUM_DEBUG_WINDOWS = 7;
|
||||
static constexpr const std::array<DebugWindowInfo, NUM_DEBUG_WINDOWS> s_debug_window_info = {{
|
||||
{"Freecam", "Free Camera", ":icons/applications-system.png", >E::DrawFreecamWindow, 510, 500},
|
||||
{"SPU", "SPU State", ":icons/applications-system.png", &SPU::DrawDebugStateWindow, 820, 950},
|
||||
{"CDROM", "CD-ROM State", ":icons/applications-system.png", &CDROM::DrawDebugWindow, 820, 555},
|
||||
{"GPU", "GPU State", ":icons/applications-system.png", &GPU::DrawDebugStateWindow, 450, 550},
|
||||
{"DMA", "DMA State", ":icons/applications-system.png", &DMA::DrawDebugStateWindow, 860, 180},
|
||||
{"MDEC", "MDEC State", ":icons/applications-system.png", &MDEC::DrawDebugStateWindow, 300, 350},
|
||||
{"Timers", "Timers State", ":icons/applications-system.png", &Timers::DrawDebugStateWindow, 800, 95},
|
||||
{"SPU", "SPU State", ":icons/applications-system.png", &SPU::DrawDebugStateWindow, 1100, 745},
|
||||
{"CDROM", "CD-ROM State", ":icons/applications-system.png", &CDROM::DrawDebugWindow, 1050, 650},
|
||||
{"GPU", "GPU State", ":icons/applications-system.png", &GPU::DrawDebugStateWindow, 1000, 650},
|
||||
{"DMA", "DMA State", ":icons/applications-system.png", &DMA::DrawDebugStateWindow, 1160, 215},
|
||||
{"MDEC", "MDEC State", ":icons/applications-system.png", &MDEC::DrawDebugStateWindow, 900, 370},
|
||||
{"Timers", "Timers State", ":icons/applications-system.png", &Timers::DrawDebugStateWindow, 800, 315},
|
||||
}};
|
||||
static std::array<ImGuiManager::AuxiliaryRenderWindowState, NUM_DEBUG_WINDOWS> s_debug_window_state = {};
|
||||
|
||||
@@ -149,11 +149,21 @@ void ImGuiManager::SaveDebugWindowVisibility(SettingsInterface& si, u8 debug_win
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiManager::IsCDROMDebugWindowVisible(u8 debug_window_visibility)
|
||||
{
|
||||
return ((debug_window_visibility & (1u << 2)) != 0);
|
||||
}
|
||||
|
||||
bool ImGuiManager::IsSPUDebugWindowVisible(u8 debug_window_visibility)
|
||||
{
|
||||
return ((debug_window_visibility & (1u << 1)) != 0);
|
||||
}
|
||||
|
||||
bool ImGuiManager::IsGPUDebugWindowVisible(u8 debug_window_visibility)
|
||||
{
|
||||
return ((debug_window_visibility & (1u << 3)) != 0);
|
||||
}
|
||||
|
||||
bool ImGuiManager::UpdateDebugWindowConfig(u8 debug_window_visibility)
|
||||
{
|
||||
bool was_changed = false;
|
||||
|
||||
@@ -19,7 +19,9 @@ void UpdateInputOverlay();
|
||||
void RenderTextOverlays(const GPUBackend* gpu);
|
||||
u8 LoadDebugWindowVisibility(const SettingsInterface& si);
|
||||
void SaveDebugWindowVisibility(SettingsInterface& si, u8 debug_window_visibility);
|
||||
bool IsCDROMDebugWindowVisible(u8 debug_window_visibility);
|
||||
bool IsSPUDebugWindowVisible(u8 debug_window_visibility);
|
||||
bool IsGPUDebugWindowVisible(u8 debug_window_visibility);
|
||||
void RenderDebugWindows();
|
||||
bool UpdateDebugWindowConfig(u8 debug_window_visibility);
|
||||
void DestroyAllDebugWindows();
|
||||
|
||||
+125
-28
@@ -16,9 +16,11 @@
|
||||
#include "common/gsvector.h"
|
||||
#include "common/log.h"
|
||||
|
||||
#include "IconsFontAwesome.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
LOG_CHANNEL(MDEC);
|
||||
@@ -1254,37 +1256,132 @@ void MDEC::SetScaleMatrix(const u16* values)
|
||||
|
||||
void MDEC::DrawDebugStateWindow(float scale)
|
||||
{
|
||||
static constexpr std::array<const char*, 5> state_names = {
|
||||
{"None", "Decoding Macroblock", "Writing Macroblock", "SetIqTab", "SetScale"}};
|
||||
static constexpr std::array<const char*, 6> state_names = {{"Idle", "Decoding macroblock", "Writing macroblock",
|
||||
"Loading quantization table", "Loading scale table",
|
||||
"No command"}};
|
||||
static constexpr std::array<const char*, 4> output_depths = {{"4-bit", "8-bit", "24-bit", "15-bit"}};
|
||||
static constexpr std::array<const char*, 7> block_names = {{"Crblk", "Cbblk", "Y1", "Y2", "Y3", "Y4", "Output"}};
|
||||
static constexpr std::array<const char*, 7> block_names = {{"Cr", "Cb", "Y1", "Y2", "Y3", "Y4", "Output"}};
|
||||
|
||||
#if defined(_DEBUG) || defined(_DEVEL)
|
||||
ImGui::Text("Blocks Decoded: %u", s_state.total_blocks_decoded);
|
||||
ImGui::Text("IDCT Fast Path: %u/%u (%.1f%%)", s_state.fast_idct_blocks, s_state.total_idct_blocks,
|
||||
s_state.total_idct_blocks ? (static_cast<double>(s_state.fast_idct_blocks) * 100.0 /
|
||||
static_cast<double>(s_state.total_idct_blocks)) :
|
||||
0.0);
|
||||
#endif
|
||||
ImGui::Text("Data-In FIFO Size: %u (%u bytes)", s_state.data_in_fifo.GetSize(), s_state.data_in_fifo.GetSize() * 4);
|
||||
ImGui::Text("Data-Out FIFO Size: %u (%u bytes)", s_state.data_out_fifo.GetSize(),
|
||||
s_state.data_out_fifo.GetSize() * 4);
|
||||
ImGui::Text("DMA Enable: %s%s", s_state.enable_dma_in ? "In " : "", s_state.enable_dma_out ? "Out" : "");
|
||||
ImGui::Text("Current State: %s", state_names[static_cast<u8>(s_state.state)]);
|
||||
ImGui::Text("Current Block: %s", block_names[s_state.current_block]);
|
||||
ImGui::Text("Current Coefficient: %u", s_state.current_coefficient);
|
||||
const float left = ImGui::GetCursorPosX();
|
||||
const float button_width =
|
||||
ImGui::CalcTextSize(ICON_FA_COPY " Copy State").x + ImGui::GetStyle().FramePadding.x * 2.0f;
|
||||
ImGui::SetCursorPosX(left + ImGui::GetContentRegionAvail().x - button_width);
|
||||
const bool copy_state = ImGui::Button(ICON_FA_COPY " Copy State");
|
||||
if (copy_state)
|
||||
ImGui::LogToClipboard();
|
||||
ImGui::SameLine(left);
|
||||
ImGui::PushFont(nullptr, std::ceil(18.0f * scale), 0.0f);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(state_names[static_cast<u8>(s_state.state)]);
|
||||
ImGui::PopFont();
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::CollapsingHeader("Status", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
constexpr ImGuiTableFlags table_flags =
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_PadOuterX | ImGuiTableFlags_SizingStretchSame;
|
||||
static constexpr auto label = [](const char* text) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImVec4 color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||
color.w *= 0.7f;
|
||||
ImGui::TextColored(color, "%s", text);
|
||||
ImGui::TableNextColumn();
|
||||
};
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(10.0f * scale, ImGui::GetStyle().CellPadding.y));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(1.0f, 1.0f, 1.0f, 0.035f));
|
||||
if (ImGui::BeginTable("layout", 2, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV))
|
||||
{
|
||||
ImGui::Text("Data-Out FIFO Empty: %s", s_state.status.data_out_fifo_empty ? "Yes" : "No");
|
||||
ImGui::Text("Data-In FIFO Full: %s", s_state.status.data_in_fifo_full ? "Yes" : "No");
|
||||
ImGui::Text("Command Busy: %s", s_state.status.command_busy ? "Yes" : "No");
|
||||
ImGui::Text("Data-In Request: %s", s_state.status.data_in_request ? "Yes" : "No");
|
||||
ImGui::Text("Output Depth: %s", output_depths[static_cast<u8>(s_state.status.data_output_depth.GetValue())]);
|
||||
ImGui::Text("Output Signed: %s", s_state.status.data_output_signed ? "Yes" : "No");
|
||||
ImGui::Text("Output Bit 15: %u", ZeroExtend32(s_state.status.data_output_bit15.GetValue()));
|
||||
ImGui::Text("Current Block: %u", ZeroExtend32(s_state.status.current_block.GetValue()));
|
||||
ImGui::Text("Parameter Words Remaining: %d",
|
||||
static_cast<s32>(SignExtend32(s_state.status.parameter_words_remaining.GetValue())));
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::SeparatorText("Decoder");
|
||||
if (ImGui::BeginTable("decoder", 2, table_flags))
|
||||
{
|
||||
label("Block");
|
||||
ImGui::TextUnformatted(block_names[s_state.current_block]);
|
||||
label("Coefficient");
|
||||
ImGui::Text("%u", s_state.current_coefficient);
|
||||
label("Q scale");
|
||||
ImGui::Text("%u", s_state.current_q_scale);
|
||||
label("Parameter halfwords");
|
||||
ImGui::Text("%u", s_state.remaining_halfwords);
|
||||
label("Command busy");
|
||||
ImGui::TextUnformatted(s_state.status.command_busy ? "Yes" : "No");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("FIFO");
|
||||
if (ImGui::BeginTable("fifo", 2, table_flags))
|
||||
{
|
||||
label("Input");
|
||||
ImGui::TextWrapped("%u / %u halfwords (%u bytes)", s_state.data_in_fifo.GetSize(),
|
||||
s_state.data_in_fifo.GetCapacity(), s_state.data_in_fifo.GetSize() * 2);
|
||||
label("Output");
|
||||
ImGui::TextWrapped("%u / %u words (%u bytes)", s_state.data_out_fifo.GetSize(),
|
||||
s_state.data_out_fifo.GetCapacity(), s_state.data_out_fifo.GetSize() * 4);
|
||||
label("Input full");
|
||||
ImGui::TextUnformatted(s_state.status.data_in_fifo_full ? "Yes" : "No");
|
||||
label("Output empty");
|
||||
ImGui::TextUnformatted(s_state.status.data_out_fifo_empty ? "Yes" : "No");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
#if defined(_DEBUG) || defined(_DEVEL)
|
||||
ImGui::SeparatorText("Statistics");
|
||||
if (ImGui::BeginTable("statistics", 2, table_flags))
|
||||
{
|
||||
label("Blocks decoded");
|
||||
ImGui::Text("%u", s_state.total_blocks_decoded);
|
||||
label("IDCT fast path");
|
||||
ImGui::TextWrapped("%u / %u (%.1f%%)", s_state.fast_idct_blocks, s_state.total_idct_blocks,
|
||||
s_state.total_idct_blocks ? (static_cast<double>(s_state.fast_idct_blocks) * 100.0 /
|
||||
static_cast<double>(s_state.total_idct_blocks)) :
|
||||
0.0);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SeparatorText("Output");
|
||||
if (ImGui::BeginTable("output", 2, table_flags))
|
||||
{
|
||||
label("Depth");
|
||||
ImGui::TextUnformatted(output_depths[static_cast<u8>(s_state.status.data_output_depth.GetValue())]);
|
||||
label("Signed");
|
||||
ImGui::TextUnformatted(s_state.status.data_output_signed ? "Yes" : "No");
|
||||
label("Bit 15");
|
||||
ImGui::Text("%u", ZeroExtend32(s_state.status.data_output_bit15.GetValue()));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("DMA");
|
||||
if (ImGui::BeginTable("dma", 2, table_flags))
|
||||
{
|
||||
label("Input");
|
||||
ImGui::TextUnformatted(s_state.enable_dma_in ? "Enabled" : "Disabled");
|
||||
label("Output");
|
||||
ImGui::TextUnformatted(s_state.enable_dma_out ? "Enabled" : "Disabled");
|
||||
label("Input request");
|
||||
ImGui::TextUnformatted(s_state.status.data_in_request ? "Asserted" : "Clear");
|
||||
label("Output request");
|
||||
ImGui::TextUnformatted(s_state.status.data_out_request ? "Asserted" : "Clear");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Status register");
|
||||
if (ImGui::BeginTable("status", 2, table_flags))
|
||||
{
|
||||
label("Value");
|
||||
ImGui::Text("%08X", s_state.status.bits);
|
||||
label("Block");
|
||||
ImGui::Text("%u", ZeroExtend32(s_state.status.current_block.GetValue()));
|
||||
label("Parameter words - 1");
|
||||
ImGui::Text("%u", ZeroExtend32(s_state.status.parameter_words_remaining.GetValue()));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
if (copy_state)
|
||||
ImGui::LogFinish();
|
||||
}
|
||||
+360
-281
@@ -25,9 +25,11 @@
|
||||
#include "common/path.h"
|
||||
|
||||
#include "IconsEmoji.h"
|
||||
#include "IconsFontAwesome.h"
|
||||
#include "fmt/format.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
LOG_CHANNEL(SPU);
|
||||
@@ -2858,307 +2860,384 @@ static constexpr const char* GetReverbModeName(const SPU::ReverbRegisters& rever
|
||||
|
||||
void SPU::DrawDebugStateWindow(float scale)
|
||||
{
|
||||
static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f};
|
||||
ImVec4 inactive_color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||
inactive_color.w *= 0.5f;
|
||||
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
const auto draw_vu_meter = [&scale](s16* peaks) {
|
||||
const auto draw_vu_meter = [scale](s16* peaks) {
|
||||
constexpr s32 num_sections = 12;
|
||||
constexpr s32 amp_per_section = 32767 / num_sections;
|
||||
const s32 lidx = peaks[0] / amp_per_section;
|
||||
const s32 ridx = peaks[1] / amp_per_section;
|
||||
|
||||
const ImVec2 section_size = ImVec2(std::floor(scale * 8.0f), std::floor(scale * 4.0f));
|
||||
const float divider_size = std::floor(scale * 1.0f);
|
||||
ImVec2 left_start = ImGui::GetCursorScreenPos() + ImVec2(0.0f, std::floor(scale * 4.0f));
|
||||
ImVec2 right_start = left_start + ImVec2(0.0f, section_size.y + divider_size);
|
||||
for (s32 i = 0; i < num_sections; i++)
|
||||
const float width = std::max(1.0f, std::min(ImGui::GetContentRegionAvail().x, 108.0f * scale));
|
||||
const float gap = std::min(scale, width / (num_sections * 2));
|
||||
const ImVec2 section_size((width - gap * (num_sections - 1)) / num_sections, 4.0f * scale);
|
||||
const ImVec2 origin = ImGui::GetCursorScreenPos();
|
||||
const float offset_y = std::max(0.0f, (ImGui::GetTextLineHeight() - section_size.y * 2.0f - gap) * 0.5f);
|
||||
for (u32 channel = 0; channel < 2; channel++)
|
||||
{
|
||||
u32 left_color = IM_COL32(30, 30, 30, 255);
|
||||
if (peaks[0] > 0 && lidx >= i)
|
||||
const s32 level = peaks[channel] / amp_per_section;
|
||||
ImVec2 pos(origin.x, origin.y + offset_y + channel * (section_size.y + gap));
|
||||
for (s32 section = 0; section < num_sections; section++)
|
||||
{
|
||||
left_color = IM_COL32(255, 0, 0, 255);
|
||||
left_color = (i <= 8) ? IM_COL32(255, 255, 0, 255) : left_color;
|
||||
left_color = (i <= 6) ? IM_COL32(0, 255, 0, 255) : left_color;
|
||||
const ImU32 color = (peaks[channel] > 0 && level >= section) ? (section <= 6 ? IM_COL32(0, 220, 80, 255) :
|
||||
section <= 8 ? IM_COL32(240, 200, 60, 255) :
|
||||
IM_COL32(240, 70, 60, 255)) :
|
||||
IM_COL32(30, 30, 30, 255);
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + section_size, color);
|
||||
pos.x += section_size.x + gap;
|
||||
}
|
||||
u32 right_color = IM_COL32(30, 30, 30, 255);
|
||||
if (peaks[1] > 0 && ridx >= i)
|
||||
{
|
||||
right_color = IM_COL32(255, 0, 0, 255);
|
||||
right_color = (i <= 8) ? IM_COL32(255, 255, 0, 255) : right_color;
|
||||
right_color = (i <= 6) ? IM_COL32(0, 255, 0, 255) : right_color;
|
||||
}
|
||||
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(left_start, left_start + section_size, left_color);
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(right_start, right_start + section_size, right_color);
|
||||
left_start.x += section_size.x + divider_size;
|
||||
right_start.x += section_size.x + divider_size;
|
||||
peaks[channel] = 0;
|
||||
}
|
||||
|
||||
peaks[0] = 0;
|
||||
peaks[1] = 0;
|
||||
ImGui::Dummy(ImVec2(width, ImGui::GetTextLineHeight()));
|
||||
};
|
||||
#endif
|
||||
|
||||
// status
|
||||
if (ImGui::CollapsingHeader("Status", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
const ImVec2 content_pos = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 content_size = ImGui::GetContentRegionAvail();
|
||||
const float content_height = std::max(1.0f, content_size.y - ImGui::GetFrameHeightWithSpacing());
|
||||
const float button_width =
|
||||
ImGui::CalcTextSize(ICON_FA_COPY " Copy State").x + ImGui::GetStyle().FramePadding.x * 2.0f;
|
||||
// Submit the footer first so clipboard logging can capture both tabs on the same frame.
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x, content_pos.y + content_height + ImGui::GetStyle().ItemSpacing.y));
|
||||
if (ImGui::Button("Key Off All Voices"))
|
||||
{
|
||||
static constexpr std::array<const char*, 4> transfer_modes = {
|
||||
{"Transfer Stopped", "Manual Write", "DMA Write", "DMA Read"}};
|
||||
const std::array<float, 6> offsets = {
|
||||
{100.0f * scale, 200.0f * scale, 300.0f * scale, 420.0f * scale, 500.0f * scale, 600.0f * scale}};
|
||||
|
||||
ImGui::Text("Control: ");
|
||||
ImGui::SameLine(offsets[0]);
|
||||
ImGui::TextColored(s_state.SPUCNT.enable ? active_color : inactive_color, "SPU Enable");
|
||||
ImGui::SameLine(offsets[1]);
|
||||
ImGui::TextColored(s_state.SPUCNT.mute_n ? inactive_color : active_color, "Mute SPU");
|
||||
ImGui::SameLine(offsets[2]);
|
||||
ImGui::TextColored(s_state.SPUCNT.external_audio_enable ? active_color : inactive_color, "External Audio");
|
||||
ImGui::SameLine(offsets[3]);
|
||||
ImGui::TextColored(s_state.SPUCNT.ram_transfer_mode != RAMTransferMode::Stopped ? active_color : inactive_color,
|
||||
"%s", transfer_modes[static_cast<u8>(s_state.SPUCNT.ram_transfer_mode.GetValue())]);
|
||||
|
||||
ImGui::Text("Status: ");
|
||||
ImGui::SameLine(offsets[0]);
|
||||
ImGui::TextColored(s_state.SPUSTAT.irq9_flag ? active_color : inactive_color, "IRQ9");
|
||||
ImGui::SameLine(offsets[1]);
|
||||
ImGui::TextColored(s_state.SPUSTAT.dma_request ? active_color : inactive_color, "DMA Request");
|
||||
ImGui::SameLine(offsets[2]);
|
||||
ImGui::TextColored(s_state.SPUSTAT.dma_read_request ? active_color : inactive_color, "DMA Read");
|
||||
ImGui::SameLine(offsets[3]);
|
||||
ImGui::TextColored(s_state.SPUSTAT.dma_write_request ? active_color : inactive_color, "DMA Write");
|
||||
ImGui::SameLine(offsets[4]);
|
||||
ImGui::TextColored(s_state.SPUSTAT.transfer_busy ? active_color : inactive_color, "Transfer Busy");
|
||||
ImGui::SameLine(offsets[5]);
|
||||
ImGui::TextColored(s_state.SPUSTAT.second_half_capture_buffer ? active_color : inactive_color,
|
||||
"Second Capture Buffer");
|
||||
|
||||
ImGui::Text("Interrupt: ");
|
||||
ImGui::SameLine(offsets[0]);
|
||||
ImGui::TextColored(s_state.SPUCNT.irq9_enable ? active_color : inactive_color,
|
||||
s_state.SPUCNT.irq9_enable ? "Enabled @ 0x%04X (actual 0x%08X)" :
|
||||
"Disabled @ 0x%04X (actual 0x%08X)",
|
||||
s_state.irq_address, (ZeroExtend32(s_state.irq_address) * 8) & RAM_MASK);
|
||||
|
||||
ImGui::Text("Volume: ");
|
||||
ImGui::SameLine(offsets[0]);
|
||||
ImGui::Text("Left: %.1f%%", ApplyVolumeF(100.0f, s_state.main_volume_left.current_level));
|
||||
ImGui::SameLine(offsets[1]);
|
||||
ImGui::Text("Right: %.1f%%", ApplyVolumeF(100.0f, s_state.main_volume_right.current_level));
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::SameLine(offsets[2]);
|
||||
draw_vu_meter(s_state.output_peaks);
|
||||
ImGui::NewLine();
|
||||
#endif
|
||||
|
||||
ImGui::Text("CD Audio: ");
|
||||
ImGui::SameLine(offsets[0]);
|
||||
ImGui::TextColored(s_state.SPUCNT.cd_audio_enable ? active_color : inactive_color,
|
||||
s_state.SPUCNT.cd_audio_enable ? "Enabled" : "Disabled");
|
||||
ImGui::SameLine(offsets[1]);
|
||||
ImGui::TextColored(s_state.SPUCNT.cd_audio_enable ? active_color : inactive_color, "Left Volume: %.1f%%",
|
||||
ApplyVolumeF(100.0f, s_state.cd_audio_volume_left));
|
||||
ImGui::SameLine(offsets[3]);
|
||||
ImGui::TextColored(s_state.SPUCNT.cd_audio_enable ? active_color : inactive_color, "Right Volume: %.1f%%",
|
||||
ApplyVolumeF(100.0f, s_state.cd_audio_volume_right));
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::SameLine(offsets[5]);
|
||||
draw_vu_meter(s_state.cd_audio_peaks);
|
||||
ImGui::NewLine();
|
||||
#endif
|
||||
|
||||
ImGui::Text("Transfer FIFO: ");
|
||||
ImGui::SameLine(offsets[0]);
|
||||
ImGui::TextColored(s_state.transfer_event.IsActive() ? active_color : inactive_color, "%u halfwords (%u bytes)",
|
||||
s_state.transfer_fifo.GetSize(), s_state.transfer_fifo.GetSize() * 2);
|
||||
}
|
||||
|
||||
// draw voice states
|
||||
if (ImGui::CollapsingHeader("Voice State", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
static constexpr std::array column_titles = {
|
||||
"#", "StartAddr", "RepeatAddr", "CurAddr", "SampleIdx", "SampleRate",
|
||||
"VolLeft", "VolRight", "ADSRPhase", "ADSRVol", "ADSRTicks",
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
"VUMeter",
|
||||
#endif
|
||||
};
|
||||
static constexpr std::array adsr_phases = {"Off", "Attack", "Decay", "Sustain", "Release"};
|
||||
|
||||
ImGui::Columns(static_cast<int>(column_titles.size()));
|
||||
|
||||
// headers
|
||||
for (const char* column_title : column_titles)
|
||||
for (Voice& voice : s_state.voices)
|
||||
{
|
||||
ImGui::TextUnformatted(column_title);
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
|
||||
// states
|
||||
for (u32 voice_index = 0; voice_index < NUM_VOICES; voice_index++)
|
||||
{
|
||||
const Voice& v = s_state.voices[voice_index];
|
||||
ImVec4 color = v.IsOn() ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
ImGui::TextColored(color, "%u", ZeroExtend32(voice_index));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%04X", ZeroExtend32(v.regs.adpcm_start_address));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%04X", ZeroExtend32(v.regs.adpcm_repeat_address));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%04X", ZeroExtend32(v.current_address));
|
||||
ImGui::NextColumn();
|
||||
if (IsVoiceNoiseEnabled(voice_index))
|
||||
ImGui::TextColored(color, "NOISE");
|
||||
else
|
||||
ImGui::TextColored(color, "%u", ZeroExtend32(v.counter.sample_index.GetValue()));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%.2f", (float(v.regs.adpcm_sample_rate) / 4096.0f) * 44100.0f);
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%.1f%%", ApplyVolumeF(100.0f, v.left_volume.current_level));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%.1f%%", ApplyVolumeF(100.0f, v.right_volume.current_level));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%s", adsr_phases[static_cast<u8>(v.adsr_phase)]);
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%.1f%%", ApplyVolumeF(100.0f, v.regs.adsr_volume));
|
||||
ImGui::NextColumn();
|
||||
ImGui::TextColored(color, "%d", v.adsr_envelope.counter);
|
||||
ImGui::NextColumn();
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
draw_vu_meter(s_state.voice_peaks[voice_index]);
|
||||
ImGui::NextColumn();
|
||||
#endif
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Reverb", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::TextColored(s_state.SPUCNT.reverb_master_enable ? active_color : inactive_color, "Master Enable: %s",
|
||||
s_state.SPUCNT.reverb_master_enable ? "Yes" : "No");
|
||||
ImGui::Text("Voices Enabled: ");
|
||||
|
||||
for (u32 i = 0; i < NUM_VOICES; i++)
|
||||
{
|
||||
ImGui::SameLine(0.0f, 16.0f);
|
||||
|
||||
const bool active = IsVoiceReverbEnabled(i);
|
||||
ImGui::TextColored(active ? active_color : inactive_color, "%u", i);
|
||||
}
|
||||
|
||||
ImGui::TextColored(s_state.SPUCNT.cd_audio_reverb ? active_color : inactive_color, "CD Audio Enable: %s",
|
||||
s_state.SPUCNT.cd_audio_reverb ? "Yes" : "No");
|
||||
|
||||
ImGui::TextColored(s_state.SPUCNT.external_audio_reverb ? active_color : inactive_color,
|
||||
"External Audio Enable: %s", s_state.SPUCNT.external_audio_reverb ? "Yes" : "No");
|
||||
|
||||
ImGui::Text("Base Address: 0x%08X (%04X)", s_state.reverb_base_address, s_state.reverb_registers.mBASE);
|
||||
ImGui::Text("Current Address: 0x%08X", s_state.reverb_current_address);
|
||||
ImGui::Text("Current Amplitude: Input (%d, %d) Output (%d, %d)", s_state.last_reverb_input[0],
|
||||
s_state.last_reverb_input[1], s_state.last_reverb_output[0], s_state.last_reverb_output[1]);
|
||||
ImGui::Text("Current Mode: %s", GetReverbModeName(s_state.reverb_registers));
|
||||
ImGui::Text("Output Volume: Left %.1f%% Right %.1f%%", ApplyVolumeF(100.0f, s_state.reverb_registers.vLOUT),
|
||||
ApplyVolumeF(100.0f, s_state.reverb_registers.vROUT));
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::SameLine();
|
||||
draw_vu_meter(s_state.reverb_peaks);
|
||||
ImGui::NewLine();
|
||||
#endif
|
||||
|
||||
ImGui::Text("Pitch Modulation: ");
|
||||
for (u32 i = 1; i < NUM_VOICES; i++)
|
||||
{
|
||||
ImGui::SameLine(0.0f, 16.0f);
|
||||
|
||||
const bool active = IsPitchModulationEnabled(i);
|
||||
ImGui::TextColored(active ? active_color : inactive_color, "%u", i);
|
||||
voice.KeyOff();
|
||||
voice.adsr_envelope.counter = 0;
|
||||
voice.regs.adsr_volume = 0;
|
||||
}
|
||||
}
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + content_size.x - button_width,
|
||||
content_pos.y + content_height + ImGui::GetStyle().ItemSpacing.y));
|
||||
const bool copy_state = ImGui::Button(ICON_FA_COPY " Copy State");
|
||||
if (copy_state)
|
||||
ImGui::LogToClipboard();
|
||||
ImGui::SetCursorScreenPos(content_pos);
|
||||
|
||||
if (ImGui::CollapsingHeader("Reverb Environment"))
|
||||
constexpr ImGuiTableFlags table_flags =
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_PadOuterX | ImGuiTableFlags_SizingStretchSame;
|
||||
const auto label = [](const char* text) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImVec4 color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||
color.w *= 0.7f;
|
||||
ImGui::TextColored(color, "%s", text);
|
||||
ImGui::TableNextColumn();
|
||||
};
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(6.0f * scale, ImGui::GetStyle().CellPadding.y));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(1.0f, 1.0f, 1.0f, 0.035f));
|
||||
|
||||
if (ImGui::BeginChild("state", ImVec2(0.0f, content_height)))
|
||||
{
|
||||
ImGui::Columns(4);
|
||||
|
||||
ImGui::Text("[0] FB_SRC_A: 0x%04X", static_cast<u32>(s_state.reverb_registers.FB_SRC_A));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[1] FB_SRC_B: 0x%04X", static_cast<u32>(s_state.reverb_registers.FB_SRC_B));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[2] IIR_ALPHA: %d", static_cast<s32>(s_state.reverb_registers.IIR_ALPHA));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[3] ACC_COEF_A: %d", static_cast<s32>(s_state.reverb_registers.ACC_COEF_A));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[4] ACC_COEF_B: %d", static_cast<s32>(s_state.reverb_registers.ACC_COEF_B));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[5] ACC_COEF_C: %d", static_cast<s32>(s_state.reverb_registers.ACC_COEF_C));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[6] ACC_COEF_D: %d", static_cast<s32>(s_state.reverb_registers.ACC_COEF_D));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[7] IIR_COEF: %d", static_cast<s32>(s_state.reverb_registers.IIR_COEF));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[8] FB_ALPHA: %d", static_cast<s32>(s_state.reverb_registers.FB_ALPHA));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[9] FB_X: %d", static_cast<s32>(s_state.reverb_registers.FB_X));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[10] IIR_DEST_A[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_DEST_A[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[11] IIR_DEST_A[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_DEST_A[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[12] ACC_SRC_A[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_A[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[13] ACC_SRC_A[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_A[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[14] ACC_SRC_B[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_B[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[15] ACC_SRC_B[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_B[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[16] IIR_SRC_A[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_SRC_A[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[17] IIR_SRC_A[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_SRC_A[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[18] IIR_DEST_B[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_DEST_B[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[19] IIR_DEST_B[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_DEST_B[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[20] ACC_SRC_C[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_C[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[21] ACC_SRC_C[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_C[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[22] ACC_SRC_D[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_D[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[23] ACC_SRC_D[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.ACC_SRC_D[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[24] IIR_SRC_B[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_SRC_B[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[25] IIR_SRC_B[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.IIR_SRC_B[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[26] MIX_DEST_A[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.MIX_DEST_A[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[27] MIX_DEST_A[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.MIX_DEST_A[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[28] MIX_DEST_B[L]: 0x%04X", static_cast<u32>(s_state.reverb_registers.MIX_DEST_B[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[29] MIX_DEST_B[R]: 0x%04X", static_cast<u32>(s_state.reverb_registers.MIX_DEST_B[1]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[30] IN_COEF[L]: %d", static_cast<u32>(s_state.reverb_registers.IN_COEF[0]));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("[31] IN_COEF[R]: %d", static_cast<u32>(s_state.reverb_registers.IN_COEF[1]));
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Columns(1);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Hacks", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
if (ImGui::Button("Key Off All Voices"))
|
||||
if (ImGui::BeginTable("overview", 2, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV))
|
||||
{
|
||||
for (u32 i = 0; i < NUM_VOICES; i++)
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
u32 active_voices = 0;
|
||||
for (const Voice& voice : s_state.voices)
|
||||
active_voices += BoolToUInt32(voice.IsOn());
|
||||
ImGui::SeparatorText(
|
||||
TinyString::from_format("Mixer ({} / {} voices active)", active_voices, static_cast<u32>(NUM_VOICES)).c_str());
|
||||
if (ImGui::BeginTable("mixer",
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
6,
|
||||
#else
|
||||
5,
|
||||
#endif
|
||||
table_flags))
|
||||
{
|
||||
s_state.voices[i].KeyOff();
|
||||
s_state.voices[i].adsr_envelope.counter = 0;
|
||||
s_state.voices[i].regs.adsr_volume = 0;
|
||||
ImGui::TableSetupColumn("Source", ImGuiTableColumnFlags_WidthStretch, 1.1f);
|
||||
ImGui::TableSetupColumn("State", ImGuiTableColumnFlags_WidthStretch, 1.1f);
|
||||
ImGui::TableSetupColumn("Left vol.", ImGuiTableColumnFlags_WidthStretch, 0.9f);
|
||||
ImGui::TableSetupColumn("Right vol.", ImGuiTableColumnFlags_WidthStretch, 0.9f);
|
||||
ImGui::TableSetupColumn("Reverb send", ImGuiTableColumnFlags_WidthStretch, 1.2f);
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::TableSetupColumn("Peaks L/R", ImGuiTableColumnFlags_WidthFixed, 108.0f * scale);
|
||||
#endif
|
||||
ImGui::TableHeadersRow();
|
||||
const auto mix_row = [](const char* source, bool enabled, s16 volume_left, s16 volume_right, const char* send) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(source);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(enabled ? "Enabled" : "Disabled");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f%%", ApplyVolumeF(100.0f, volume_left));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f%%", ApplyVolumeF(100.0f, volume_right));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(send);
|
||||
};
|
||||
mix_row("Main", s_state.SPUCNT.enable, s_state.main_volume_left.current_level,
|
||||
s_state.main_volume_right.current_level, "-");
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::TableNextColumn();
|
||||
draw_vu_meter(s_state.output_peaks);
|
||||
#endif
|
||||
mix_row("CD", s_state.SPUCNT.cd_audio_enable, s_state.cd_audio_volume_left, s_state.cd_audio_volume_right,
|
||||
s_state.SPUCNT.cd_audio_reverb ? "Yes" : "No");
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::TableNextColumn();
|
||||
draw_vu_meter(s_state.cd_audio_peaks);
|
||||
#endif
|
||||
mix_row("External", s_state.SPUCNT.external_audio_enable, s_state.external_volume_left,
|
||||
s_state.external_volume_right, s_state.SPUCNT.external_audio_reverb ? "Yes" : "No");
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("-");
|
||||
#endif
|
||||
mix_row("Reverb", s_state.SPUCNT.reverb_master_enable, s_state.reverb_registers.vLOUT,
|
||||
s_state.reverb_registers.vROUT, "-");
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::TableNextColumn();
|
||||
draw_vu_meter(s_state.reverb_peaks);
|
||||
#endif
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("reverb_levels", 3, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Reverb (raw)");
|
||||
ImGui::TableSetupColumn("Left");
|
||||
ImGui::TableSetupColumn("Right");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Input");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", s_state.last_reverb_input[0]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", s_state.last_reverb_input[1]);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Output");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", s_state.last_reverb_output[0]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", s_state.last_reverb_output[1]);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SeparatorText("Transfer / IRQ");
|
||||
if (ImGui::BeginTable("transfer", 2, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Field", ImGuiTableColumnFlags_WidthStretch, 0.35f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.65f);
|
||||
static constexpr std::array transfer_modes = {"Stopped", "Manual write", "DMA write", "DMA read"};
|
||||
label("Transfer mode");
|
||||
ImGui::TextUnformatted(transfer_modes[static_cast<u8>(s_state.SPUCNT.ram_transfer_mode.GetValue())]);
|
||||
label("Transfer FIFO");
|
||||
ImGui::Text("%u halfwords (%u bytes)", s_state.transfer_fifo.GetSize(), s_state.transfer_fifo.GetSize() * 2);
|
||||
label("Interrupt");
|
||||
ImGui::Text("%s @ %04X (0x%05X bytes)", s_state.SPUCNT.irq9_enable ? "Enabled" : "Disabled",
|
||||
s_state.irq_address, (ZeroExtend32(s_state.irq_address) * 8) & RAM_MASK);
|
||||
label("Capture buffer");
|
||||
ImGui::TextUnformatted(s_state.SPUSTAT.second_half_capture_buffer ? "Second" : "First");
|
||||
label("Muted");
|
||||
ImGui::TextUnformatted(s_state.SPUCNT.mute_n ? "No" : "Yes");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (ImGui::BeginTable("signals", 5, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("IRQ9");
|
||||
ImGui::TableSetupColumn("DMA");
|
||||
ImGui::TableSetupColumn("DMA read");
|
||||
ImGui::TableSetupColumn("DMA write");
|
||||
ImGui::TableSetupColumn("Busy");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.SPUSTAT.irq9_flag ? "Asserted" : "Clear");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.SPUSTAT.dma_request ? "Asserted" : "Clear");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.SPUSTAT.dma_read_request ? "Asserted" : "Clear");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.SPUSTAT.dma_write_request ? "Asserted" : "Clear");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(s_state.SPUSTAT.transfer_busy ? "Yes" : "No");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("reverb_state", 3, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Reverb preset");
|
||||
ImGui::TableSetupColumn("Base (byte / reg)");
|
||||
ImGui::TableSetupColumn("Current (byte)");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(GetReverbModeName(s_state.reverb_registers));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%05X / %04X", s_state.reverb_base_address, s_state.reverb_registers.mBASE);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%05X", s_state.reverb_current_address);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabBar("state_tabs"))
|
||||
{
|
||||
const bool voices_tab = ImGui::BeginTabItem("Voices");
|
||||
// Clipboard logging includes both tabs, without retaining a separate snapshot.
|
||||
if (voices_tab || copy_state)
|
||||
{
|
||||
static constexpr int num_columns =
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
14;
|
||||
#else
|
||||
13;
|
||||
#endif
|
||||
static constexpr std::array adsr_phases = {"Off", "Attack", "Decay", "Sustain", "Release"};
|
||||
const float table_height = std::max(1.0f, ImGui::GetContentRegionAvail().y - ImGui::GetStyle().ItemSpacing.y);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(6.0f * scale, 1.0f * scale));
|
||||
if (ImGui::BeginTable("voices", num_columns, table_flags | ImGuiTableFlags_ScrollY, ImVec2(0.0f, table_height)))
|
||||
{
|
||||
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_WidthFixed, 22.0f * scale);
|
||||
ImGui::TableSetupColumn("Start", ImGuiTableColumnFlags_WidthStretch, 0.8f);
|
||||
ImGui::TableSetupColumn("Repeat", ImGuiTableColumnFlags_WidthStretch, 0.8f);
|
||||
ImGui::TableSetupColumn("Current", ImGuiTableColumnFlags_WidthStretch, 0.8f);
|
||||
ImGui::TableSetupColumn("Sample", ImGuiTableColumnFlags_WidthStretch, 0.75f);
|
||||
ImGui::TableSetupColumn("Rate (Hz)", ImGuiTableColumnFlags_WidthStretch, 1.25f);
|
||||
ImGui::TableSetupColumn("Left vol.", ImGuiTableColumnFlags_WidthStretch, 1.0f);
|
||||
ImGui::TableSetupColumn("Right vol.", ImGuiTableColumnFlags_WidthStretch, 1.0f);
|
||||
ImGui::TableSetupColumn("ADSR", ImGuiTableColumnFlags_WidthStretch, 0.95f);
|
||||
ImGui::TableSetupColumn("Level", ImGuiTableColumnFlags_WidthStretch, 0.9f);
|
||||
ImGui::TableSetupColumn("Ticks", ImGuiTableColumnFlags_WidthStretch, 1.15f);
|
||||
ImGui::TableSetupColumn("Reverb", ImGuiTableColumnFlags_WidthStretch, 0.8f);
|
||||
ImGui::TableSetupColumn("Pitch mod.", ImGuiTableColumnFlags_WidthStretch, 0.95f);
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::TableSetupColumn("Peaks L/R", ImGuiTableColumnFlags_WidthFixed, 108.0f * scale);
|
||||
#endif
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
for (u32 i = 0; i < NUM_VOICES; i++)
|
||||
{
|
||||
const Voice& voice = s_state.voices[i];
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
voice.IsOn() ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : inactive_color);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u", i);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%04X", voice.regs.adpcm_start_address);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%04X", voice.regs.adpcm_repeat_address);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%04X", voice.current_address);
|
||||
ImGui::TableNextColumn();
|
||||
if (IsVoiceNoiseEnabled(i))
|
||||
ImGui::TextUnformatted("Noise");
|
||||
else
|
||||
ImGui::Text("%u", ZeroExtend32(voice.counter.sample_index.GetValue()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.2f", (static_cast<float>(voice.regs.adpcm_sample_rate) / 4096.0f) * 44100.0f);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f%%", ApplyVolumeF(100.0f, voice.left_volume.current_level));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f%%", ApplyVolumeF(100.0f, voice.right_volume.current_level));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(adsr_phases[static_cast<u8>(voice.adsr_phase)]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f%%", ApplyVolumeF(100.0f, voice.regs.adsr_volume));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", voice.adsr_envelope.counter);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(IsVoiceReverbEnabled(i) ? "Yes" : "-");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(IsPitchModulationEnabled(i) ? "Yes" : "-");
|
||||
#ifdef SPU_ENABLE_VU_METER
|
||||
ImGui::TableNextColumn();
|
||||
draw_vu_meter(s_state.voice_peaks[i]);
|
||||
#endif
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
if (voices_tab)
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
const bool reverb_tab = ImGui::BeginTabItem("Reverb Registers");
|
||||
if (reverb_tab || copy_state)
|
||||
{
|
||||
const ReverbRegisters& regs = s_state.reverb_registers;
|
||||
if (ImGui::BeginTable("reverb_layout", 2, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV))
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::SeparatorText("Coefficients");
|
||||
if (ImGui::BeginTable("coefficients", 3, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_WidthFixed, 22.0f * scale);
|
||||
ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthStretch, 1.0f);
|
||||
ImGui::TableSetupColumn("Signed / Hex", ImGuiTableColumnFlags_WidthStretch, 1.5f);
|
||||
ImGui::TableHeadersRow();
|
||||
const auto coefficient = [&](u32 index, const char* name) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u", index);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(name);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d / 0x%04X", static_cast<s32>(static_cast<s16>(regs.rev[index])), regs.rev[index]);
|
||||
};
|
||||
static constexpr std::array names = {"IIR_ALPHA", "ACC_COEF_A", "ACC_COEF_B", "ACC_COEF_C",
|
||||
"ACC_COEF_D", "IIR_COEF", "FB_ALPHA", "FB_X"};
|
||||
for (u32 i = 0; i < names.size(); i++)
|
||||
coefficient(i + 2, names[i]);
|
||||
coefficient(30, "IN_COEF[L]");
|
||||
coefficient(31, "IN_COEF[R]");
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SeparatorText("Offsets");
|
||||
if (ImGui::BeginTable("offsets", 3, table_flags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Register");
|
||||
ImGui::TableSetupColumn("Left (hex)");
|
||||
ImGui::TableSetupColumn("Right (hex)");
|
||||
ImGui::TableHeadersRow();
|
||||
for (u32 i = 0; i < 2; i++)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u FB_SRC_%c", i, 'A' + i);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%04X", regs.rev[i]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("-");
|
||||
}
|
||||
static constexpr std::array names = {"IIR_DEST_A", "ACC_SRC_A", "ACC_SRC_B", "IIR_SRC_A", "IIR_DEST_B",
|
||||
"ACC_SRC_C", "ACC_SRC_D", "IIR_SRC_B", "MIX_DEST_A", "MIX_DEST_B"};
|
||||
for (u32 i = 0; i < names.size(); i++)
|
||||
{
|
||||
const u32 index = 10 + i * 2;
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u/%u %s", index, index + 1, names[i]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%04X", regs.rev[index]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%04X", regs.rev[index + 1]);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (reverb_tab)
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
if (copy_state)
|
||||
ImGui::LogFinish();
|
||||
}
|
||||
+122
-51
@@ -13,6 +13,7 @@
|
||||
#include "common/bitfield.h"
|
||||
#include "common/log.h"
|
||||
|
||||
#include "IconsFontAwesome.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#include <array>
|
||||
@@ -689,62 +690,132 @@ void Timers::UpdateSysClkEvent()
|
||||
|
||||
void Timers::DrawDebugStateWindow(float scale)
|
||||
{
|
||||
static constexpr u32 NUM_COLUMNS = 10;
|
||||
static constexpr std::array<const char*, NUM_COLUMNS> column_names = {
|
||||
{"#", "Value", "Target", "Sync", "Reset", "IRQ", "IRQRepeat", "IRQToggle", "Clock Source", "Reached"}};
|
||||
static constexpr std::array<const char*, 4> sync_mode_names = {
|
||||
{"PauseOnGate", "ResetOnGate", "ResetAndRunOnGate", "FreeRunOnGate"}};
|
||||
{"Pause on gate", "Reset on gate end", "Reset/run on gate start", "Free run on gate end"}};
|
||||
static constexpr std::array<std::array<const char*, 4>, 3> clock_source_names = {
|
||||
{{{"SysClk", "DotClk", "SysClk", "DotClk"}},
|
||||
{{"SysClk", "HBlank", "SysClk", "HBlank"}},
|
||||
{{"SysClk", "DotClk", "SysClk/8", "SysClk/8"}}}};
|
||||
{{{"System", "Dot clock", "System", "Dot clock"}},
|
||||
{{"System", "HBlank", "System", "HBlank"}},
|
||||
{{"System", "System", "System / 8", "System / 8"}}}};
|
||||
|
||||
ImGui::Columns(NUM_COLUMNS);
|
||||
ImGui::SetColumnWidth(0, 20.0f * scale);
|
||||
ImGui::SetColumnWidth(1, 50.0f * scale);
|
||||
ImGui::SetColumnWidth(2, 50.0f * scale);
|
||||
ImGui::SetColumnWidth(3, 100.0f * scale);
|
||||
ImGui::SetColumnWidth(4, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(5, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(6, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(7, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(8, 80.0f * scale);
|
||||
ImGui::SetColumnWidth(9, 80.0f * scale);
|
||||
const ImVec2 content_pos = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 content_size = ImGui::GetContentRegionAvail();
|
||||
const float table_height = std::max(1.0f, content_size.y - ImGui::GetFrameHeightWithSpacing());
|
||||
const float button_width =
|
||||
ImGui::CalcTextSize(ICON_FA_COPY " Copy State").x + ImGui::GetStyle().FramePadding.x * 2.0f;
|
||||
// Submit the footer first so clipboard logging can capture the table on the same frame.
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + content_size.x - button_width,
|
||||
content_pos.y + table_height + ImGui::GetStyle().ItemSpacing.y));
|
||||
const bool copy_state = ImGui::Button(ICON_FA_COPY " Copy State");
|
||||
if (copy_state)
|
||||
ImGui::LogToClipboard();
|
||||
ImGui::SetCursorScreenPos(content_pos);
|
||||
|
||||
for (const char* title : column_names)
|
||||
const auto row = [](const char* text) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImVec4 color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||
color.w *= 0.7f;
|
||||
ImGui::TextColored(color, "%s", text);
|
||||
};
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(10.0f * scale, ImGui::GetStyle().CellPadding.y));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(1.0f, 1.0f, 1.0f, 0.035f));
|
||||
if (ImGui::BeginTable("timers", NUM_TIMERS + 1,
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_PadOuterX | ImGuiTableFlags_SizingStretchSame |
|
||||
ImGuiTableFlags_ScrollY,
|
||||
ImVec2(0.0f, table_height)))
|
||||
{
|
||||
ImGui::TextUnformatted(title);
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("Timer 0");
|
||||
ImGui::TableSetupColumn("Timer 1");
|
||||
ImGui::TableSetupColumn("Timer 2");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (u32 i = 0; i < NUM_TIMERS; i++)
|
||||
{
|
||||
const CounterState& cs = s_state.counters[i];
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
cs.counting_enabled ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
ImGui::Text("%u", i);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", cs.counter);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", cs.target);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s",
|
||||
cs.mode.sync_enable ? sync_mode_names[static_cast<u8>(cs.mode.sync_mode.GetValue())] : "Disabled");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", cs.mode.reset_at_target ? "@Target" : "@Overflow");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s%s", cs.mode.irq_at_target ? "Target " : "", cs.mode.irq_on_overflow ? "Overflow" : "");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", cs.mode.irq_repeat ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", cs.mode.irq_pulse_n ? "Yes" : "No");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s%s", clock_source_names[i][cs.mode.clock_source], cs.external_counting_enabled ? " (External)" : "");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s%s", cs.mode.reached_target ? "Target " : "", cs.mode.reached_overflow ? "Overflow" : "");
|
||||
ImGui::NextColumn();
|
||||
ImGui::PopStyleColor();
|
||||
row("Counting");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.counting_enabled ? "Enabled" : "Disabled");
|
||||
}
|
||||
row("Value");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u", cs.counter);
|
||||
}
|
||||
row("Target");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%u", cs.target);
|
||||
}
|
||||
row("Clock");
|
||||
for (u32 i = 0; i < NUM_TIMERS; i++)
|
||||
{
|
||||
const CounterState& cs = s_state.counters[i];
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(clock_source_names[i][cs.mode.clock_source]);
|
||||
}
|
||||
row("Sync");
|
||||
for (u32 i = 0; i < NUM_TIMERS; i++)
|
||||
{
|
||||
const CounterState& cs = s_state.counters[i];
|
||||
ImGui::TableNextColumn();
|
||||
if (!cs.mode.sync_enable)
|
||||
ImGui::TextUnformatted("Disabled");
|
||||
else if (i == 2)
|
||||
ImGui::TextUnformatted(cs.counting_enabled ? "Free run" : "Stopped");
|
||||
else
|
||||
ImGui::TextWrapped("%s", sync_mode_names[static_cast<u8>(cs.mode.sync_mode.GetValue())]);
|
||||
}
|
||||
row("Gate");
|
||||
for (u32 i = 0; i < NUM_TIMERS; i++)
|
||||
{
|
||||
const CounterState& cs = s_state.counters[i];
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(i == 2 ? "None" : (cs.gate ? "Active" : "Inactive"));
|
||||
}
|
||||
row("Reset");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.mode.reset_at_target ? "Target" : "Overflow");
|
||||
}
|
||||
row("IRQ source");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.mode.irq_at_target ? (cs.mode.irq_on_overflow ? "Target + overflow" : "Target") :
|
||||
(cs.mode.irq_on_overflow ? "Overflow" : "None"));
|
||||
}
|
||||
row("IRQ repeat");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.mode.irq_repeat ? "Enabled" : "Disabled");
|
||||
}
|
||||
row("IRQ mode");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.mode.irq_pulse_n ? "Toggle" : "Pulse");
|
||||
}
|
||||
row("IRQ request");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.mode.interrupt_request_n ? "Clear" : "Asserted");
|
||||
}
|
||||
row("Reached");
|
||||
for (const CounterState& cs : s_state.counters)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cs.mode.reached_target ? (cs.mode.reached_overflow ? "Target + overflow" : "Target") :
|
||||
(cs.mode.reached_overflow ? "Overflow" : "None"));
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
if (copy_state)
|
||||
ImGui::LogFinish();
|
||||
}
|
||||
Reference in new issue
Block a user