2024-10-03 20:43:23 +00:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
#include <imgui.h>
|
|
|
|
|
|
|
|
#include "common/config.h"
|
|
|
|
#include "common/singleton.h"
|
|
|
|
#include "common/types.h"
|
|
|
|
#include "core/debug_state.h"
|
|
|
|
#include "imgui/imgui_std.h"
|
|
|
|
#include "imgui_internal.h"
|
|
|
|
#include "layer.h"
|
2024-10-13 12:02:22 +00:00
|
|
|
#include "options.h"
|
2024-10-03 20:43:23 +00:00
|
|
|
#include "widget/frame_dump.h"
|
|
|
|
#include "widget/frame_graph.h"
|
|
|
|
|
|
|
|
using namespace ImGui;
|
|
|
|
using namespace Core::Devtools;
|
|
|
|
using L = Core::Devtools::Layer;
|
|
|
|
|
|
|
|
static bool show_simple_fps = false;
|
2024-10-21 15:26:32 +00:00
|
|
|
static bool visibility_toggled = false;
|
2024-10-04 03:44:36 +00:00
|
|
|
|
2024-10-13 12:02:22 +00:00
|
|
|
static float fps_scale = 1.0f;
|
2024-10-03 20:43:23 +00:00
|
|
|
static bool show_advanced_debug = false;
|
|
|
|
static int dump_frame_count = 1;
|
|
|
|
|
|
|
|
static Widget::FrameGraph frame_graph;
|
|
|
|
static std::vector<Widget::FrameDumpViewer> frame_viewers;
|
|
|
|
|
|
|
|
static float debug_popup_timing = 3.0f;
|
|
|
|
|
2024-10-13 12:02:22 +00:00
|
|
|
static bool just_opened_options = false;
|
|
|
|
|
2024-10-16 10:12:46 +00:00
|
|
|
// clang-format off
|
|
|
|
static std::string help_text =
|
|
|
|
#include "help.txt"
|
|
|
|
;
|
|
|
|
// clang-format on
|
|
|
|
|
2024-10-03 20:43:23 +00:00
|
|
|
void L::DrawMenuBar() {
|
|
|
|
const auto& ctx = *GImGui;
|
|
|
|
const auto& io = ctx.IO;
|
|
|
|
|
|
|
|
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
|
|
|
|
2024-10-13 12:02:22 +00:00
|
|
|
bool open_popup_options = false;
|
2024-10-16 10:12:46 +00:00
|
|
|
bool open_popup_help = false;
|
2024-10-13 12:02:22 +00:00
|
|
|
|
2024-10-03 20:43:23 +00:00
|
|
|
if (BeginMainMenuBar()) {
|
|
|
|
if (BeginMenu("Options")) {
|
|
|
|
if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) {
|
|
|
|
if (isSystemPaused) {
|
|
|
|
DebugState.ResumeGuestThreads();
|
|
|
|
} else {
|
|
|
|
DebugState.PauseGuestThreads();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
if (BeginMenu("GPU Tools")) {
|
|
|
|
MenuItem("Show frame info", nullptr, &frame_graph.is_open);
|
|
|
|
if (BeginMenu("Dump frames")) {
|
|
|
|
SliderInt("Count", &dump_frame_count, 1, 5);
|
|
|
|
if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) {
|
|
|
|
DebugState.RequestFrameDump(dump_frame_count);
|
|
|
|
}
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
2024-10-13 12:02:22 +00:00
|
|
|
open_popup_options = MenuItem("Options");
|
2024-10-16 10:12:46 +00:00
|
|
|
open_popup_help = MenuItem("Help & Tips");
|
2024-10-03 20:43:23 +00:00
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
EndMainMenuBar();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsKeyPressed(ImGuiKey_F9, false)) {
|
|
|
|
if (io.KeyCtrl && io.KeyAlt) {
|
|
|
|
if (!DebugState.ShouldPauseInSubmit()) {
|
|
|
|
DebugState.RequestFrameDump(dump_frame_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!io.KeyCtrl && !io.KeyAlt) {
|
|
|
|
if (isSystemPaused) {
|
|
|
|
DebugState.ResumeGuestThreads();
|
|
|
|
} else {
|
|
|
|
DebugState.PauseGuestThreads();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-10-13 12:02:22 +00:00
|
|
|
|
|
|
|
if (open_popup_options) {
|
|
|
|
OpenPopup("GPU Tools Options");
|
|
|
|
just_opened_options = true;
|
|
|
|
}
|
2024-10-16 10:12:46 +00:00
|
|
|
if (open_popup_help) {
|
|
|
|
OpenPopup("HelpTips");
|
|
|
|
}
|
2024-10-03 20:43:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void L::DrawAdvanced() {
|
|
|
|
DrawMenuBar();
|
|
|
|
|
|
|
|
const auto& ctx = *GImGui;
|
|
|
|
const auto& io = ctx.IO;
|
|
|
|
|
|
|
|
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
|
|
|
|
|
|
|
frame_graph.Draw();
|
|
|
|
|
|
|
|
if (isSystemPaused) {
|
|
|
|
GetForegroundDrawList(GetMainViewport())
|
|
|
|
->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused");
|
|
|
|
}
|
|
|
|
|
2024-10-13 12:02:22 +00:00
|
|
|
if (DebugState.should_show_frame_dump && DebugState.waiting_reg_dumps.empty()) {
|
2024-10-03 20:43:23 +00:00
|
|
|
DebugState.should_show_frame_dump = false;
|
|
|
|
std::unique_lock lock{DebugState.frame_dump_list_mutex};
|
|
|
|
while (!DebugState.frame_dump_list.empty()) {
|
2024-10-13 12:02:22 +00:00
|
|
|
const auto& frame_dump = DebugState.frame_dump_list.back();
|
2024-10-03 20:43:23 +00:00
|
|
|
frame_viewers.emplace_back(frame_dump);
|
2024-10-13 12:02:22 +00:00
|
|
|
DebugState.frame_dump_list.pop_back();
|
|
|
|
}
|
|
|
|
static bool first_time = true;
|
|
|
|
if (first_time) {
|
|
|
|
first_time = false;
|
|
|
|
DebugState.ShowDebugMessage("Tip: You can shift+click any\n"
|
|
|
|
"popup to open a new window");
|
2024-10-03 20:43:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto it = frame_viewers.begin(); it != frame_viewers.end();) {
|
|
|
|
if (it->is_open) {
|
|
|
|
it->Draw();
|
|
|
|
++it;
|
|
|
|
} else {
|
|
|
|
it = frame_viewers.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!DebugState.debug_message_popup.empty()) {
|
|
|
|
if (debug_popup_timing > 0.0f) {
|
|
|
|
debug_popup_timing -= io.DeltaTime;
|
|
|
|
if (Begin("##devtools_msg", nullptr,
|
|
|
|
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
|
|
|
ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove)) {
|
|
|
|
BringWindowToDisplayFront(GetCurrentWindow());
|
|
|
|
const auto display_size = io.DisplaySize;
|
|
|
|
const auto& msg = DebugState.debug_message_popup.front();
|
|
|
|
const auto padding = GetStyle().WindowPadding;
|
|
|
|
const auto txt_size = CalcTextSize(&msg.front(), &msg.back() + 1, false, 250.0f);
|
|
|
|
SetWindowPos({display_size.x - padding.x * 2.0f - txt_size.x, 50.0f});
|
|
|
|
SetWindowSize({txt_size.x + padding.x * 2.0f, txt_size.y + padding.y * 2.0f});
|
|
|
|
PushTextWrapPos(250.0f);
|
|
|
|
TextEx(&msg.front(), &msg.back() + 1);
|
|
|
|
PopTextWrapPos();
|
|
|
|
}
|
|
|
|
End();
|
|
|
|
} else {
|
|
|
|
DebugState.debug_message_popup.pop();
|
|
|
|
debug_popup_timing = 3.0f;
|
|
|
|
}
|
|
|
|
}
|
2024-10-13 12:02:22 +00:00
|
|
|
|
|
|
|
bool close_popup_options = true;
|
|
|
|
if (BeginPopupModal("GPU Tools Options", &close_popup_options,
|
|
|
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) {
|
|
|
|
static char disassembly_cli[512];
|
2024-10-16 10:12:46 +00:00
|
|
|
static bool frame_dump_render_on_collapse;
|
2024-10-13 12:02:22 +00:00
|
|
|
|
|
|
|
if (just_opened_options) {
|
|
|
|
just_opened_options = false;
|
|
|
|
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1);
|
|
|
|
disassembly_cli[s] = '\0';
|
2024-10-16 10:12:46 +00:00
|
|
|
frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse;
|
2024-10-13 12:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli));
|
|
|
|
if (IsItemHovered()) {
|
|
|
|
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")");
|
|
|
|
}
|
2024-10-16 10:12:46 +00:00
|
|
|
Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse);
|
|
|
|
if (IsItemHovered()) {
|
|
|
|
SetTooltip("When a frame dump is collapsed, it will keep\n"
|
|
|
|
"showing all opened popups related to it");
|
|
|
|
}
|
2024-10-13 12:02:22 +00:00
|
|
|
|
|
|
|
if (Button("Save")) {
|
|
|
|
Options.disassembly_cli = disassembly_cli;
|
2024-10-16 10:12:46 +00:00
|
|
|
Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse;
|
2024-10-13 12:02:22 +00:00
|
|
|
SaveIniSettingsToDisk(io.IniFilename);
|
|
|
|
CloseCurrentPopup();
|
|
|
|
}
|
|
|
|
EndPopup();
|
|
|
|
}
|
2024-10-16 10:12:46 +00:00
|
|
|
|
|
|
|
if (BeginPopup("HelpTips", ImGuiWindowFlags_AlwaysAutoResize |
|
|
|
|
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove)) {
|
|
|
|
CentralizeWindow();
|
|
|
|
|
|
|
|
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{10.0f});
|
|
|
|
PushTextWrapPos(600.0f);
|
|
|
|
|
|
|
|
const char* begin = help_text.data();
|
|
|
|
TextUnformatted(begin, begin + help_text.size());
|
|
|
|
|
|
|
|
PopTextWrapPos();
|
|
|
|
PopStyleVar();
|
|
|
|
|
|
|
|
EndPopup();
|
|
|
|
}
|
2024-10-03 20:43:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void L::DrawSimple() {
|
|
|
|
const auto io = GetIO();
|
|
|
|
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
|
|
|
}
|
|
|
|
|
2024-10-13 12:02:22 +00:00
|
|
|
static void LoadSettings(const char* line) {
|
|
|
|
int i;
|
|
|
|
float f;
|
|
|
|
if (sscanf(line, "fps_scale=%f", &f) == 1) {
|
|
|
|
fps_scale = f;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sscanf(line, "show_advanced_debug=%d", &i) == 1) {
|
|
|
|
show_advanced_debug = i != 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sscanf(line, "show_frame_graph=%d", &i) == 1) {
|
|
|
|
frame_graph.is_open = i != 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sscanf(line, "dump_frame_count=%d", &i) == 1) {
|
|
|
|
dump_frame_count = i;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-03 20:43:23 +00:00
|
|
|
void L::SetupSettings() {
|
|
|
|
frame_graph.is_open = true;
|
|
|
|
|
2024-10-13 12:02:22 +00:00
|
|
|
using SettingLoader = void (*)(const char*);
|
|
|
|
|
2024-10-03 20:43:23 +00:00
|
|
|
ImGuiSettingsHandler handler{};
|
|
|
|
handler.TypeName = "DevtoolsLayer";
|
|
|
|
handler.TypeHash = ImHashStr(handler.TypeName);
|
|
|
|
handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
|
2024-10-13 12:02:22 +00:00
|
|
|
if (std::string_view("Data") == name) {
|
|
|
|
static_assert(std::is_same_v<decltype(&LoadSettings), SettingLoader>);
|
|
|
|
return (void*)&LoadSettings;
|
|
|
|
}
|
|
|
|
if (std::string_view("CmdList") == name) {
|
|
|
|
static_assert(
|
|
|
|
std::is_same_v<decltype(&Widget::CmdListViewer::LoadConfig), SettingLoader>);
|
|
|
|
return (void*)&Widget::CmdListViewer::LoadConfig;
|
|
|
|
}
|
|
|
|
if (std::string_view("Options") == name) {
|
|
|
|
static_assert(std::is_same_v<decltype(&LoadOptionsConfig), SettingLoader>);
|
|
|
|
return (void*)&LoadOptionsConfig;
|
|
|
|
}
|
|
|
|
return (void*)nullptr;
|
2024-10-03 20:43:23 +00:00
|
|
|
};
|
2024-10-13 12:02:22 +00:00
|
|
|
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* handle, const char* line) {
|
|
|
|
if (handle != nullptr) {
|
|
|
|
reinterpret_cast<SettingLoader>(handle)(line);
|
2024-10-03 20:43:23 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
|
|
|
|
buf->appendf("[%s][Data]\n", handler->TypeName);
|
2024-10-04 03:44:36 +00:00
|
|
|
buf->appendf("fps_scale=%f\n", fps_scale);
|
2024-10-03 20:43:23 +00:00
|
|
|
buf->appendf("show_advanced_debug=%d\n", show_advanced_debug);
|
|
|
|
buf->appendf("show_frame_graph=%d\n", frame_graph.is_open);
|
|
|
|
buf->appendf("dump_frame_count=%d\n", dump_frame_count);
|
|
|
|
buf->append("\n");
|
2024-10-13 12:02:22 +00:00
|
|
|
buf->appendf("[%s][CmdList]\n", handler->TypeName);
|
|
|
|
Widget::CmdListViewer::SerializeConfig(buf);
|
|
|
|
buf->append("\n");
|
|
|
|
buf->appendf("[%s][Options]\n", handler->TypeName);
|
|
|
|
SerializeOptionsConfig(buf);
|
|
|
|
buf->append("\n");
|
2024-10-03 20:43:23 +00:00
|
|
|
};
|
|
|
|
AddSettingsHandler(&handler);
|
|
|
|
|
|
|
|
const ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
|
|
|
DockBuilderAddNode(dock_id, 0);
|
2024-10-13 12:02:22 +00:00
|
|
|
DockBuilderSetNodePos(dock_id, ImVec2{450.0, 150.0});
|
|
|
|
DockBuilderSetNodeSize(dock_id, ImVec2{400.0, 500.0});
|
2024-10-03 20:43:23 +00:00
|
|
|
DockBuilderFinish(dock_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void L::Draw() {
|
|
|
|
const auto io = GetIO();
|
|
|
|
PushID("DevtoolsLayer");
|
|
|
|
|
|
|
|
if (!DebugState.IsGuestThreadsPaused()) {
|
|
|
|
const auto fn = DebugState.flip_frame_count.load();
|
|
|
|
frame_graph.AddFrame(fn, io.DeltaTime);
|
|
|
|
}
|
|
|
|
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
|
|
|
if (io.KeyCtrl) {
|
|
|
|
show_advanced_debug = !show_advanced_debug;
|
|
|
|
} else {
|
|
|
|
show_simple_fps = !show_simple_fps;
|
|
|
|
}
|
2024-10-21 15:26:32 +00:00
|
|
|
visibility_toggled = true;
|
2024-10-03 20:43:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (show_simple_fps) {
|
|
|
|
if (Begin("Video Info", nullptr,
|
|
|
|
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
|
|
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
2024-10-21 15:26:32 +00:00
|
|
|
// Set window position to top left if it was toggled on
|
|
|
|
if (visibility_toggled) {
|
|
|
|
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_Always);
|
|
|
|
visibility_toggled = false;
|
|
|
|
}
|
2024-10-04 03:44:36 +00:00
|
|
|
if (BeginPopupContextWindow()) {
|
|
|
|
#define M(label, value) \
|
|
|
|
if (MenuItem(label, nullptr, fps_scale == value)) \
|
|
|
|
fps_scale = value
|
|
|
|
M("0.5x", 0.5f);
|
|
|
|
M("1.0x", 1.0f);
|
|
|
|
M("1.5x", 1.5f);
|
|
|
|
M("2.0x", 2.0f);
|
|
|
|
M("2.5x", 2.5f);
|
|
|
|
EndPopup();
|
|
|
|
#undef M
|
|
|
|
}
|
2024-10-03 20:43:23 +00:00
|
|
|
KeepWindowInside();
|
2024-10-04 03:44:36 +00:00
|
|
|
SetWindowFontScale(fps_scale);
|
2024-10-03 20:43:23 +00:00
|
|
|
DrawSimple();
|
|
|
|
}
|
|
|
|
End();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (show_advanced_debug) {
|
|
|
|
PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]);
|
|
|
|
PushID("DevtoolsLayer");
|
|
|
|
DrawAdvanced();
|
|
|
|
PopID();
|
|
|
|
PopFont();
|
|
|
|
}
|
|
|
|
|
|
|
|
PopID();
|
|
|
|
}
|