mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-01-27 08:51:47 +00:00
More devtools stuff (#1637)
Some checks are pending
Build and Release / reuse (push) Waiting to run
Build and Release / clang-format (push) Waiting to run
Build and Release / get-info (push) Waiting to run
Build and Release / windows-sdl (push) Blocked by required conditions
Build and Release / windows-qt (push) Blocked by required conditions
Build and Release / macos-sdl (push) Blocked by required conditions
Build and Release / macos-qt (push) Blocked by required conditions
Build and Release / linux-sdl (push) Blocked by required conditions
Build and Release / linux-qt (push) Blocked by required conditions
Build and Release / pre-release (push) Blocked by required conditions
Some checks are pending
Build and Release / reuse (push) Waiting to run
Build and Release / clang-format (push) Waiting to run
Build and Release / get-info (push) Waiting to run
Build and Release / windows-sdl (push) Blocked by required conditions
Build and Release / windows-qt (push) Blocked by required conditions
Build and Release / macos-sdl (push) Blocked by required conditions
Build and Release / macos-qt (push) Blocked by required conditions
Build and Release / linux-sdl (push) Blocked by required conditions
Build and Release / linux-qt (push) Blocked by required conditions
Build and Release / pre-release (push) Blocked by required conditions
* devtools: memory map viewer * devtools: batch highlight only for non-group viewer * devtools: fix not showing entire user data * devtools: shader debug viewer * devtools: add more reg naming
This commit is contained in:
parent
f658fc58d1
commit
0835dc71b3
|
@ -435,10 +435,14 @@ set(DEV_TOOLS src/core/devtools/layer.cpp
|
|||
src/core/devtools/widget/frame_graph.cpp
|
||||
src/core/devtools/widget/frame_graph.h
|
||||
src/core/devtools/widget/imgui_memory_editor.h
|
||||
src/core/devtools/widget/memory_map.cpp
|
||||
src/core/devtools/widget/memory_map.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.h
|
||||
src/core/devtools/widget/shader_list.cpp
|
||||
src/core/devtools/widget/shader_list.h
|
||||
src/core/devtools/widget/text_editor.cpp
|
||||
src/core/devtools/widget/text_editor.h
|
||||
)
|
||||
|
|
|
@ -47,6 +47,7 @@ static std::string backButtonBehavior = "left";
|
|||
static bool useSpecialPad = false;
|
||||
static int specialPadClass = 1;
|
||||
static bool isDebugDump = false;
|
||||
static bool isShaderDebug = false;
|
||||
static bool isShowSplash = false;
|
||||
static bool isAutoUpdate = false;
|
||||
static bool isNullGpu = false;
|
||||
|
@ -159,6 +160,10 @@ bool debugDump() {
|
|||
return isDebugDump;
|
||||
}
|
||||
|
||||
bool collectShadersForDebug() {
|
||||
return isShaderDebug;
|
||||
}
|
||||
|
||||
bool showSplash() {
|
||||
return isShowSplash;
|
||||
}
|
||||
|
@ -235,6 +240,10 @@ void setDebugDump(bool enable) {
|
|||
isDebugDump = enable;
|
||||
}
|
||||
|
||||
void setCollectShaderForDebug(bool enable) {
|
||||
isShaderDebug = enable;
|
||||
}
|
||||
|
||||
void setShowSplash(bool enable) {
|
||||
isShowSplash = enable;
|
||||
}
|
||||
|
@ -571,6 +580,7 @@ void load(const std::filesystem::path& path) {
|
|||
const toml::value& debug = data.at("Debug");
|
||||
|
||||
isDebugDump = toml::find_or<bool>(debug, "DebugDump", false);
|
||||
isShaderDebug = toml::find_or<bool>(debug, "CollectShader", false);
|
||||
}
|
||||
|
||||
if (data.contains("GUI")) {
|
||||
|
@ -662,6 +672,7 @@ void save(const std::filesystem::path& path) {
|
|||
data["Vulkan"]["rdocMarkersEnable"] = vkMarkers;
|
||||
data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic;
|
||||
data["Debug"]["DebugDump"] = isDebugDump;
|
||||
data["Debug"]["CollectShader"] = isShaderDebug;
|
||||
data["GUI"]["theme"] = mw_themes;
|
||||
data["GUI"]["iconSize"] = m_icon_size;
|
||||
data["GUI"]["sliderPos"] = m_slider_pos;
|
||||
|
@ -717,6 +728,7 @@ void setDefaultValues() {
|
|||
useSpecialPad = false;
|
||||
specialPadClass = 1;
|
||||
isDebugDump = false;
|
||||
isShaderDebug = false;
|
||||
isShowSplash = false;
|
||||
isAutoUpdate = false;
|
||||
isNullGpu = false;
|
||||
|
|
|
@ -37,6 +37,7 @@ u32 getScreenHeight();
|
|||
s32 getGpuId();
|
||||
|
||||
bool debugDump();
|
||||
bool collectShadersForDebug();
|
||||
bool showSplash();
|
||||
bool autoUpdate();
|
||||
bool nullGpu();
|
||||
|
@ -47,6 +48,7 @@ bool isRdocEnabled();
|
|||
u32 vblankDiv();
|
||||
|
||||
void setDebugDump(bool enable);
|
||||
void setCollectShaderForDebug(bool enable);
|
||||
void setShowSplash(bool enable);
|
||||
void setAutoUpdate(bool enable);
|
||||
void setNullGpu(bool enable);
|
||||
|
|
|
@ -157,7 +157,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
|||
if (is_compute) {
|
||||
dump.is_compute = true;
|
||||
const auto& cs = dump.regs.cs_program;
|
||||
dump.cs_data = ComputerShaderDump{
|
||||
dump.cs_data = PipelineComputerProgramDump{
|
||||
.cs_program = cs,
|
||||
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
|
||||
};
|
||||
|
@ -167,7 +167,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
|||
auto stage = regs.ProgramForStage(i);
|
||||
if (stage->address_lo != 0) {
|
||||
auto code = stage->Code();
|
||||
dump.stages[i] = ShaderDump{
|
||||
dump.stages[i] = PipelineShaderProgramDump{
|
||||
.user_data = *stage,
|
||||
.code = std::vector<u32>{code.begin(), code.end()},
|
||||
};
|
||||
|
@ -176,3 +176,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugStateImpl::CollectShader(const std::string& name, std::span<const u32> spv,
|
||||
std::span<const u32> raw_code) {
|
||||
shader_dump_list.emplace_back(name, std::vector<u32>{spv.begin(), spv.end()},
|
||||
std::vector<u32>{raw_code.begin(), raw_code.end()});
|
||||
std::ranges::sort(shader_dump_list, {}, &ShaderDump::name);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ namespace Core::Devtools {
|
|||
class Layer;
|
||||
namespace Widget {
|
||||
class FrameGraph;
|
||||
}
|
||||
class ShaderList;
|
||||
} // namespace Widget
|
||||
} // namespace Core::Devtools
|
||||
|
||||
namespace DebugStateType {
|
||||
|
@ -49,12 +50,12 @@ struct QueueDump {
|
|||
uintptr_t base_addr;
|
||||
};
|
||||
|
||||
struct ShaderDump {
|
||||
struct PipelineShaderProgramDump {
|
||||
Vulkan::Liverpool::ShaderProgram user_data{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
|
||||
struct ComputerShaderDump {
|
||||
struct PipelineComputerProgramDump {
|
||||
Vulkan::Liverpool::ComputeProgram cs_program{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
|
@ -63,8 +64,8 @@ struct RegDump {
|
|||
bool is_compute{false};
|
||||
static constexpr size_t MaxShaderStages = 5;
|
||||
Vulkan::Liverpool::Regs regs{};
|
||||
std::array<ShaderDump, MaxShaderStages> stages{};
|
||||
ComputerShaderDump cs_data{};
|
||||
std::array<PipelineShaderProgramDump, MaxShaderStages> stages{};
|
||||
PipelineComputerProgramDump cs_data{};
|
||||
};
|
||||
|
||||
struct FrameDump {
|
||||
|
@ -73,9 +74,41 @@ struct FrameDump {
|
|||
std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump
|
||||
};
|
||||
|
||||
struct ShaderDump {
|
||||
std::string name;
|
||||
std::vector<u32> spv;
|
||||
std::vector<u32> raw_code;
|
||||
|
||||
std::string cache_spv_disasm{};
|
||||
std::string cache_raw_disasm{};
|
||||
|
||||
ShaderDump(std::string name, std::vector<u32> spv, std::vector<u32> raw_code)
|
||||
: name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {}
|
||||
|
||||
ShaderDump(const ShaderDump& other) = delete;
|
||||
ShaderDump(ShaderDump&& other) noexcept
|
||||
: name{std::move(other.name)}, spv{std::move(other.spv)},
|
||||
raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)},
|
||||
cache_raw_disasm{std::move(other.cache_raw_disasm)} {}
|
||||
ShaderDump& operator=(const ShaderDump& other) = delete;
|
||||
ShaderDump& operator=(ShaderDump&& other) noexcept {
|
||||
if (this == &other)
|
||||
return *this;
|
||||
name = std::move(other.name);
|
||||
spv = std::move(other.spv);
|
||||
raw_code = std::move(other.raw_code);
|
||||
cache_spv_disasm = std::move(other.cache_spv_disasm);
|
||||
cache_raw_disasm = std::move(other.cache_raw_disasm);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class DebugStateImpl {
|
||||
friend class Core::Devtools::Layer;
|
||||
friend class Core::Devtools::Widget::FrameGraph;
|
||||
friend class Core::Devtools::Widget::ShaderList;
|
||||
|
||||
std::queue<std::string> debug_message_popup;
|
||||
|
||||
std::mutex guest_threads_mutex{};
|
||||
std::vector<ThreadID> guest_threads{};
|
||||
|
@ -94,7 +127,7 @@ class DebugStateImpl {
|
|||
std::shared_mutex frame_dump_list_mutex;
|
||||
std::vector<FrameDump> frame_dump_list{};
|
||||
|
||||
std::queue<std::string> debug_message_popup;
|
||||
std::vector<ShaderDump> shader_dump_list{};
|
||||
|
||||
public:
|
||||
void ShowDebugMessage(std::string message) {
|
||||
|
@ -152,6 +185,9 @@ public:
|
|||
|
||||
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
|
||||
|
||||
void CollectShader(const std::string& name, std::span<const u32> spv,
|
||||
std::span<const u32> raw_code);
|
||||
};
|
||||
} // namespace DebugStateType
|
||||
|
||||
|
|
|
@ -289,6 +289,16 @@ const char* GetContextRegName(u32 reg_offset) {
|
|||
return "mmSPI_PS_INPUT_CNTL_2";
|
||||
case mmSPI_PS_INPUT_CNTL_3:
|
||||
return "mmSPI_PS_INPUT_CNTL_3";
|
||||
case mmPA_SU_POLY_OFFSET_FRONT_SCALE:
|
||||
return "mmPA_SU_POLY_OFFSET_FRONT_SCALE";
|
||||
case mmPA_SU_POLY_OFFSET_FRONT_OFFSET:
|
||||
return "mmPA_SU_POLY_OFFSET_FRONT_OFFSET";
|
||||
case mmPA_SU_POLY_OFFSET_BACK_SCALE:
|
||||
return "mmPA_SU_POLY_OFFSET_BACK_SCALE";
|
||||
case mmPA_SU_POLY_OFFSET_BACK_OFFSET:
|
||||
return "mmPA_SU_POLY_OFFSET_BACK_OFFSET";
|
||||
case mmPA_SU_POLY_OFFSET_CLAMP:
|
||||
return "mmPA_SU_POLY_OFFSET_CLAMP";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "layer.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/config.h"
|
||||
|
@ -9,11 +11,12 @@
|
|||
#include "core/debug_state.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "layer.h"
|
||||
#include "options.h"
|
||||
#include "video_core/renderer_vulkan/vk_presenter.h"
|
||||
#include "widget/frame_dump.h"
|
||||
#include "widget/frame_graph.h"
|
||||
#include "widget/memory_map.h"
|
||||
#include "widget/shader_list.h"
|
||||
|
||||
extern std::unique_ptr<Vulkan::Presenter> presenter;
|
||||
|
||||
|
@ -35,6 +38,9 @@ static float debug_popup_timing = 3.0f;
|
|||
|
||||
static bool just_opened_options = false;
|
||||
|
||||
static Widget::MemoryMapViewer memory_map;
|
||||
static Widget::ShaderList shader_list;
|
||||
|
||||
// clang-format off
|
||||
static std::string help_text =
|
||||
#include "help.txt"
|
||||
|
@ -63,6 +69,7 @@ void L::DrawMenuBar() {
|
|||
}
|
||||
if (BeginMenu("GPU Tools")) {
|
||||
MenuItem("Show frame info", nullptr, &frame_graph.is_open);
|
||||
MenuItem("Show loaded shaders", nullptr, &shader_list.open);
|
||||
if (BeginMenu("Dump frames")) {
|
||||
SliderInt("Count", &dump_frame_count, 1, 5);
|
||||
if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) {
|
||||
|
@ -81,6 +88,12 @@ void L::DrawMenuBar() {
|
|||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (BeginMenu("Debug")) {
|
||||
if (MenuItem("Memory map")) {
|
||||
memory_map.open = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
EndMainMenuBar();
|
||||
}
|
||||
|
||||
|
@ -175,19 +188,29 @@ void L::DrawAdvanced() {
|
|||
bool close_popup_options = true;
|
||||
if (BeginPopupModal("GPU Tools Options", &close_popup_options,
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
static char disassembly_cli[512];
|
||||
static char disassembler_cli_isa[512];
|
||||
static char disassembler_cli_spv[512];
|
||||
static bool frame_dump_render_on_collapse;
|
||||
|
||||
if (just_opened_options) {
|
||||
just_opened_options = false;
|
||||
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1);
|
||||
disassembly_cli[s] = '\0';
|
||||
auto s = Options.disassembler_cli_isa.copy(disassembler_cli_isa,
|
||||
sizeof(disassembler_cli_isa) - 1);
|
||||
disassembler_cli_isa[s] = '\0';
|
||||
s = Options.disassembler_cli_spv.copy(disassembler_cli_spv,
|
||||
sizeof(disassembler_cli_spv) - 1);
|
||||
disassembler_cli_spv[s] = '\0';
|
||||
frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse;
|
||||
}
|
||||
|
||||
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli));
|
||||
InputText("Shader isa disassembler: ", disassembler_cli_isa, sizeof(disassembler_cli_isa));
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")");
|
||||
SetTooltip(R"(Command to disassemble shaders. Example: dis.exe --raw "{src}")");
|
||||
}
|
||||
InputText("Shader SPIRV disassembler: ", disassembler_cli_spv,
|
||||
sizeof(disassembler_cli_spv));
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip(R"(Command to disassemble shaders. Example: spirv-cross -V "{src}")");
|
||||
}
|
||||
Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse);
|
||||
if (IsItemHovered()) {
|
||||
|
@ -196,7 +219,8 @@ void L::DrawAdvanced() {
|
|||
}
|
||||
|
||||
if (Button("Save")) {
|
||||
Options.disassembly_cli = disassembly_cli;
|
||||
Options.disassembler_cli_isa = disassembler_cli_isa;
|
||||
Options.disassembler_cli_spv = disassembler_cli_spv;
|
||||
Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse;
|
||||
SaveIniSettingsToDisk(io.IniFilename);
|
||||
CloseCurrentPopup();
|
||||
|
@ -219,6 +243,13 @@ void L::DrawAdvanced() {
|
|||
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
if (memory_map.open) {
|
||||
memory_map.Draw();
|
||||
}
|
||||
if (shader_list.open) {
|
||||
shader_list.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawSimple() {
|
||||
|
|
|
@ -12,8 +12,12 @@ TOptions Options;
|
|||
void LoadOptionsConfig(const char* line) {
|
||||
char str[512];
|
||||
int i;
|
||||
if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) {
|
||||
Options.disassembly_cli = str;
|
||||
if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) {
|
||||
Options.disassembler_cli_isa = str;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "disassembler_cli_spv=%511[^\n]", str) == 1) {
|
||||
Options.disassembler_cli_spv = str;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) {
|
||||
|
@ -23,7 +27,8 @@ void LoadOptionsConfig(const char* line) {
|
|||
}
|
||||
|
||||
void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
|
||||
buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str());
|
||||
buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str());
|
||||
buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str());
|
||||
buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ struct ImGuiTextBuffer;
|
|||
namespace Core::Devtools {
|
||||
|
||||
struct TOptions {
|
||||
std::string disassembly_cli{};
|
||||
std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""};
|
||||
std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""};
|
||||
bool frame_dump_render_on_collapse{false};
|
||||
};
|
||||
|
||||
|
|
|
@ -1306,7 +1306,7 @@ void CmdListViewer::Draw(bool only_batches_view) {
|
|||
if (batch.id == batch_bp) { // highlight batch at breakpoint
|
||||
PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f});
|
||||
}
|
||||
if (batch.id == highlight_batch) {
|
||||
if (batch.id == highlight_batch && !group_batches) {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
|
||||
}
|
||||
|
||||
|
@ -1459,7 +1459,7 @@ void CmdListViewer::Draw(bool only_batches_view) {
|
|||
}
|
||||
}
|
||||
|
||||
if (batch.id == highlight_batch) {
|
||||
if (batch.id == highlight_batch && !group_batches) {
|
||||
PopStyleColor();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
@ -10,9 +11,16 @@
|
|||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/types.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "video_core/amdgpu/pm4_opcodes.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#endif
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
/*
|
||||
* Generic PM4 header
|
||||
|
@ -106,4 +114,53 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
|
|||
}
|
||||
}
|
||||
|
||||
inline std::optional<std::string> exec_cli(const char* cli) {
|
||||
std::array<char, 64> buffer{};
|
||||
std::string output;
|
||||
const auto f = popen(cli, "r");
|
||||
if (!f) {
|
||||
pclose(f);
|
||||
return {};
|
||||
}
|
||||
while (fgets(buffer.data(), buffer.size(), f)) {
|
||||
output += buffer.data();
|
||||
}
|
||||
pclose(f);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline std::string RunDisassembler(const std::string& disassembler_cli,
|
||||
const std::vector<u32>& shader_code) {
|
||||
std::string shader_dis;
|
||||
|
||||
if (disassembler_cli.empty()) {
|
||||
shader_dis = "No disassembler set";
|
||||
} else {
|
||||
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
|
||||
|
||||
constexpr std::string_view src_arg = "{src}";
|
||||
std::string cli = disassembler_cli;
|
||||
const auto pos = cli.find(src_arg);
|
||||
if (pos == std::string::npos) {
|
||||
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" +
|
||||
disassembler_cli);
|
||||
} else {
|
||||
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
|
||||
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
|
||||
file.Write(shader_code);
|
||||
file.Close();
|
||||
|
||||
auto result = exec_cli(cli.c_str());
|
||||
shader_dis = result.value_or("Could not disassemble shader");
|
||||
if (shader_dis.empty()) {
|
||||
shader_dis = "Disassembly empty or failed";
|
||||
}
|
||||
|
||||
std::filesystem::remove(bin_path);
|
||||
}
|
||||
}
|
||||
|
||||
return shader_dis;
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
134
src/core/devtools/widget/memory_map.cpp
Normal file
134
src/core/devtools/widget/memory_map.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "core/debug_state.h"
|
||||
#include "core/memory.h"
|
||||
#include "memory_map.h"
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
bool MemoryMapViewer::Iterator::DrawLine() {
|
||||
if (is_vma) {
|
||||
if (vma.it == vma.end) {
|
||||
return false;
|
||||
}
|
||||
auto m = vma.it->second;
|
||||
if (m.type == VMAType::Free) {
|
||||
++vma.it;
|
||||
return DrawLine();
|
||||
}
|
||||
TableNextColumn();
|
||||
Text("%zX", m.base);
|
||||
TableNextColumn();
|
||||
Text("%zX", m.size);
|
||||
TableNextColumn();
|
||||
Text("%s", magic_enum::enum_name(m.type).data());
|
||||
TableNextColumn();
|
||||
Text("%s", magic_enum::enum_name(m.prot).data());
|
||||
TableNextColumn();
|
||||
if (m.is_exec) {
|
||||
Text("X");
|
||||
}
|
||||
TableNextColumn();
|
||||
Text("%s", m.name.c_str());
|
||||
++vma.it;
|
||||
return true;
|
||||
}
|
||||
if (dmem.it == dmem.end) {
|
||||
return false;
|
||||
}
|
||||
auto m = dmem.it->second;
|
||||
if (m.is_free) {
|
||||
++dmem.it;
|
||||
return DrawLine();
|
||||
}
|
||||
TableNextColumn();
|
||||
Text("%llX", m.base);
|
||||
TableNextColumn();
|
||||
Text("%llX", m.size);
|
||||
TableNextColumn();
|
||||
auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type);
|
||||
Text("%s", magic_enum::enum_name(type).data());
|
||||
TableNextColumn();
|
||||
Text("%d", m.is_pooled);
|
||||
++dmem.it;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryMapViewer::Draw() {
|
||||
SetNextWindowSize({600.0f, 500.0f}, ImGuiCond_FirstUseEver);
|
||||
if (!Begin("Memory map", &open)) {
|
||||
End();
|
||||
return;
|
||||
}
|
||||
|
||||
auto mem = Memory::Instance();
|
||||
std::scoped_lock lck{mem->mutex};
|
||||
|
||||
{
|
||||
bool next_showing_vma = showing_vma;
|
||||
if (showing_vma) {
|
||||
PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
|
||||
}
|
||||
if (Button("VMem")) {
|
||||
next_showing_vma = true;
|
||||
}
|
||||
if (showing_vma) {
|
||||
PopStyleColor();
|
||||
}
|
||||
SameLine();
|
||||
if (!showing_vma) {
|
||||
PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
|
||||
}
|
||||
if (Button("DMem")) {
|
||||
next_showing_vma = false;
|
||||
}
|
||||
if (!showing_vma) {
|
||||
PopStyleColor();
|
||||
}
|
||||
showing_vma = next_showing_vma;
|
||||
}
|
||||
|
||||
Iterator it{};
|
||||
if (showing_vma) {
|
||||
it.is_vma = true;
|
||||
it.vma.it = mem->vma_map.begin();
|
||||
it.vma.end = mem->vma_map.end();
|
||||
} else {
|
||||
it.is_vma = false;
|
||||
it.dmem.it = mem->dmem_map.begin();
|
||||
it.dmem.end = mem->dmem_map.end();
|
||||
}
|
||||
|
||||
if (BeginTable("memory_view_table", showing_vma ? 6 : 4,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_SizingFixedFit)) {
|
||||
if (showing_vma) {
|
||||
TableSetupColumn("Address");
|
||||
TableSetupColumn("Size");
|
||||
TableSetupColumn("Type");
|
||||
TableSetupColumn("Prot");
|
||||
TableSetupColumn("Is Exec");
|
||||
TableSetupColumn("Name");
|
||||
} else {
|
||||
TableSetupColumn("Address");
|
||||
TableSetupColumn("Size");
|
||||
TableSetupColumn("Type");
|
||||
TableSetupColumn("Pooled");
|
||||
}
|
||||
TableHeadersRow();
|
||||
|
||||
while (it.DrawLine())
|
||||
;
|
||||
EndTable();
|
||||
}
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
33
src/core/devtools/widget/memory_map.h
Normal file
33
src/core/devtools/widget/memory_map.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class MemoryMapViewer {
|
||||
struct Iterator {
|
||||
bool is_vma;
|
||||
struct {
|
||||
MemoryManager::DMemMap::iterator it;
|
||||
MemoryManager::DMemMap::iterator end;
|
||||
} dmem;
|
||||
struct {
|
||||
MemoryManager::VMAMap::iterator it;
|
||||
MemoryManager::VMAMap::iterator end;
|
||||
} vma;
|
||||
|
||||
bool DrawLine();
|
||||
};
|
||||
|
||||
bool showing_vma = true;
|
||||
|
||||
public:
|
||||
bool open = false;
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
|
@ -25,21 +25,6 @@ using magic_enum::enum_name;
|
|||
|
||||
constexpr auto depth_id = 0xF3;
|
||||
|
||||
static std::optional<std::string> exec_cli(const char* cli) {
|
||||
std::array<char, 64> buffer{};
|
||||
std::string output;
|
||||
const auto f = popen(cli, "r");
|
||||
if (!f) {
|
||||
pclose(f);
|
||||
return {};
|
||||
}
|
||||
while (fgets(buffer.data(), buffer.size(), f)) {
|
||||
output += buffer.data();
|
||||
}
|
||||
pclose(f);
|
||||
return output;
|
||||
}
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
void RegView::ProcessShader(int shader_id) {
|
||||
|
@ -54,38 +39,12 @@ void RegView::ProcessShader(int shader_id) {
|
|||
user_data = s.user_data.user_data;
|
||||
}
|
||||
|
||||
std::string shader_dis;
|
||||
|
||||
if (Options.disassembly_cli.empty()) {
|
||||
shader_dis = "No disassembler set";
|
||||
} else {
|
||||
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
|
||||
|
||||
constexpr std::string_view src_arg = "{src}";
|
||||
std::string cli = Options.disassembly_cli;
|
||||
const auto pos = cli.find(src_arg);
|
||||
if (pos == std::string::npos) {
|
||||
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument");
|
||||
} else {
|
||||
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
|
||||
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
|
||||
file.Write(shader_code);
|
||||
file.Close();
|
||||
|
||||
auto result = exec_cli(cli.c_str());
|
||||
shader_dis = result.value_or("Could not disassemble shader");
|
||||
if (shader_dis.empty()) {
|
||||
shader_dis = "Disassembly empty or failed";
|
||||
}
|
||||
|
||||
std::filesystem::remove(bin_path);
|
||||
}
|
||||
}
|
||||
std::string shader_dis = RunDisassembler(Options.disassembler_cli_isa, shader_code);
|
||||
|
||||
MemoryEditor hex_view;
|
||||
hex_view.Open = true;
|
||||
hex_view.ReadOnly = true;
|
||||
hex_view.Cols = 8;
|
||||
hex_view.Cols = 16;
|
||||
hex_view.OptShowAscii = false;
|
||||
hex_view.OptShowOptions = false;
|
||||
|
||||
|
@ -376,7 +335,9 @@ void RegView::Draw() {
|
|||
if (!shader) {
|
||||
Text("Stage not selected");
|
||||
} else {
|
||||
shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size());
|
||||
shader->hex_view.DrawContents(shader->user_data.data(),
|
||||
shader->user_data.size() *
|
||||
sizeof(Vulkan::Liverpool::UserData::value_type));
|
||||
}
|
||||
}
|
||||
End();
|
||||
|
|
95
src/core/devtools/widget/shader_list.cpp
Normal file
95
src/core/devtools/widget/shader_list.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_list.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "common/config.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/options.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
void ShaderList::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
if (!loaded_data) {
|
||||
loaded_data = true;
|
||||
if (value.cache_raw_disasm.empty()) {
|
||||
value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code);
|
||||
}
|
||||
isa_editor.SetText(value.cache_raw_disasm);
|
||||
|
||||
if (value.cache_spv_disasm.empty()) {
|
||||
value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv);
|
||||
}
|
||||
spv_editor.SetText(value.cache_spv_disasm);
|
||||
}
|
||||
|
||||
if (SmallButton("<-")) {
|
||||
selected_shader = -1;
|
||||
}
|
||||
SameLine();
|
||||
Text("%s", value.name.c_str());
|
||||
SameLine(0.0f, 7.0f);
|
||||
if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) {
|
||||
if (Selectable("SPIRV")) {
|
||||
showing_isa = false;
|
||||
}
|
||||
if (Selectable("ISA")) {
|
||||
showing_isa = true;
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
|
||||
if (showing_isa) {
|
||||
isa_editor.Render("ISA", GetContentRegionAvail());
|
||||
} else {
|
||||
spv_editor.Render("SPIRV", GetContentRegionAvail());
|
||||
}
|
||||
}
|
||||
|
||||
ShaderList::ShaderList() {
|
||||
isa_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
isa_editor.SetReadOnly(true);
|
||||
spv_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
spv_editor.SetReadOnly(true);
|
||||
spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
|
||||
}
|
||||
|
||||
void ShaderList::Draw() {
|
||||
SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver);
|
||||
if (!Begin("Shader list", &open)) {
|
||||
End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Config::collectShadersForDebug()) {
|
||||
DrawCenteredText("Enable 'CollectShader' in config to see shaders");
|
||||
End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected_shader >= 0) {
|
||||
DrawShader(DebugState.shader_dump_list[selected_shader]);
|
||||
End();
|
||||
return;
|
||||
}
|
||||
|
||||
auto width = GetContentRegionAvail().x;
|
||||
int i = 0;
|
||||
for (const auto& shader : DebugState.shader_dump_list) {
|
||||
if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) {
|
||||
selected_shader = i;
|
||||
loaded_data = false;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
28
src/core/devtools/widget/shader_list.h
Normal file
28
src/core/devtools/widget/shader_list.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/debug_state.h"
|
||||
#include "text_editor.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class ShaderList {
|
||||
int selected_shader = -1;
|
||||
TextEditor isa_editor{};
|
||||
TextEditor spv_editor{};
|
||||
bool loaded_data = false;
|
||||
bool showing_isa = false;
|
||||
|
||||
void DrawShader(DebugStateType::ShaderDump& value);
|
||||
|
||||
public:
|
||||
ShaderList();
|
||||
|
||||
bool open = false;
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
|
@ -20,6 +20,10 @@ namespace Libraries::Kernel {
|
|||
struct OrbisQueryInfo;
|
||||
}
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
class MemoryMapViewer;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
enum class MemoryProt : u32 {
|
||||
|
@ -257,6 +261,8 @@ private:
|
|||
size_t total_flexible_size{};
|
||||
size_t flexible_usage{};
|
||||
Vulkan::Rasterizer* rasterizer{};
|
||||
|
||||
friend class ::Core::Devtools::Widget::MemoryMapViewer;
|
||||
};
|
||||
|
||||
using Memory = Common::Singleton<MemoryManager>;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "common/hash.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/recompiler.h"
|
||||
|
@ -416,6 +417,9 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info,
|
|||
const auto module = CompileSPV(spv, instance.GetDevice());
|
||||
const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx);
|
||||
Vulkan::SetObjectName(instance.GetDevice(), module, name);
|
||||
if (Config::collectShadersForDebug()) {
|
||||
DebugState.CollectShader(name, spv, code);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue