devtools: pm4 - show color buffer info as popup

add ux improvements for open new windows with shift+click
better window titles
This commit is contained in:
Vinicius Rangel 2024-10-12 14:54:22 -03:00
parent ccd997c6a6
commit dbce929d7b
No known key found for this signature in database
GPG Key ID: A5B154D904B761D9
14 changed files with 414 additions and 242 deletions

View File

@ -353,16 +353,18 @@ set(DEV_TOOLS src/core/devtools/layer.cpp
src/core/devtools/gcn/gcn_shader_regs.cpp src/core/devtools/gcn/gcn_shader_regs.cpp
src/core/devtools/widget/cmd_list.cpp src/core/devtools/widget/cmd_list.cpp
src/core/devtools/widget/cmd_list.h src/core/devtools/widget/cmd_list.h
src/core/devtools/widget/common.h
src/core/devtools/widget/frame_dump.cpp src/core/devtools/widget/frame_dump.cpp
src/core/devtools/widget/frame_dump.h src/core/devtools/widget/frame_dump.h
src/core/devtools/widget/frame_graph.cpp src/core/devtools/widget/frame_graph.cpp
src/core/devtools/widget/frame_graph.h src/core/devtools/widget/frame_graph.h
src/core/devtools/widget/imgui_memory_editor.h src/core/devtools/widget/imgui_memory_editor.h
src/core/devtools/widget/reg_popup.cpp
src/core/devtools/widget/reg_popup.h
src/core/devtools/widget/reg_view.cpp src/core/devtools/widget/reg_view.cpp
src/core/devtools/widget/reg_view.h src/core/devtools/widget/reg_view.h
src/core/devtools/widget/text_editor.cpp src/core/devtools/widget/text_editor.cpp
src/core/devtools/widget/text_editor.h src/core/devtools/widget/text_editor.h
src/core/devtools/widget/types.h
) )
set(COMMON src/common/logging/backend.cpp set(COMMON src/common/logging/backend.cpp

View File

@ -1,11 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <imgui.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/native_clock.h" #include "common/native_clock.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "debug_state.h" #include "debug_state.h"
#include "devtools/widget/types.h" #include "devtools/widget/common.h"
#include "libraries/kernel/time_management.h" #include "libraries/kernel/time_management.h"
#include "libraries/system/msgdialog.h" #include "libraries/system/msgdialog.h"
#include "video_core/amdgpu/pm4_cmds.h" #include "video_core/amdgpu/pm4_cmds.h"

View File

@ -105,9 +105,15 @@ void L::DrawAdvanced() {
DebugState.should_show_frame_dump = false; DebugState.should_show_frame_dump = false;
std::unique_lock lock{DebugState.frame_dump_list_mutex}; std::unique_lock lock{DebugState.frame_dump_list_mutex};
while (!DebugState.frame_dump_list.empty()) { while (!DebugState.frame_dump_list.empty()) {
auto frame_dump = std::move(DebugState.frame_dump_list.back()); const auto& frame_dump = DebugState.frame_dump_list.back();
DebugState.frame_dump_list.pop_back();
frame_viewers.emplace_back(frame_dump); frame_viewers.emplace_back(frame_dump);
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");
} }
} }

View File

@ -63,6 +63,21 @@ static HdrType GetNext(HdrType this_pm4, uint32_t n) {
return curr_pm4; return curr_pm4;
} }
static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
using AmdGpu::PM4ItOpcode;
switch (opcode) {
case PM4ItOpcode::DispatchDirect:
case PM4ItOpcode::DispatchIndirect:
case PM4ItOpcode::DrawIndex2:
case PM4ItOpcode::DrawIndexAuto:
case PM4ItOpcode::DrawIndexOffset2:
case PM4ItOpcode::DrawIndexIndirect:
return true;
default:
return false;
}
}
void ParsePolygonControl(u32 value, bool begin_table) { void ParsePolygonControl(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<AmdGpu::Liverpool::PolygonControl const&>(value); auto const reg = reinterpret_cast<AmdGpu::Liverpool::PolygonControl const&>(value);
@ -888,7 +903,7 @@ void CmdListViewer::OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* b
Separator(); Separator();
BeginGroup(); BeginGroup();
auto const* pkt = reinterpret_cast<AmdGpu::PM4CmdSetBase const*>(header); // auto const* pkt = reinterpret_cast<AmdGpu::PM4CmdSetBase const*>(header);
Text("BASE_INDEX: %08X", body[0]); Text("BASE_INDEX: %08X", body[0]);
Text("ADDRESS0 : %08X", body[1]); Text("ADDRESS0 : %08X", body[1]);
Text("ADDRESS1 : %08X", body[2]); Text("ADDRESS1 : %08X", body[2]);
@ -1077,9 +1092,10 @@ void CmdListViewer::OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const*
EndGroup(); EndGroup();
} }
CmdListViewer::CmdListViewer(const FrameDumpViewer* parent, const std::vector<u32>& cmd_list, CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump,
uintptr_t base_addr, std::string name) const std::vector<u32>& cmd_list, uintptr_t _base_addr,
: parent(parent), base_addr(base_addr), name(std::move(name)) { std::string _name)
: frame_dump(_frame_dump), base_addr(_base_addr), name(std::move(_name)) {
using namespace AmdGpu; using namespace AmdGpu;
cmdb_addr = (uintptr_t)cmd_list.data(); cmdb_addr = (uintptr_t)cmd_list.data();
@ -1131,11 +1147,7 @@ CmdListViewer::CmdListViewer(const FrameDumpViewer* parent, const std::vector<u3
} }
} }
if (opcode == PM4ItOpcode::DispatchDirect || opcode == PM4ItOpcode::DispatchIndirect || if (IsDrawCall(opcode)) {
opcode == PM4ItOpcode::DrawIndex2 || opcode == PM4ItOpcode::DrawIndexAuto ||
opcode == PM4ItOpcode::DrawIndexOffset2 || opcode == PM4ItOpcode::DrawIndexIndirect
// ...
) {
// All these commands are terminated by NOP at the end, so // All these commands are terminated by NOP at the end, so
// it is safe to skip it to be even with CP // it is safe to skip it to be even with CP
// next_pm4_hdr = get_next(next_pm4_hdr, 1); // next_pm4_hdr = get_next(next_pm4_hdr, 1);
@ -1179,6 +1191,34 @@ CmdListViewer::CmdListViewer(const FrameDumpViewer* parent, const std::vector<u3
void CmdListViewer::Draw() { void CmdListViewer::Draw() {
const auto& ctx = *GetCurrentContext(); const auto& ctx = *GetCurrentContext();
if (batch_view.open) {
batch_view.Draw();
}
for (auto it = extra_batch_view.begin(); it != extra_batch_view.end();) {
if (!it->open) {
it = extra_batch_view.erase(it);
continue;
}
it->Draw();
++it;
}
if (cmdb_view.Open) {
MemoryEditor::Sizes s;
cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr);
SetNextWindowSize({s.WindowWidth, s.WindowWidth * 0.6f}, ImGuiCond_FirstUseEver);
SetNextWindowSizeConstraints({0.0f}, {s.WindowWidth, FLT_MAX});
if (Begin(cmdb_view_name.c_str(), &cmdb_view.Open,
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) {
cmdb_view.DrawContents((void*)cmdb_addr, cmdb_size, base_addr);
if (cmdb_view.ContentsWidthChanged) {
cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr);
SetWindowSize({s.WindowWidth, s.WindowWidth * 0.6f});
}
}
End();
}
PushID(name.c_str()); PushID(name.c_str());
if (BeginChild("cmd_queue", {})) { if (BeginChild("cmd_queue", {})) {
@ -1213,10 +1253,17 @@ void CmdListViewer::Draw() {
} }
u32 batch_id = ~0u; u32 batch_id = ~0u;
s32 current_highlight_batch = -1; u32 current_highlight_batch = ~0u;
int id = 0;
PushID(0);
for (const auto& event : events) { for (const auto& event : events) {
auto processed_size = 0ull; PopID();
PushID(id++);
if (std::holds_alternative<BatchBegin>(event)) {
batch_id = std::get<BatchBegin>(event).id;
}
if (show_markers) { if (show_markers) {
if (std::holds_alternative<PushMarker>(event)) { if (std::holds_alternative<PushMarker>(event)) {
@ -1243,11 +1290,6 @@ void CmdListViewer::Draw() {
} }
} }
if (std::holds_alternative<BatchBegin>(event)) {
batch_id = std::get<BatchBegin>(event).id;
continue;
}
if (!std::holds_alternative<BatchInfo>(event)) { if (!std::holds_alternative<BatchInfo>(event)) {
continue; continue;
} }
@ -1274,15 +1316,35 @@ void CmdListViewer::Draw() {
PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
} }
if (!group_batches || CollapsingHeader(batch_hdr)) { const auto open_batch_view = [&, this] {
if (frame_dump->regs.contains(batch.command_addr)) {
auto data = frame_dump->regs.at(batch.command_addr);
if (GetIO().KeyShift) {
auto& pop = extra_batch_view.emplace_back();
pop.SetData(data, batch_id);
pop.open = true;
} else {
batch_view.SetData(data, batch_id);
batch_view.open = true;
}
}
};
bool show_batch_content = true;
if (group_batches) {
show_batch_content =
CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap);
SameLine(GetContentRegionAvail().x - 40.0f);
if (Button("->", {40.0f, 0.0f})) {
open_batch_view();
}
}
if (show_batch_content) {
auto processed_size = 0ull;
auto bb = ctx.LastItemData.Rect; auto bb = ctx.LastItemData.Rect;
if (group_batches) { if (group_batches) {
if (IsItemToggledOpen()) {
if (parent->frame_dump.regs.contains(batch.command_addr)) {
batch_view.SetData(parent->frame_dump.regs.at(batch.command_addr));
batch_view.open = true;
}
}
Indent(); Indent();
} }
auto const batch_sz = batch.end_addr - batch.start_addr; auto const batch_sz = batch.end_addr - batch.start_addr;
@ -1300,9 +1362,23 @@ void CmdListViewer::Draw() {
cmdb_addr + batch.start_addr + processed_size, cmdb_addr + batch.start_addr + processed_size,
Gcn::GetOpCodeName((u32)op)); Gcn::GetOpCodeName((u32)op));
if (TreeNode(header_name)) { bool open_pm4 = TreeNode(header_name);
const bool just_opened = IsItemToggledOpen(); if (!group_batches) {
if (just_opened) { if (IsDrawCall(op)) {
SameLine(GetContentRegionAvail().x - 40.0f);
if (Button("->", {40.0f, 0.0f})) {
open_batch_view();
}
}
if (IsItemHovered() && ctx.IO.KeyShift) {
if (BeginTooltip()) {
Text("Batch %d", batch_id);
EndTooltip();
}
}
}
if (open_pm4) {
if (IsItemToggledOpen()) {
// Editor // Editor
cmdb_view.GotoAddrAndHighlight( cmdb_view.GotoAddrAndHighlight(
reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr, reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr,
@ -1383,6 +1459,7 @@ void CmdListViewer::Draw() {
Separator(); Separator();
} }
} }
PopID();
highlight_batch = current_highlight_batch; highlight_batch = current_highlight_batch;
@ -1391,34 +1468,6 @@ void CmdListViewer::Draw() {
} }
EndChild(); EndChild();
PopID(); PopID();
if (cmdb_view.Open) {
MemoryEditor::Sizes s;
cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr);
SetNextWindowSize({s.WindowWidth, s.WindowWidth * 0.6f}, ImGuiCond_FirstUseEver);
SetNextWindowSizeConstraints({0.0f}, {s.WindowWidth, FLT_MAX});
if (Begin(cmdb_view_name.c_str(), &cmdb_view.Open,
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) {
cmdb_view.DrawContents((void*)cmdb_addr, cmdb_size, base_addr);
if (cmdb_view.ContentsWidthChanged) {
cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr);
SetWindowSize({s.WindowWidth, s.WindowWidth * 0.6f});
}
}
End();
}
if (batch_view.open) {
batch_view.Draw();
}
for (auto it = extra_batch_view.begin(); it != extra_batch_view.end(); ++it) {
if (!it->open) {
it = extra_batch_view.erase(it);
continue;
}
it->Draw();
++it;
}
} }
} // namespace Core::Devtools::Widget } // namespace Core::Devtools::Widget

View File

@ -5,13 +5,14 @@
#pragma once #pragma once
#include <memory>
#include <vector> #include <vector>
#include <imgui.h> #include <imgui.h>
#include "common.h"
#include "common/types.h" #include "common/types.h"
#include "imgui_memory_editor.h" #include "imgui_memory_editor.h"
#include "reg_view.h" #include "reg_view.h"
#include "types.h"
namespace AmdGpu { namespace AmdGpu {
union PM4Type3Header; union PM4Type3Header;
@ -36,7 +37,7 @@ void ParseZInfo(u32 value, bool begin_table = true);
class CmdListViewer { class CmdListViewer {
const FrameDumpViewer* parent; DebugStateType::FrameDump* frame_dump;
uintptr_t base_addr; uintptr_t base_addr;
std::string name; std::string name;
@ -49,22 +50,22 @@ class CmdListViewer {
int batch_bp{-1}; int batch_bp{-1};
int vqid{255}; int vqid{255};
s32 highlight_batch{-1}; u32 highlight_batch{~0u};
RegView batch_view; RegView batch_view;
std::vector<RegView> extra_batch_view; std::vector<RegView> extra_batch_view;
void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body);
void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body); static void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body);
void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body); static void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body); static void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body); static void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body);
public: public:
static void LoadConfig(const char* line); static void LoadConfig(const char* line);
static void SerializeConfig(ImGuiTextBuffer* buf); static void SerializeConfig(ImGuiTextBuffer* buf);
explicit CmdListViewer(const FrameDumpViewer* parent, const std::vector<u32>& cmd_list, explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list,
uintptr_t base_addr = 0, std::string name = ""); uintptr_t base_addr = 0, std::string name = "");
void Draw(); void Draw();

View File

@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <variant>
#include "common/types.h"
#include "video_core/amdgpu/pm4_opcodes.h"
namespace Core::Devtools::Widget {
/*
* Generic PM4 header
*/
union PM4Header {
struct {
u32 reserved : 16;
u32 count : 14;
u32 type : 2; // PM4_TYPE
};
u32 u32All;
};
struct PushMarker {
std::string name{};
};
struct PopMarker {};
struct BatchBegin {
u32 id;
};
struct BatchInfo {
u32 id;
std::string marker{};
size_t start_addr;
size_t end_addr;
size_t command_addr;
AmdGpu::PM4ItOpcode type;
bool bypass{false};
};
using GPUEvent = std::variant<PushMarker, PopMarker, BatchBegin, BatchInfo>;
template <typename... Args>
void DrawRow(const char* text, const char* fmt, Args... args) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(text);
ImGui::TableNextColumn();
char buf[128];
snprintf(buf, sizeof(buf), fmt, args...);
ImGui::TextUnformatted(buf);
}
template <typename V, typename... Extra>
void DrawMultipleRow(const char* text, const char* fmt, V arg, Extra&&... extra_args) {
DrawRow(text, fmt, arg);
if constexpr (sizeof...(extra_args) > 0) {
DrawMultipleRow(std::forward<Extra>(extra_args)...);
}
}
template <typename... Args>
static void DoTooltip(const char* str_id, Args&&... args) {
if (ImGui::BeginTooltip()) {
if (ImGui::BeginTable(str_id, 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
DrawMultipleRow(std::forward<Args>(args)...);
ImGui::EndTable();
}
ImGui::EndTooltip();
}
}
} // namespace Core::Devtools::Widget

View File

@ -36,7 +36,8 @@ static std::array<char, 3> small_int_to_str(const s32 i) {
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_frame_dump)) { FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump)
: frame_dump(std::make_shared<FrameDump>(_frame_dump)) {
static int unique_id = 0; static int unique_id = 0;
id = unique_id++; id = unique_id++;
@ -44,15 +45,14 @@ FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_
selected_submit_num = 0; selected_submit_num = 0;
selected_queue_num2 = 0; selected_queue_num2 = 0;
cmd_list_viewer.reserve(frame_dump.queues.size()); cmd_list_viewer.reserve(frame_dump->queues.size());
for (const auto& cmd : frame_dump.queues) { for (const auto& cmd : frame_dump->queues) {
const auto fname = const auto fname =
fmt::format("{}_{}_{:02}_{:02}", id, magic_enum::enum_name(selected_queue_type), fmt::format("{}_{}_{:02}_{:02}", id, magic_enum::enum_name(selected_queue_type),
selected_submit_num, selected_queue_num2); selected_submit_num, selected_queue_num2);
cmd_list_viewer.emplace_back(this, cmd.data, cmd.base_addr, fname); cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname);
if (cmd.type == QueueType::dcb && cmd.submit_num == selected_submit_num && if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) {
cmd.num2 == selected_queue_num2) { selected_cmd = static_cast<s32>(cmd_list_viewer.size() - 1);
selected_cmd = cmd_list_viewer.size() - 1;
} }
} }
} }
@ -96,7 +96,7 @@ void FrameDumpViewer::Draw() {
magic_enum::enum_name(selected_queue_type), magic_enum::enum_name(selected_queue_type),
selected_submit_num, selected_queue_num2); selected_submit_num, selected_queue_num2);
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write); Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
auto& data = frame_dump.queues[selected_cmd].data; const auto& data = frame_dump->queues[selected_cmd].data;
if (file.IsOpen()) { if (file.IsOpen()) {
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname)); DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
file.Write(data); file.Write(data);
@ -112,7 +112,7 @@ void FrameDumpViewer::Draw() {
if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(), if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(),
ImGuiComboFlags_WidthFitPreview)) { ImGuiComboFlags_WidthFitPreview)) {
std::array<bool, 32> available_submits{}; std::array<bool, 32> available_submits{};
for (const auto& cmd : frame_dump.queues) { for (const auto& cmd : frame_dump->queues) {
if (cmd.type == selected_queue_type) { if (cmd.type == selected_queue_type) {
available_submits[cmd.submit_num] = true; available_submits[cmd.submit_num] = true;
} }
@ -137,7 +137,7 @@ void FrameDumpViewer::Draw() {
if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(), if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(),
ImGuiComboFlags_WidthFitPreview)) { ImGuiComboFlags_WidthFitPreview)) {
std::array<bool, 32> available_queues{}; std::array<bool, 32> available_queues{};
for (const auto& cmd : frame_dump.queues) { for (const auto& cmd : frame_dump->queues) {
if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) { if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) {
available_queues[cmd.num2] = true; available_queues[cmd.num2] = true;
} }
@ -152,12 +152,12 @@ void FrameDumpViewer::Draw() {
} }
} }
if (selected) { if (selected) {
const auto it = std::ranges::find_if(frame_dump.queues, [&](const auto& cmd) { const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) {
return cmd.type == selected_queue_type && return cmd.type == selected_queue_type &&
cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2; cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2;
}); });
if (it != frame_dump.queues.end()) { if (it != frame_dump->queues.end()) {
selected_cmd = std::distance(frame_dump.queues.begin(), it); selected_cmd = static_cast<s32>(std::distance(frame_dump->queues.begin(), it));
} }
} }
EndCombo(); EndCombo();

View File

@ -8,7 +8,6 @@
#include "cmd_list.h" #include "cmd_list.h"
#include "core/debug_state.h" #include "core/debug_state.h"
#include "imgui_memory_editor.h"
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
@ -17,7 +16,7 @@ class CmdListViewer;
class FrameDumpViewer { class FrameDumpViewer {
friend class CmdListViewer; friend class CmdListViewer;
DebugStateType::FrameDump frame_dump; std::shared_ptr<DebugStateType::FrameDump> frame_dump;
int id; int id;
std::vector<CmdListViewer> cmd_list_viewer; std::vector<CmdListViewer> cmd_list_viewer;
@ -30,7 +29,7 @@ class FrameDumpViewer {
public: public:
bool is_open = true; bool is_open = true;
explicit FrameDumpViewer(DebugStateType::FrameDump frame_dump); explicit FrameDumpViewer(const DebugStateType::FrameDump& frame_dump);
~FrameDumpViewer(); ~FrameDumpViewer();

View File

@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "reg_popup.h"
#include <cstdio>
#include <imgui.h>
#include <magic_enum.hpp>
#include "cmd_list.h"
#include "common.h"
using namespace ImGui;
using magic_enum::enum_name;
namespace Core::Devtools::Widget {
void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
if (BeginTable("COLOR_BUFFER", 2, ImGuiTableFlags_Borders)) {
TableNextRow();
// clang-format off
DrawMultipleRow(
"BASE_ADDR", "%X", buffer.base_address,
"PITCH.TILE_MAX", "%X", buffer.pitch.tile_max,
"PITCH.FMASK_TILE_MAX", "%X", buffer.pitch.fmask_tile_max,
"SLICE.TILE_MAX", "%X", buffer.slice.tile_max,
"VIEW.SLICE_START", "%X", buffer.view.slice_start,
"VIEW.SLICE_MAX", "%X", buffer.view.slice_max
);
TableNextRow();
TableNextColumn();
if (TreeNode("Color0Info")) {
TableNextRow();
TableNextColumn();
ParseColor0Info(buffer.info.u32all, false);
TreePop();
}
TableNextRow();
TableNextColumn();
if (TreeNode("Color0Attrib")) {
TableNextRow();
TableNextColumn();
ParseColor0Attrib(buffer.attrib.u32all, false);
TreePop();
}
TableNextRow();
DrawMultipleRow(
"CMASK_BASE_EXT", "%X", buffer.cmask_base_address,
"FMASK_BASE_EXT", "%X", buffer.fmask_base_address,
"FMASK_SLICE.TILE_MAX", "%X", buffer.fmask_slice.tile_max,
"CLEAR_WORD0", "%X", buffer.clear_word0,
"CLEAR_WORD1", "%X", buffer.clear_word1
);
DrawMultipleRow(
"Pitch()", "%X", buffer.Pitch(),
"Height()", "%X", buffer.Height(),
"Address()", "%X", buffer.Address(),
"CmaskAddress", "%X", buffer.CmaskAddress(),
"FmaskAddress", "%X", buffer.FmaskAddress(),
"NumSamples()", "%X", buffer.NumSamples(),
"NumSlices()", "%X", buffer.NumSlices(),
"GetColorSliceSize()", "%X", buffer.GetColorSliceSize()
);
auto tiling_mode = buffer.GetTilingMode();
auto num_format = buffer.NumFormat();
DrawRow("GetTilingMode()", "%X (%s)", tiling_mode, enum_name(tiling_mode).data());
DrawRow("IsTiled()", "%X", buffer.IsTiled());
DrawRow("NumFormat()", "%X (%s)", num_format, enum_name(num_format).data());
// clang-format on
EndTable();
}
}
RegPopup::RegPopup() {
static int unique_id = 0;
id = unique_id++;
}
void RegPopup::SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id) {
this->data = color_buffer;
this->title = fmt::format("Batch #{} CB #{}", batch_id, cb_id);
}
void RegPopup::Draw() {
char name[128];
snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id);
SetNextWindowSize({250.0f, 300.0f}, ImGuiCond_FirstUseEver);
if (Begin(name, &open, ImGuiWindowFlags_NoSavedSettings)) {
if (const auto* buffer = std::get_if<AmdGpu::Liverpool::ColorBuffer>(&data)) {
DrawColorBuffer(*buffer);
}
}
End();
}
} // namespace Core::Devtools::Widget

View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <variant>
#include "common/types.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
namespace Core::Devtools::Widget {
class RegPopup {
int id;
std::variant<AmdGpu::Liverpool::ColorBuffer> data;
std::string title{};
void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer);
public:
bool open = false;
RegPopup();
void SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id);
void Draw();
};
} // namespace Core::Devtools::Widget

View File

@ -8,7 +8,7 @@
#include <magic_enum.hpp> #include <magic_enum.hpp>
#include <stdio.h> #include <stdio.h>
#include "cmd_list.h" #include "common.h"
#include "common/io_file.h" #include "common/io_file.h"
#include "core/devtools/options.h" #include "core/devtools/options.h"
#include "imgui_internal.h" #include "imgui_internal.h"
@ -19,41 +19,9 @@
#define pclose _pclose #define pclose _pclose
#endif #endif
#include <iostream>
using namespace ImGui; using namespace ImGui;
using magic_enum::enum_name; using magic_enum::enum_name;
template <typename... Args>
void DrawRow(const char* text, const char* fmt, Args... args) {
TableNextColumn();
TextUnformatted(text);
TableNextColumn();
char buf[128];
snprintf(buf, sizeof(buf), fmt, args...);
TextUnformatted(buf);
}
template <typename V, typename... Extra>
void DrawMultipleRow(const char* text, const char* fmt, V arg, Extra&&... extra_args) {
DrawRow(text, fmt, arg);
if constexpr (sizeof...(extra_args) > 0) {
DrawMultipleRow(std::forward<Extra>(extra_args)...);
}
}
// Must end with EndTable
template <typename... Args>
static void DoTooltip(const char* str_id, Args&&... args) {
if (BeginTooltip()) {
if (BeginTable(str_id, 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
DrawMultipleRow(std::forward<Args>(args)...);
EndTable();
}
EndTooltip();
}
}
static std::optional<std::string> exec_cli(const char* cli) { static std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{}; std::array<char, 64> buffer{};
std::string output; std::string output;
@ -132,9 +100,9 @@ void RegView::DrawRegs() {
if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) { if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) {
auto& scissor = regs.screen_scissor; auto& s = regs.screen_scissor;
DrawRow("Scissor", "(%d, %d, %d, %d)", scissor.top_left_x, scissor.top_left_y, DrawRow("Scissor", "(%d, %d, %d, %d)", s.top_left_x, s.top_left_y, s.bottom_right_x,
scissor.bottom_right_x, scissor.bottom_right_y); s.bottom_right_y);
auto cc_mode = regs.color_control.mode.Value(); auto cc_mode = regs.color_control.mode.Value();
DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data()); DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data());
@ -147,77 +115,40 @@ void RegView::DrawRegs() {
const auto& buffer = regs.color_buffers[cb]; const auto& buffer = regs.color_buffers[cb];
bool open = opened_cb[cb]; const auto open_new_popup = [&] {
auto& pop = extra_reg_popup.emplace_back();
pop.SetData(buffer, batch_id, cb);
pop.open = true;
};
Text("Color buffer %d", cb);
TableNextColumn();
if (!buffer || !regs.color_target_mask.GetMask(cb)) { if (!buffer || !regs.color_target_mask.GetMask(cb)) {
Text("Color buffer %d", cb);
TableNextColumn();
TextUnformatted("N/A"); TextUnformatted("N/A");
} else if (last_selected_cb == cb && default_reg_popup.open) {
if (SmallButton("x")) {
if (GetIO().KeyShift) {
open_new_popup();
} else {
default_reg_popup.open = false;
}
}
} else { } else {
SetNextItemOpen(open); if (SmallButton("->")) {
bool keep_open = TreeNode("cb", "Color buffer %d", cb); if (GetIO().KeyShift) {
open = opened_cb[cb] = keep_open; open_new_popup();
} } else {
last_selected_cb = cb;
if (open) { default_reg_popup.SetData(buffer, batch_id, cb);
TableNextRow(); if (!default_reg_popup.open) {
default_reg_popup.open = true;
// clang-format off auto popup_pos =
GetCurrentContext()->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f);
DrawMultipleRow( SetNextWindowPos(popup_pos, ImGuiCond_Always);
"BASE_ADDR", "%X", buffer.base_address, default_reg_popup.Draw();
"PITCH.TILE_MAX", "%X", buffer.pitch.tile_max, }
"PITCH.FMASK_TILE_MAX", "%X", buffer.pitch.fmask_tile_max, }
"SLICE.TILE_MAX", "%X", buffer.slice.tile_max,
"VIEW.SLICE_START", "%X", buffer.view.slice_start,
"VIEW.SLICE_MAX", "%X", buffer.view.slice_max
);
TableNextRow();
TableNextColumn();
if (TreeNode("Color0Info")) {
TableNextRow();
TableNextColumn();
ParseColor0Info(buffer.info.u32all, false);
TreePop();
} }
TableNextRow();
TableNextColumn();
if (TreeNode("Color0Attrib")) {
TableNextRow();
TableNextColumn();
ParseColor0Attrib(buffer.attrib.u32all, false);
TreePop();
}
DrawMultipleRow(
"CMASK_BASE_EXT", "%X", buffer.cmask_base_address,
"FMASK_BASE_EXT", "%X", buffer.fmask_base_address,
"FMASK_SLICE.TILE_MAX", "%X", buffer.fmask_slice.tile_max,
"CLEAR_WORD0", "%X", buffer.clear_word0,
"CLEAR_WORD1", "%X", buffer.clear_word1
);
DrawMultipleRow(
"Pitch()", "%X", buffer.Pitch(),
"Height()", "%X", buffer.Height(),
"Address()", "%X", buffer.Address(),
"CmaskAddress", "%X", buffer.CmaskAddress(),
"FmaskAddress", "%X", buffer.FmaskAddress(),
"NumSamples()", "%X", buffer.NumSamples(),
"NumSlices()", "%X", buffer.NumSlices(),
"GetColorSliceSize()", "%X", buffer.GetColorSliceSize()
);
auto tiling_mode = buffer.GetTilingMode();
auto num_format = buffer.NumFormat();
DrawRow("GetTilingMode()", "%X (%s)", tiling_mode, enum_name(tiling_mode).data());
DrawRow("IsTiled()", "%X", buffer.IsTiled());
DrawRow("NumFormat()", "%X (%s)", num_format, enum_name(num_format).data());
// clang-format on
TreePop();
} }
PopID(); PopID();
@ -262,18 +193,20 @@ RegView::RegView() {
DockBuilderFinish(root_dock_id); DockBuilderFinish(root_dock_id);
} }
void RegView::SetData(DebugStateType::RegDump data) { void RegView::SetData(DebugStateType::RegDump data, u32 batch_id) {
this->data = std::move(data); this->data = std::move(data);
this->batch_id = batch_id;
// clear cache // clear cache
selected_shader = -1; selected_shader = -1;
opened_cb.fill(false);
shader_decomp.clear(); shader_decomp.clear();
default_reg_popup.open = false;
extra_reg_popup.clear();
} }
void RegView::Draw() { void RegView::Draw() {
char name[128]; char name[128];
snprintf(name, sizeof(name), "BatchView###reg_dump_%d", id); snprintf(name, sizeof(name), "BatchView %u###reg_dump_%d", batch_id, id);
if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) { if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) {
const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"}; const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"};
@ -346,6 +279,18 @@ void RegView::Draw() {
} }
End(); End();
} }
if (default_reg_popup.open) {
default_reg_popup.Draw();
}
for (auto it = extra_reg_popup.begin(); it != extra_reg_popup.end();) {
if (!it->open) {
it = extra_reg_popup.erase(it);
continue;
}
it->Draw();
++it;
}
} }
} // namespace Core::Devtools::Widget } // namespace Core::Devtools::Widget

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "core/debug_state.h" #include "core/debug_state.h"
#include "imgui_memory_editor.h" #include "imgui_memory_editor.h"
#include "reg_popup.h"
#include "text_editor.h" #include "text_editor.h"
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
@ -16,13 +17,15 @@ struct ShaderCache {
class RegView { class RegView {
int id; int id;
bool first_render = true;
DebugStateType::RegDump data; DebugStateType::RegDump data;
u32 batch_id{~0u};
std::unordered_map<int, ShaderCache> shader_decomp; std::unordered_map<int, ShaderCache> shader_decomp;
int selected_shader{-1}; int selected_shader{-1};
std::array<bool, AmdGpu::Liverpool::NumColorBuffers> opened_cb{}; RegPopup default_reg_popup;
int last_selected_cb{-1};
std::vector<RegPopup> extra_reg_popup;
bool show_registers{true}; bool show_registers{true};
bool show_user_data{true}; bool show_user_data{true};
@ -39,7 +42,7 @@ public:
RegView(); RegView();
void SetData(DebugStateType::RegDump data); void SetData(DebugStateType::RegDump data, u32 batch_id);
void Draw(); void Draw();
}; };

View File

@ -1,48 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <variant>
#include "common/types.h"
#include "video_core/amdgpu/pm4_opcodes.h"
namespace Core::Devtools::Widget {
/*
* Generic PM4 header
*/
union PM4Header {
struct {
u32 reserved : 16;
u32 count : 14;
u32 type : 2; // PM4_TYPE
};
u32 u32All;
};
struct PushMarker {
std::string name{};
};
struct PopMarker {};
struct BatchBegin {
u32 id;
};
struct BatchInfo {
u32 id;
std::string marker{};
size_t start_addr;
size_t end_addr;
size_t command_addr;
AmdGpu::PM4ItOpcode type;
bool bypass{false};
};
using GPUEvent = std::variant<PushMarker, PopMarker, BatchBegin, BatchInfo>;
} // namespace Core::Devtools::Widget

View File

@ -80,8 +80,6 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w
imgui_font_proggyvector_regular_compressed_size, imgui_font_proggyvector_regular_compressed_size,
16.0f); 16.0f);
GetCurrentContext()->DebugLogFlags |= ImGuiDebugLogFlags_EventDocking;
StyleColorsDark(); StyleColorsDark();
::Core::Devtools::Layer::SetupSettings(); ::Core::Devtools::Layer::SetupSettings();