Devtools: PM4 Explorer (#1094)

* Devtools: Pause system

* Devtools: pm4 viewer

- new menu bar
- refactored video_info layer
- dump & inspect pm4 packets
- removed dumpPM4 config
- renamed System to DebugState
- add docking space
- simple video info constrained to window size

* Devtools: pm4 viewer - add combo to select the queue

* Devtools: pm4 viewer - add hex editor

* Devtools: pm4 viewer - dump current cmd

* add monospaced font to devtools

* Devtools: pm4 viewer - use spec op name

avoid some allocations
This commit is contained in:
Vinicius Rangel 2024-10-03 17:43:23 -03:00 committed by GitHub
parent 009f956d8d
commit af398e3684
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 19323 additions and 242 deletions

215
src/core/devtools/layer.cpp Normal file
View file

@ -0,0 +1,215 @@
// 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"
#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;
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;
void L::DrawMenuBar() {
const auto& ctx = *GImGui;
const auto& io = ctx.IO;
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
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();
}
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();
}
}
}
}
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");
}
if (DebugState.should_show_frame_dump) {
DebugState.should_show_frame_dump = false;
std::unique_lock lock{DebugState.frame_dump_list_mutex};
while (!DebugState.frame_dump_list.empty()) {
auto frame_dump = std::move(DebugState.frame_dump_list.back());
DebugState.frame_dump_list.pop_back();
frame_viewers.emplace_back(frame_dump);
}
}
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;
}
}
}
void L::DrawSimple() {
const auto io = GetIO();
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
}
void L::SetupSettings() {
frame_graph.is_open = true;
ImGuiSettingsHandler handler{};
handler.TypeName = "DevtoolsLayer";
handler.TypeHash = ImHashStr(handler.TypeName);
handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
return std::string_view("Data") == name ? (void*)1 : nullptr;
};
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void*, const char* line) {
int v;
if (sscanf(line, "show_simple_fps=%d", &v) == 1) {
show_simple_fps = v != 0;
} else if (sscanf(line, "show_advanced_debug=%d", &v) == 1) {
show_advanced_debug = v != 0;
} else if (sscanf(line, "show_frame_graph=%d", &v) == 1) {
frame_graph.is_open = v != 0;
} else if (sscanf(line, "dump_frame_count=%d", &v) == 1) {
dump_frame_count = v;
}
};
handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
buf->appendf("[%s][Data]\n", handler->TypeName);
buf->appendf("show_simple_fps=%d\n", show_simple_fps);
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");
};
AddSettingsHandler(&handler);
const ImGuiID dock_id = ImHashStr("FrameDumpDock");
DockBuilderAddNode(dock_id, 0);
DockBuilderSetNodePos(dock_id, ImVec2{50.0, 50.0});
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;
}
}
if (show_simple_fps) {
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_FirstUseEver);
if (Begin("Video Info", nullptr,
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
KeepWindowInside();
DrawSimple();
}
End();
}
if (show_advanced_debug) {
PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]);
PushID("DevtoolsLayer");
DrawAdvanced();
PopID();
PopFont();
}
PopID();
}