mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2024-12-28 18:46:06 +00:00
Devtools - Inspect regs/User data/Shader disassembly (#1358)
* devtools: pm4 - show markers * SaveDataDialogLib: fix compile with mingw * devtools: pm4 - show program state * devtools: pm4 - show program disassembly * devtools: pm4 - show frame regs * devtools: pm4 - show color buffer info as popup add ux improvements for open new windows with shift+click better window titles * imgui: skip all textures to avoid hanging with crash diagnostic enabled not sure why this happens :c * devtools: pm4 - show reg depth buffer
This commit is contained in:
parent
8776eba8c8
commit
cf2e617f08
|
@ -354,15 +354,25 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
|
||||||
|
|
||||||
set(DEV_TOOLS src/core/devtools/layer.cpp
|
set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||||
src/core/devtools/layer.h
|
src/core/devtools/layer.h
|
||||||
|
src/core/devtools/options.cpp
|
||||||
|
src/core/devtools/options.h
|
||||||
src/core/devtools/gcn/gcn_context_regs.cpp
|
src/core/devtools/gcn/gcn_context_regs.cpp
|
||||||
src/core/devtools/gcn/gcn_op_names.cpp
|
src/core/devtools/gcn/gcn_op_names.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/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/text_editor.cpp
|
||||||
|
src/core/devtools/widget/text_editor.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(COMMON src/common/logging/backend.cpp
|
set(COMMON src/common/logging/backend.cpp
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
// 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 "libraries/kernel/event_queues.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"
|
||||||
|
|
||||||
using namespace DebugStateType;
|
using namespace DebugStateType;
|
||||||
|
|
||||||
|
@ -95,8 +98,68 @@ void DebugStateImpl::ResumeGuestThreads() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugStateImpl::RequestFrameDump(s32 count) {
|
void DebugStateImpl::RequestFrameDump(s32 count) {
|
||||||
|
ASSERT(!DumpingCurrentFrame());
|
||||||
gnm_frame_dump_request_count = count;
|
gnm_frame_dump_request_count = count;
|
||||||
frame_dump_list.clear();
|
frame_dump_list.clear();
|
||||||
frame_dump_list.resize(count);
|
frame_dump_list.resize(count);
|
||||||
waiting_submit_pause = true;
|
waiting_submit_pause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugStateImpl::PushQueueDump(QueueDump dump) {
|
||||||
|
ASSERT(DumpingCurrentFrame());
|
||||||
|
std::unique_lock lock{frame_dump_list_mutex};
|
||||||
|
auto& frame = GetFrameDump();
|
||||||
|
{ // Find draw calls
|
||||||
|
auto data = std::span{dump.data};
|
||||||
|
auto initial_data = data.data();
|
||||||
|
while (!data.empty()) {
|
||||||
|
const auto* header = reinterpret_cast<const AmdGpu::PM4Type3Header*>(data.data());
|
||||||
|
const auto type = header->type;
|
||||||
|
if (type == 2) {
|
||||||
|
data = data.subspan(1);
|
||||||
|
} else if (type != 3) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
const AmdGpu::PM4ItOpcode opcode = header->opcode;
|
||||||
|
if (Core::Devtools::Widget::IsDrawCall(opcode)) {
|
||||||
|
const auto offset =
|
||||||
|
reinterpret_cast<uintptr_t>(header) - reinterpret_cast<uintptr_t>(initial_data);
|
||||||
|
const auto addr = dump.base_addr + offset;
|
||||||
|
waiting_reg_dumps.emplace(addr, &frame);
|
||||||
|
waiting_reg_dumps_dbg.emplace(
|
||||||
|
addr,
|
||||||
|
fmt::format("#{} h({}) queue {} {} {}",
|
||||||
|
frame_dump_list.size() - gnm_frame_dump_request_count, addr,
|
||||||
|
magic_enum::enum_name(dump.type), dump.submit_num, dump.num2));
|
||||||
|
}
|
||||||
|
data = data.subspan(header->NumWords() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.queues.push_back(std::move(dump));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||||
|
const AmdGpu::Liverpool::Regs& regs) {
|
||||||
|
std::scoped_lock lock{frame_dump_list_mutex};
|
||||||
|
const auto it = waiting_reg_dumps.find(header_addr);
|
||||||
|
if (it == waiting_reg_dumps.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& frame = *it->second;
|
||||||
|
waiting_reg_dumps.erase(it);
|
||||||
|
waiting_reg_dumps_dbg.erase(waiting_reg_dumps_dbg.find(header_addr));
|
||||||
|
auto& dump = frame.regs[header_addr - base_addr];
|
||||||
|
dump.regs = regs;
|
||||||
|
for (int i = 0; i < RegDump::MaxShaderStages; i++) {
|
||||||
|
if (regs.stage_enable.IsStageEnabled(i)) {
|
||||||
|
auto stage = regs.ProgramForStage(i);
|
||||||
|
if (stage->address_lo != 0) {
|
||||||
|
auto code = stage->Code();
|
||||||
|
dump.stages[i] = ShaderDump{
|
||||||
|
.user_data = *stage,
|
||||||
|
.code = std::vector<u32>{code.begin(), code.end()},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,10 +5,14 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "video_core/amdgpu/liverpool.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
@ -42,10 +46,23 @@ struct QueueDump {
|
||||||
u32 submit_num;
|
u32 submit_num;
|
||||||
u32 num2; // acb: queue_num; else: buffer_in_submit
|
u32 num2; // acb: queue_num; else: buffer_in_submit
|
||||||
std::vector<u32> data;
|
std::vector<u32> data;
|
||||||
|
uintptr_t base_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderDump {
|
||||||
|
Vulkan::Liverpool::ShaderProgram user_data{};
|
||||||
|
std::vector<u32> code{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegDump {
|
||||||
|
static constexpr size_t MaxShaderStages = 5;
|
||||||
|
Vulkan::Liverpool::Regs regs{};
|
||||||
|
std::array<ShaderDump, MaxShaderStages> stages{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FrameDump {
|
struct FrameDump {
|
||||||
std::vector<QueueDump> queues;
|
std::vector<QueueDump> queues;
|
||||||
|
std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump
|
||||||
};
|
};
|
||||||
|
|
||||||
class DebugStateImpl {
|
class DebugStateImpl {
|
||||||
|
@ -61,15 +78,24 @@ class DebugStateImpl {
|
||||||
std::atomic_int32_t gnm_frame_count = 0;
|
std::atomic_int32_t gnm_frame_count = 0;
|
||||||
|
|
||||||
s32 gnm_frame_dump_request_count = -1;
|
s32 gnm_frame_dump_request_count = -1;
|
||||||
|
std::unordered_map<size_t, FrameDump*> waiting_reg_dumps;
|
||||||
|
std::unordered_map<size_t, std::string> waiting_reg_dumps_dbg;
|
||||||
bool waiting_submit_pause = false;
|
bool waiting_submit_pause = false;
|
||||||
bool should_show_frame_dump = false;
|
bool should_show_frame_dump = false;
|
||||||
|
|
||||||
std::mutex frame_dump_list_mutex;
|
std::shared_mutex frame_dump_list_mutex;
|
||||||
std::vector<FrameDump> frame_dump_list{};
|
std::vector<FrameDump> frame_dump_list{};
|
||||||
|
|
||||||
std::queue<std::string> debug_message_popup;
|
std::queue<std::string> debug_message_popup;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
void ShowDebugMessage(std::string message) {
|
||||||
|
if (message.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debug_message_popup.push(std::move(message));
|
||||||
|
}
|
||||||
|
|
||||||
void AddCurrentThreadToGuestList();
|
void AddCurrentThreadToGuestList();
|
||||||
|
|
||||||
void RemoveCurrentThreadFromGuestList();
|
void RemoveCurrentThreadFromGuestList();
|
||||||
|
@ -99,6 +125,11 @@ public:
|
||||||
return gnm_frame_dump_request_count > 0;
|
return gnm_frame_dump_request_count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DumpingCurrentReg() {
|
||||||
|
std::shared_lock lock{frame_dump_list_mutex};
|
||||||
|
return !waiting_reg_dumps.empty();
|
||||||
|
}
|
||||||
|
|
||||||
bool ShouldPauseInSubmit() const {
|
bool ShouldPauseInSubmit() const {
|
||||||
return waiting_submit_pause && gnm_frame_dump_request_count == 0;
|
return waiting_submit_pause && gnm_frame_dump_request_count == 0;
|
||||||
}
|
}
|
||||||
|
@ -109,17 +140,10 @@ public:
|
||||||
return frame_dump_list[frame_dump_list.size() - gnm_frame_dump_request_count];
|
return frame_dump_list[frame_dump_list.size() - gnm_frame_dump_request_count];
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushQueueDump(QueueDump dump) {
|
void PushQueueDump(QueueDump dump);
|
||||||
std::unique_lock lock{frame_dump_list_mutex};
|
|
||||||
GetFrameDump().queues.push_back(std::move(dump));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowDebugMessage(std::string message) {
|
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||||
if (message.empty()) {
|
const AmdGpu::Liverpool::Regs& regs);
|
||||||
return;
|
|
||||||
}
|
|
||||||
debug_message_popup.push(std::move(message));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace DebugStateType
|
} // namespace DebugStateType
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "imgui/imgui_std.h"
|
#include "imgui/imgui_std.h"
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
#include "layer.h"
|
#include "layer.h"
|
||||||
|
#include "options.h"
|
||||||
#include "widget/frame_dump.h"
|
#include "widget/frame_dump.h"
|
||||||
#include "widget/frame_graph.h"
|
#include "widget/frame_graph.h"
|
||||||
|
|
||||||
|
@ -18,10 +19,9 @@ using namespace Core::Devtools;
|
||||||
using L = Core::Devtools::Layer;
|
using L = Core::Devtools::Layer;
|
||||||
|
|
||||||
static bool show_simple_fps = false;
|
static bool show_simple_fps = false;
|
||||||
|
|
||||||
static float fps_scale = 1.0f;
|
static float fps_scale = 1.0f;
|
||||||
|
|
||||||
static bool show_advanced_debug = false;
|
static bool show_advanced_debug = false;
|
||||||
|
|
||||||
static int dump_frame_count = 1;
|
static int dump_frame_count = 1;
|
||||||
|
|
||||||
static Widget::FrameGraph frame_graph;
|
static Widget::FrameGraph frame_graph;
|
||||||
|
@ -29,12 +29,16 @@ static std::vector<Widget::FrameDumpViewer> frame_viewers;
|
||||||
|
|
||||||
static float debug_popup_timing = 3.0f;
|
static float debug_popup_timing = 3.0f;
|
||||||
|
|
||||||
|
static bool just_opened_options = false;
|
||||||
|
|
||||||
void L::DrawMenuBar() {
|
void L::DrawMenuBar() {
|
||||||
const auto& ctx = *GImGui;
|
const auto& ctx = *GImGui;
|
||||||
const auto& io = ctx.IO;
|
const auto& io = ctx.IO;
|
||||||
|
|
||||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||||
|
|
||||||
|
bool open_popup_options = false;
|
||||||
|
|
||||||
if (BeginMainMenuBar()) {
|
if (BeginMainMenuBar()) {
|
||||||
if (BeginMenu("Options")) {
|
if (BeginMenu("Options")) {
|
||||||
if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) {
|
if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) {
|
||||||
|
@ -55,6 +59,7 @@ void L::DrawMenuBar() {
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
open_popup_options = MenuItem("Options");
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
EndMainMenuBar();
|
EndMainMenuBar();
|
||||||
|
@ -74,6 +79,11 @@ void L::DrawMenuBar() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (open_popup_options) {
|
||||||
|
OpenPopup("GPU Tools Options");
|
||||||
|
just_opened_options = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void L::DrawAdvanced() {
|
void L::DrawAdvanced() {
|
||||||
|
@ -91,13 +101,19 @@ void L::DrawAdvanced() {
|
||||||
->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused");
|
->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DebugState.should_show_frame_dump) {
|
if (DebugState.should_show_frame_dump && DebugState.waiting_reg_dumps.empty()) {
|
||||||
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +149,30 @@ void L::DrawAdvanced() {
|
||||||
debug_popup_timing = 3.0f;
|
debug_popup_timing = 3.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool close_popup_options = true;
|
||||||
|
if (BeginPopupModal("GPU Tools Options", &close_popup_options,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) {
|
||||||
|
static char disassembly_cli[512];
|
||||||
|
|
||||||
|
if (just_opened_options) {
|
||||||
|
just_opened_options = false;
|
||||||
|
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1);
|
||||||
|
disassembly_cli[s] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli));
|
||||||
|
if (IsItemHovered()) {
|
||||||
|
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Button("Save")) {
|
||||||
|
Options.disassembly_cli = disassembly_cli;
|
||||||
|
SaveIniSettingsToDisk(io.IniFilename);
|
||||||
|
CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
EndPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void L::DrawSimple() {
|
void L::DrawSimple() {
|
||||||
|
@ -140,26 +180,54 @@ void L::DrawSimple() {
|
||||||
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void L::SetupSettings() {
|
void L::SetupSettings() {
|
||||||
frame_graph.is_open = true;
|
frame_graph.is_open = true;
|
||||||
|
|
||||||
|
using SettingLoader = void (*)(const char*);
|
||||||
|
|
||||||
ImGuiSettingsHandler handler{};
|
ImGuiSettingsHandler handler{};
|
||||||
handler.TypeName = "DevtoolsLayer";
|
handler.TypeName = "DevtoolsLayer";
|
||||||
handler.TypeHash = ImHashStr(handler.TypeName);
|
handler.TypeHash = ImHashStr(handler.TypeName);
|
||||||
handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
|
handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
|
||||||
return std::string_view("Data") == name ? (void*)1 : nullptr;
|
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;
|
||||||
};
|
};
|
||||||
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void*, const char* line) {
|
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* handle, const char* line) {
|
||||||
int v;
|
if (handle != nullptr) {
|
||||||
float f;
|
reinterpret_cast<SettingLoader>(handle)(line);
|
||||||
if (sscanf(line, "fps_scale=%f", &f) == 1) {
|
|
||||||
fps_scale = f;
|
|
||||||
} 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) {
|
handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
|
||||||
|
@ -169,12 +237,19 @@ void L::SetupSettings() {
|
||||||
buf->appendf("show_frame_graph=%d\n", frame_graph.is_open);
|
buf->appendf("show_frame_graph=%d\n", frame_graph.is_open);
|
||||||
buf->appendf("dump_frame_count=%d\n", dump_frame_count);
|
buf->appendf("dump_frame_count=%d\n", dump_frame_count);
|
||||||
buf->append("\n");
|
buf->append("\n");
|
||||||
|
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");
|
||||||
};
|
};
|
||||||
AddSettingsHandler(&handler);
|
AddSettingsHandler(&handler);
|
||||||
|
|
||||||
const ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
const ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||||
DockBuilderAddNode(dock_id, 0);
|
DockBuilderAddNode(dock_id, 0);
|
||||||
DockBuilderSetNodePos(dock_id, ImVec2{50.0, 50.0});
|
DockBuilderSetNodePos(dock_id, ImVec2{450.0, 150.0});
|
||||||
|
DockBuilderSetNodeSize(dock_id, ImVec2{400.0, 500.0});
|
||||||
DockBuilderFinish(dock_id);
|
DockBuilderFinish(dock_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
src/core/devtools/options.cpp
Normal file
24
src/core/devtools/options.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
|
namespace Core::Devtools {
|
||||||
|
|
||||||
|
TOptions Options;
|
||||||
|
|
||||||
|
void LoadOptionsConfig(const char* line) {
|
||||||
|
char str[512];
|
||||||
|
if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) {
|
||||||
|
Options.disassembly_cli = str;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
|
||||||
|
buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core::Devtools
|
21
src/core/devtools/options.h
Normal file
21
src/core/devtools/options.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct ImGuiTextBuffer;
|
||||||
|
|
||||||
|
namespace Core::Devtools {
|
||||||
|
|
||||||
|
struct TOptions {
|
||||||
|
std::string disassembly_cli;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern TOptions Options;
|
||||||
|
|
||||||
|
void LoadOptionsConfig(const char* line);
|
||||||
|
void SerializeOptionsConfig(ImGuiTextBuffer* buf);
|
||||||
|
|
||||||
|
} // namespace Core::Devtools
|
|
@ -32,6 +32,26 @@ const char* GetOpCodeName(u32 op);
|
||||||
|
|
||||||
namespace Core::Devtools::Widget {
|
namespace Core::Devtools::Widget {
|
||||||
|
|
||||||
|
static bool group_batches = true;
|
||||||
|
static bool show_markers = false;
|
||||||
|
|
||||||
|
void CmdListViewer::LoadConfig(const char* line) {
|
||||||
|
int i;
|
||||||
|
if (sscanf(line, "group_batches=%d", &i) == 1) {
|
||||||
|
group_batches = i != 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sscanf(line, "show_markers=%d", &i) == 1) {
|
||||||
|
show_markers = i != 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CmdListViewer::SerializeConfig(ImGuiTextBuffer* buf) {
|
||||||
|
buf->appendf("group_batches=%d\n", group_batches);
|
||||||
|
buf->appendf("show_markers=%d\n", show_markers);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename HdrType>
|
template <typename HdrType>
|
||||||
static HdrType GetNext(HdrType this_pm4, uint32_t n) {
|
static HdrType GetNext(HdrType this_pm4, uint32_t n) {
|
||||||
HdrType curr_pm4 = this_pm4;
|
HdrType curr_pm4 = this_pm4;
|
||||||
|
@ -43,10 +63,11 @@ static HdrType GetNext(HdrType this_pm4, uint32_t n) {
|
||||||
return curr_pm4;
|
return curr_pm4;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ParsePolygonControl(u32 value) {
|
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);
|
||||||
|
|
||||||
if (BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("CULL_FRONT");
|
Text("CULL_FRONT");
|
||||||
|
@ -126,14 +147,17 @@ static void ParsePolygonControl(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.multi_prim_ib_ena.Value());
|
Text("%X", reg.multi_prim_ib_ena.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseAaConfig(u32 value) {
|
void ParseAaConfig(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::AaConfig const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::AaConfig const&>(value);
|
||||||
|
|
||||||
if (BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("MSAA_NUM_SAMPLES");
|
Text("MSAA_NUM_SAMPLES");
|
||||||
|
@ -164,14 +188,17 @@ static void ParseAaConfig(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.detail_to_exposed_mode.Value());
|
Text("%X", reg.detail_to_exposed_mode.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseViewportControl(u32 value) {
|
void ParseViewportControl(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::ViewportControl const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::ViewportControl const&>(value);
|
||||||
|
|
||||||
if (BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("VPORT_X_SCALE_ENA");
|
Text("VPORT_X_SCALE_ENA");
|
||||||
|
@ -232,14 +259,17 @@ static void ParseViewportControl(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.perfcounter_ref.Value());
|
Text("%X", reg.perfcounter_ref.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseColorControl(u32 value) {
|
void ParseColorControl(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::ColorControl const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::ColorControl const&>(value);
|
||||||
|
|
||||||
if (BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("DISABLE_DUAL_QUAD__VI");
|
Text("DISABLE_DUAL_QUAD__VI");
|
||||||
|
@ -264,14 +294,17 @@ static void ParseColorControl(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.rop3.Value());
|
Text("%X", reg.rop3.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseColor0Info(u32 value) {
|
void ParseColor0Info(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Info const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Info const&>(value);
|
||||||
|
|
||||||
if (BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("ENDIAN");
|
Text("ENDIAN");
|
||||||
|
@ -380,14 +413,17 @@ static void ParseColor0Info(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.cmask_addr_type.Value());
|
Text("%X", reg.cmask_addr_type.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseColor0Attrib(u32 value) {
|
void ParseColor0Attrib(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Attrib const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Attrib const&>(value);
|
||||||
|
|
||||||
if (BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("TILE_MODE_INDEX");
|
Text("TILE_MODE_INDEX");
|
||||||
|
@ -424,14 +460,17 @@ static void ParseColor0Attrib(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.force_dst_alpha_1.Value());
|
Text("%X", reg.force_dst_alpha_1.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseBlendControl(u32 value) {
|
void ParseBlendControl(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::BlendControl const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::BlendControl const&>(value);
|
||||||
|
|
||||||
if (BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("COLOR_SRCBLEND");
|
Text("COLOR_SRCBLEND");
|
||||||
|
@ -490,14 +529,17 @@ static void ParseBlendControl(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.disable_rop3.Value());
|
Text("%X", reg.disable_rop3.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseDepthRenderControl(u32 value) {
|
void ParseDepthRenderControl(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::DepthRenderControl const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::DepthRenderControl const&>(value);
|
||||||
|
|
||||||
if (BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("DEPTH_CLEAR_ENABLE");
|
Text("DEPTH_CLEAR_ENABLE");
|
||||||
|
@ -558,14 +600,17 @@ static void ParseDepthRenderControl(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.decompress_enable.Value());
|
Text("%X", reg.decompress_enable.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseDepthControl(u32 value) {
|
void ParseDepthControl(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::DepthControl const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::DepthControl const&>(value);
|
||||||
|
|
||||||
if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("STENCIL_ENABLE");
|
Text("STENCIL_ENABLE");
|
||||||
|
@ -628,14 +673,17 @@ static void ParseDepthControl(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.disable_color_writes_on_depth_pass.Value());
|
Text("%X", reg.disable_color_writes_on_depth_pass.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseEqaa(u32 value) {
|
void ParseEqaa(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::Eqaa const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::Eqaa const&>(value);
|
||||||
|
|
||||||
if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("MAX_ANCHOR_SAMPLES");
|
Text("MAX_ANCHOR_SAMPLES");
|
||||||
|
@ -708,14 +756,17 @@ static void ParseEqaa(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.enable_postz_overrasterization.Value());
|
Text("%X", reg.enable_postz_overrasterization.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ParseZInfo(u32 value) {
|
void ParseZInfo(u32 value, bool begin_table) {
|
||||||
auto const reg = reinterpret_cast<Liverpool::DepthBuffer::ZInfo const&>(value);
|
auto const reg = reinterpret_cast<Liverpool::DepthBuffer::ZInfo const&>(value);
|
||||||
|
|
||||||
if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
if (!begin_table ||
|
||||||
|
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
TableNextRow();
|
TableNextRow();
|
||||||
TableSetColumnIndex(0);
|
TableSetColumnIndex(0);
|
||||||
Text("FORMAT");
|
Text("FORMAT");
|
||||||
|
@ -776,40 +827,41 @@ static void ParseZInfo(u32 value) {
|
||||||
TableSetColumnIndex(1);
|
TableSetColumnIndex(1);
|
||||||
Text("%X", reg.zrange_precision.Value());
|
Text("%X", reg.zrange_precision.Value());
|
||||||
|
|
||||||
|
if (begin_table) {
|
||||||
EndTable();
|
EndTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body) {
|
void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body) {
|
||||||
using namespace std::string_view_literals;
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
enum class NOP_PAYLOAD : u32 {
|
#define NOP_PAYLOAD \
|
||||||
ACB_SUBMIT_MRK = 0x68750013,
|
P(PUSH_MARKER, 0x68750001) \
|
||||||
ALLOC_ALIGN8 = 0x68753000,
|
P(POP_MARKER, 0x68750002) \
|
||||||
PUSH_MARKER = 0x68750001,
|
P(SET_MARKER, 0x68750003) \
|
||||||
SET_VSHARP = 0x68750004,
|
P(SET_VSHARP, 0x68750004) \
|
||||||
SET_TSHARP = 0x68750005,
|
P(SET_TSHARP, 0x68750005) \
|
||||||
SET_SSHARP = 0x68750006,
|
P(SET_SSHARP, 0x68750006) \
|
||||||
SET_USER_DATA = 0x6875000d,
|
P(ACB_SUBMIT_MRK, 0x68750013) \
|
||||||
};
|
P(SET_USER_DATA, 0x6875000D) \
|
||||||
auto get_noppayload_text = [](NOP_PAYLOAD const nop_payload) {
|
P(PATCHED_FLIP, 0x68750776) \
|
||||||
|
P(PREPARE_FLIP, 0x68750777) \
|
||||||
|
P(PREPARE_FLIP_LABEL, 0x68750778) \
|
||||||
|
P(PREPARE_FLIP_INTERRUPT, 0x68750780) \
|
||||||
|
P(PREPARE_FLIP_INTERRUPT_LABEL, 0x68750781) \
|
||||||
|
P(ALLOC_ALIGN8, 0x68753000)
|
||||||
|
|
||||||
|
auto get_nop_payload_text = [](u32 const nop_payload) {
|
||||||
switch (nop_payload) {
|
switch (nop_payload) {
|
||||||
case NOP_PAYLOAD::ACB_SUBMIT_MRK:
|
#define P(name, value) \
|
||||||
return "ACB_SUBMIT_MRK"sv;
|
case value: \
|
||||||
case NOP_PAYLOAD::ALLOC_ALIGN8:
|
return #name##sv;
|
||||||
return "ALLOC_ALIGN8"sv;
|
NOP_PAYLOAD
|
||||||
case NOP_PAYLOAD::PUSH_MARKER:
|
#undef P
|
||||||
return "PUSH_MARKER"sv;
|
default:
|
||||||
case NOP_PAYLOAD::SET_VSHARP:
|
|
||||||
return "SET_VSHARP"sv;
|
|
||||||
case NOP_PAYLOAD::SET_TSHARP:
|
|
||||||
return "SET_TSHARP"sv;
|
|
||||||
case NOP_PAYLOAD::SET_SSHARP:
|
|
||||||
return "SET_SSHARP"sv;
|
|
||||||
case NOP_PAYLOAD::SET_USER_DATA:
|
|
||||||
return "SET_USER_DATA"sv;
|
|
||||||
}
|
|
||||||
return ""sv;
|
return ""sv;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Separator();
|
Separator();
|
||||||
|
@ -822,7 +874,7 @@ void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body)
|
||||||
for (unsigned i = 0; i < pkt->header.count + 1; ++i) {
|
for (unsigned i = 0; i < pkt->header.count + 1; ++i) {
|
||||||
Text("%02X: %08X", i, payload[i]);
|
Text("%02X: %08X", i, payload[i]);
|
||||||
if ((payload[i] & 0xffff0000) == 0x68750000) {
|
if ((payload[i] & 0xffff0000) == 0x68750000) {
|
||||||
const auto& e = get_noppayload_text((NOP_PAYLOAD)payload[i]);
|
const auto& e = get_nop_payload_text(payload[i]);
|
||||||
if (!e.empty()) {
|
if (!e.empty()) {
|
||||||
SameLine();
|
SameLine();
|
||||||
Text("(%s)", e.data());
|
Text("(%s)", e.data());
|
||||||
|
@ -836,7 +888,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]);
|
||||||
|
@ -1025,20 +1077,31 @@ void CmdListViewer::OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const*
|
||||||
EndGroup();
|
EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cmd_list)
|
CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump,
|
||||||
: parent(parent) {
|
const std::vector<u32>& cmd_list, uintptr_t _base_addr,
|
||||||
|
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();
|
||||||
cmdb_size = cmd_list.size() * sizeof(u32);
|
cmdb_size = cmd_list.size() * sizeof(u32);
|
||||||
|
|
||||||
|
cmdb_view_name = fmt::format("[GFX] Command buffer {}###cmdview_hex_{}", this->name, cmdb_addr);
|
||||||
|
cmdb_view.Open = false;
|
||||||
|
cmdb_view.ReadOnly = true;
|
||||||
|
|
||||||
auto const* pm4_hdr = reinterpret_cast<PM4Header const*>(cmdb_addr);
|
auto const* pm4_hdr = reinterpret_cast<PM4Header const*>(cmdb_addr);
|
||||||
|
|
||||||
size_t processed_size = 0;
|
size_t processed_size = 0;
|
||||||
size_t prev_offset = 0;
|
size_t prev_offset = 0;
|
||||||
|
u32 batch_id = 0;
|
||||||
|
|
||||||
std::string marker{};
|
std::string marker{};
|
||||||
|
|
||||||
|
if (cmdb_size > 0) {
|
||||||
|
events.emplace_back(BatchBegin{.id = 0});
|
||||||
|
}
|
||||||
|
|
||||||
while (processed_size < cmdb_size) {
|
while (processed_size < cmdb_size) {
|
||||||
auto* next_pm4_hdr = GetNext(pm4_hdr, 1);
|
auto* next_pm4_hdr = GetNext(pm4_hdr, 1);
|
||||||
auto processed_len =
|
auto processed_len =
|
||||||
|
@ -1048,22 +1111,28 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cm
|
||||||
if (pm4_hdr->type == PM4Type3Header::TYPE) {
|
if (pm4_hdr->type == PM4Type3Header::TYPE) {
|
||||||
|
|
||||||
auto const* pm4_t3 = reinterpret_cast<PM4Type3Header const*>(pm4_hdr);
|
auto const* pm4_t3 = reinterpret_cast<PM4Type3Header const*>(pm4_hdr);
|
||||||
|
auto opcode = pm4_t3->opcode;
|
||||||
|
|
||||||
if (pm4_t3->opcode == PM4ItOpcode::Nop) {
|
if (opcode == PM4ItOpcode::Nop) {
|
||||||
auto const* it_body = reinterpret_cast<uint32_t const*>(pm4_hdr + 1);
|
auto const* it_body = reinterpret_cast<uint32_t const*>(pm4_hdr + 1);
|
||||||
if (it_body[0] == 0x68750001) {
|
switch (it_body[0]) {
|
||||||
|
case PM4CmdNop::PayloadType::DebugSetMarker:
|
||||||
marker = std::string{(char*)&it_body[1]};
|
marker = std::string{(char*)&it_body[1]};
|
||||||
|
break;
|
||||||
|
case PM4CmdNop::PayloadType::DebugMarkerPush:
|
||||||
|
events.emplace_back(PushMarker{
|
||||||
|
.name = std::string{(char*)&it_body[1]},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case PM4CmdNop::PayloadType::DebugMarkerPop:
|
||||||
|
events.emplace_back(PopMarker{});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pm4_t3->opcode == PM4ItOpcode::DispatchDirect ||
|
if (IsDrawCall(opcode)) {
|
||||||
pm4_t3->opcode == PM4ItOpcode::DispatchIndirect ||
|
|
||||||
pm4_t3->opcode == PM4ItOpcode::DrawIndex2 ||
|
|
||||||
pm4_t3->opcode == PM4ItOpcode::DrawIndexAuto ||
|
|
||||||
pm4_t3->opcode == PM4ItOpcode::DrawIndexOffset2 ||
|
|
||||||
pm4_t3->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);
|
||||||
|
@ -1071,15 +1140,17 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cm
|
||||||
// processed_len += nop_len;
|
// processed_len += nop_len;
|
||||||
// processed_size += nop_len;
|
// processed_size += nop_len;
|
||||||
|
|
||||||
batches.emplace_back(BatchInfo{
|
events.emplace_back(BatchInfo{
|
||||||
marker,
|
.id = batch_id++,
|
||||||
prev_offset,
|
.marker = marker,
|
||||||
processed_size,
|
.start_addr = prev_offset,
|
||||||
processed_size - processed_len,
|
.end_addr = processed_size,
|
||||||
pm4_t3->opcode,
|
.command_addr = processed_size - processed_len,
|
||||||
|
.type = opcode,
|
||||||
});
|
});
|
||||||
prev_offset = processed_size;
|
prev_offset = processed_size;
|
||||||
marker.clear();
|
marker.clear();
|
||||||
|
events.emplace_back(BatchBegin{.id = batch_id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1088,18 +1159,58 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cm
|
||||||
|
|
||||||
// state batch (last)
|
// state batch (last)
|
||||||
if (processed_size - prev_offset > 0) {
|
if (processed_size - prev_offset > 0) {
|
||||||
batches.emplace_back(BatchInfo{
|
events.emplace_back(BatchInfo{
|
||||||
marker,
|
.id = batch_id++,
|
||||||
prev_offset,
|
.marker = marker,
|
||||||
processed_size,
|
.start_addr = prev_offset,
|
||||||
0,
|
.end_addr = processed_size,
|
||||||
static_cast<PM4ItOpcode>(0xFF),
|
.command_addr = 0,
|
||||||
|
.type = static_cast<PM4ItOpcode>(0xFF),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!events.empty() && std::holds_alternative<BatchBegin>(events.back())) {
|
||||||
|
events.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdListViewer::Draw() {
|
void CmdListViewer::Draw() {
|
||||||
|
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());
|
||||||
if (BeginChild("cmd_queue", {})) {
|
if (BeginChild("cmd_queue", {})) {
|
||||||
|
|
||||||
|
Checkbox("Group batches", &group_batches);
|
||||||
|
SameLine();
|
||||||
|
Checkbox("Show markers", &show_markers);
|
||||||
|
|
||||||
char queue_name[32]{};
|
char queue_name[32]{};
|
||||||
if (vqid < 254) {
|
if (vqid < 254) {
|
||||||
std::snprintf(queue_name, sizeof(queue_name), "%s %d", vqid > 254 ? "GFX" : "ASC",
|
std::snprintf(queue_name, sizeof(queue_name), "%s %d", vqid > 254 ? "GFX" : "ASC",
|
||||||
|
@ -1111,28 +1222,121 @@ void CmdListViewer::Draw() {
|
||||||
Text("queue : %s", queue_name);
|
Text("queue : %s", queue_name);
|
||||||
Text("base addr: %08llX", cmdb_addr);
|
Text("base addr: %08llX", cmdb_addr);
|
||||||
SameLine();
|
SameLine();
|
||||||
if (SmallButton(">")) {
|
if (SmallButton("Memory >")) {
|
||||||
parent->cmdb_view.Open ^= true;
|
cmdb_view.Open ^= true;
|
||||||
}
|
}
|
||||||
Text("size : %04llX", cmdb_size);
|
Text("size : %04llX", cmdb_size);
|
||||||
Separator();
|
Separator();
|
||||||
|
|
||||||
char batch_hdr[128];
|
if (TreeNode("Batches")) {
|
||||||
for (int batch_id = 0; batch_id < batches.size(); ++batch_id) {
|
int tree_depth = 0;
|
||||||
auto processed_size = 0ull;
|
int tree_depth_show = 0;
|
||||||
auto const* pm4_hdr =
|
|
||||||
reinterpret_cast<PM4Header const*>(cmdb_addr + batches[batch_id].start_addr);
|
|
||||||
|
|
||||||
sprintf(batch_hdr, "%08llX: batch-%03d | %s", cmdb_addr + batches[batch_id].start_addr,
|
u32 last_batch_id = ~0u;
|
||||||
batch_id, batches[batch_id].marker.c_str());
|
if (!events.empty() && std::holds_alternative<BatchInfo>(events.back())) {
|
||||||
|
last_batch_id = std::get<BatchInfo>(events.back()).id;
|
||||||
if (batch_id == batch_bp) { // highlight batch at breakpoint
|
|
||||||
PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batches[batch_id].type == static_cast<AmdGpu::PM4ItOpcode>(0xFF) ||
|
u32 batch_id = ~0u;
|
||||||
CollapsingHeader(batch_hdr)) {
|
u32 current_highlight_batch = ~0u;
|
||||||
auto const batch_sz = batches[batch_id].end_addr - batches[batch_id].start_addr;
|
|
||||||
|
int id = 0;
|
||||||
|
PushID(0);
|
||||||
|
for (const auto& event : events) {
|
||||||
|
PopID();
|
||||||
|
PushID(id++);
|
||||||
|
|
||||||
|
if (std::holds_alternative<BatchBegin>(event)) {
|
||||||
|
batch_id = std::get<BatchBegin>(event).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_markers) {
|
||||||
|
if (std::holds_alternative<PushMarker>(event)) {
|
||||||
|
if (tree_depth_show >= tree_depth) {
|
||||||
|
auto& marker = std::get<PushMarker>(event);
|
||||||
|
bool show = TreeNode(&event, "%s", marker.name.c_str());
|
||||||
|
if (show) {
|
||||||
|
tree_depth_show++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tree_depth++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (std::holds_alternative<PopMarker>(event)) {
|
||||||
|
if (tree_depth_show >= tree_depth) {
|
||||||
|
tree_depth_show--;
|
||||||
|
TreePop();
|
||||||
|
}
|
||||||
|
tree_depth--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tree_depth_show < tree_depth) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::holds_alternative<BatchInfo>(event)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& batch = std::get<BatchInfo>(event);
|
||||||
|
auto const* pm4_hdr =
|
||||||
|
reinterpret_cast<PM4Header const*>(cmdb_addr + batch.start_addr);
|
||||||
|
|
||||||
|
char batch_hdr[128];
|
||||||
|
if (batch.type == static_cast<AmdGpu::PM4ItOpcode>(0xFF)) {
|
||||||
|
snprintf(batch_hdr, sizeof(batch_hdr), "State batch");
|
||||||
|
} else if (!batch.marker.empty()) {
|
||||||
|
snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s",
|
||||||
|
cmdb_addr + batch.start_addr, batch.id,
|
||||||
|
Gcn::GetOpCodeName(static_cast<u32>(batch.type)),
|
||||||
|
batch.marker.c_str());
|
||||||
|
} else {
|
||||||
|
snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s",
|
||||||
|
cmdb_addr + batch.start_addr, batch.id,
|
||||||
|
Gcn::GetOpCodeName(static_cast<u32>(batch.type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (group_batches) {
|
||||||
|
Indent();
|
||||||
|
}
|
||||||
|
auto const batch_sz = batch.end_addr - batch.start_addr;
|
||||||
|
|
||||||
while (processed_size < batch_sz) {
|
while (processed_size < batch_sz) {
|
||||||
AmdGpu::PM4ItOpcode op{0xFFu};
|
AmdGpu::PM4ItOpcode op{0xFFu};
|
||||||
|
|
||||||
|
@ -1141,25 +1345,39 @@ void CmdListViewer::Draw() {
|
||||||
reinterpret_cast<AmdGpu::PM4Type3Header const*>(pm4_hdr);
|
reinterpret_cast<AmdGpu::PM4Type3Header const*>(pm4_hdr);
|
||||||
op = pm4_t3->opcode;
|
op = pm4_t3->opcode;
|
||||||
|
|
||||||
static char header_name[128];
|
char header_name[128];
|
||||||
sprintf(header_name, "%08llX: %s",
|
sprintf(header_name, "%08llX: %s",
|
||||||
cmdb_addr + batches[batch_id].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);
|
||||||
bool just_opened = IsItemToggledOpen();
|
if (!group_batches) {
|
||||||
if (BeginTable("split", 1)) {
|
if (IsDrawCall(op)) {
|
||||||
TableNextColumn();
|
SameLine(GetContentRegionAvail().x - 40.0f);
|
||||||
Text("size: %d", pm4_hdr->count + 1);
|
if (Button("->", {40.0f, 0.0f})) {
|
||||||
|
open_batch_view();
|
||||||
if (just_opened) {
|
}
|
||||||
|
}
|
||||||
|
if (IsItemHovered() && ctx.IO.KeyShift) {
|
||||||
|
if (BeginTooltip()) {
|
||||||
|
Text("Batch %d", batch_id);
|
||||||
|
EndTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (open_pm4) {
|
||||||
|
if (IsItemToggledOpen()) {
|
||||||
// Editor
|
// Editor
|
||||||
parent->cmdb_view.GotoAddrAndHighlight(
|
cmdb_view.GotoAddrAndHighlight(
|
||||||
reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr,
|
reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr,
|
||||||
reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr +
|
reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr +
|
||||||
(pm4_hdr->count + 2) * 4);
|
(pm4_hdr->count + 2) * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (BeginTable("split", 1)) {
|
||||||
|
TableNextColumn();
|
||||||
|
Text("size: %d", pm4_hdr->count + 1);
|
||||||
|
|
||||||
auto const* it_body =
|
auto const* it_body =
|
||||||
reinterpret_cast<uint32_t const*>(pm4_hdr + 1);
|
reinterpret_cast<uint32_t const*>(pm4_hdr + 1);
|
||||||
|
|
||||||
|
@ -1196,6 +1414,7 @@ void CmdListViewer::Draw() {
|
||||||
}
|
}
|
||||||
TreePop();
|
TreePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Text("<UNK PACKET>");
|
Text("<UNK PACKET>");
|
||||||
}
|
}
|
||||||
|
@ -1206,18 +1425,37 @@ void CmdListViewer::Draw() {
|
||||||
pm4_hdr = next_pm4_hdr;
|
pm4_hdr = next_pm4_hdr;
|
||||||
processed_size += processed_len;
|
processed_size += processed_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (group_batches) {
|
||||||
|
Unindent();
|
||||||
|
};
|
||||||
|
bb = {{0.0f, bb.Max.y}, ctx.LastItemData.Rect.Max};
|
||||||
|
if (bb.Contains(ctx.IO.MousePos)) {
|
||||||
|
current_highlight_batch = batch.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batch_id == batch_bp) {
|
if (batch.id == highlight_batch) {
|
||||||
PopStyleColor();
|
PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batch_id == batches.size() - 2) {
|
if (batch.id == batch_bp) {
|
||||||
|
PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.id == last_batch_id) {
|
||||||
Separator();
|
Separator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PopID();
|
||||||
|
|
||||||
|
highlight_batch = current_highlight_batch;
|
||||||
|
|
||||||
|
TreePop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EndChild();
|
EndChild();
|
||||||
|
PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core::Devtools::Widget
|
} // namespace Core::Devtools::Widget
|
|
@ -5,10 +5,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "video_core/buffer_cache/buffer_cache.h"
|
#include "imgui_memory_editor.h"
|
||||||
|
#include "reg_view.h"
|
||||||
|
|
||||||
namespace AmdGpu {
|
namespace AmdGpu {
|
||||||
union PM4Type3Header;
|
union PM4Type3Header;
|
||||||
|
@ -19,43 +23,50 @@ namespace Core::Devtools::Widget {
|
||||||
|
|
||||||
class FrameDumpViewer;
|
class FrameDumpViewer;
|
||||||
|
|
||||||
class CmdListViewer {
|
void ParsePolygonControl(u32 value, bool begin_table = true);
|
||||||
/*
|
void ParseAaConfig(u32 value, bool begin_table = true);
|
||||||
* Generic PM4 header
|
void ParseViewportControl(u32 value, bool begin_table = true);
|
||||||
*/
|
void ParseColorControl(u32 value, bool begin_table = true);
|
||||||
union PM4Header {
|
void ParseColor0Info(u32 value, bool begin_table = true);
|
||||||
struct {
|
void ParseColor0Attrib(u32 value, bool begin_table = true);
|
||||||
u32 reserved : 16;
|
void ParseBlendControl(u32 value, bool begin_table = true);
|
||||||
u32 count : 14;
|
void ParseDepthRenderControl(u32 value, bool begin_table = true);
|
||||||
u32 type : 2; // PM4_TYPE
|
void ParseDepthControl(u32 value, bool begin_table = true);
|
||||||
};
|
void ParseEqaa(u32 value, bool begin_table = true);
|
||||||
u32 u32All;
|
void ParseZInfo(u32 value, bool begin_table = true);
|
||||||
};
|
|
||||||
struct BatchInfo {
|
|
||||||
std::string marker{};
|
|
||||||
size_t start_addr;
|
|
||||||
size_t end_addr;
|
|
||||||
size_t command_addr;
|
|
||||||
AmdGpu::PM4ItOpcode type;
|
|
||||||
bool bypass{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
FrameDumpViewer* parent;
|
class CmdListViewer {
|
||||||
std::vector<BatchInfo> batches{};
|
|
||||||
|
DebugStateType::FrameDump* frame_dump;
|
||||||
|
|
||||||
|
uintptr_t base_addr;
|
||||||
|
std::string name;
|
||||||
|
std::vector<GPUEvent> events{};
|
||||||
uintptr_t cmdb_addr;
|
uintptr_t cmdb_addr;
|
||||||
size_t cmdb_size;
|
size_t cmdb_size;
|
||||||
|
|
||||||
|
std::string cmdb_view_name;
|
||||||
|
MemoryEditor cmdb_view;
|
||||||
|
|
||||||
int batch_bp{-1};
|
int batch_bp{-1};
|
||||||
int vqid{255};
|
int vqid{255};
|
||||||
|
u32 highlight_batch{~0u};
|
||||||
|
|
||||||
void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
RegView batch_view;
|
||||||
void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
std::vector<RegView> extra_batch_view;
|
||||||
void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
|
||||||
void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||||
void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
static void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||||
|
static void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||||
|
static void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||||
|
static void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cmd_list);
|
static void LoadConfig(const char* line);
|
||||||
|
static void SerializeConfig(ImGuiTextBuffer* buf);
|
||||||
|
|
||||||
|
explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list,
|
||||||
|
uintptr_t base_addr = 0, std::string name = "");
|
||||||
|
|
||||||
void Draw();
|
void Draw();
|
||||||
};
|
};
|
||||||
|
|
100
src/core/devtools/widget/common.h
Normal file
100
src/core/devtools/widget/common.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
|
#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 T, typename V = u32>
|
||||||
|
void DrawEnumRow(const char* text, T value) {
|
||||||
|
DrawRow(text, "%X (%s)", V(value), magic_enum::enum_name(value).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
|
||||||
|
using AmdGpu::PM4ItOpcode;
|
||||||
|
switch (opcode) {
|
||||||
|
case PM4ItOpcode::DrawIndex2:
|
||||||
|
case PM4ItOpcode::DrawIndexOffset2:
|
||||||
|
case PM4ItOpcode::DrawIndexAuto:
|
||||||
|
case PM4ItOpcode::DrawIndirect:
|
||||||
|
case PM4ItOpcode::DrawIndexIndirect:
|
||||||
|
case PM4ItOpcode::DispatchDirect:
|
||||||
|
case PM4ItOpcode::DispatchIndirect:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core::Devtools::Widget
|
|
@ -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,20 +45,19 @@ 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) {
|
||||||
cmd_list_viewer.emplace_back(this, cmd.data);
|
const auto fname =
|
||||||
if (cmd.type == QueueType::dcb && cmd.submit_num == selected_submit_num &&
|
fmt::format("{}_{}_{:02}_{:02}", id, magic_enum::enum_name(selected_queue_type),
|
||||||
cmd.num2 == selected_queue_num2) {
|
selected_submit_num, selected_queue_num2);
|
||||||
selected_cmd = cmd_list_viewer.size() - 1;
|
cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname);
|
||||||
|
if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) {
|
||||||
|
selected_cmd = static_cast<s32>(cmd_list_viewer.size() - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdb_view.Open = false;
|
FrameDumpViewer::~FrameDumpViewer() = default;
|
||||||
cmdb_view.ReadOnly = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameDumpViewer::~FrameDumpViewer() {}
|
|
||||||
|
|
||||||
void FrameDumpViewer::Draw() {
|
void FrameDumpViewer::Draw() {
|
||||||
if (!is_open) {
|
if (!is_open) {
|
||||||
|
@ -66,11 +66,11 @@ void FrameDumpViewer::Draw() {
|
||||||
|
|
||||||
char name[32];
|
char name[32];
|
||||||
snprintf(name, sizeof(name), "Frame #%d dump", id);
|
snprintf(name, sizeof(name), "Frame #%d dump", id);
|
||||||
static ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
|
||||||
SetNextWindowDockID(dock_id, ImGuiCond_Appearing);
|
|
||||||
if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) {
|
if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) {
|
||||||
if (IsWindowAppearing()) {
|
if (IsWindowAppearing()) {
|
||||||
auto window = GetCurrentWindow();
|
auto window = GetCurrentWindow();
|
||||||
|
static ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||||
|
SetWindowDock(window, dock_id, ImGuiCond_Once | ImGuiCond_FirstUseEver);
|
||||||
SetWindowSize(window, ImVec2{470.0f, 600.0f});
|
SetWindowSize(window, ImVec2{470.0f, 600.0f});
|
||||||
}
|
}
|
||||||
BeginGroup();
|
BeginGroup();
|
||||||
|
@ -89,12 +89,30 @@ void FrameDumpViewer::Draw() {
|
||||||
EndCombo();
|
EndCombo();
|
||||||
}
|
}
|
||||||
SameLine();
|
SameLine();
|
||||||
|
BeginDisabled(selected_cmd == -1);
|
||||||
|
if (SmallButton("Dump cmd")) {
|
||||||
|
auto now_time = fmt::localtime(std::time(nullptr));
|
||||||
|
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
||||||
|
magic_enum::enum_name(selected_queue_type),
|
||||||
|
selected_submit_num, selected_queue_num2);
|
||||||
|
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
|
||||||
|
const auto& data = frame_dump->queues[selected_cmd].data;
|
||||||
|
if (file.IsOpen()) {
|
||||||
|
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
|
||||||
|
file.Write(data);
|
||||||
|
} else {
|
||||||
|
DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname));
|
||||||
|
LOG_ERROR(Core, "Failed to open file {}", fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EndDisabled();
|
||||||
|
|
||||||
TextEx("Submit num");
|
TextEx("Submit num");
|
||||||
SameLine();
|
SameLine();
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -119,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;
|
||||||
}
|
}
|
||||||
|
@ -134,34 +152,16 @@ 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();
|
||||||
}
|
}
|
||||||
SameLine();
|
|
||||||
BeginDisabled(selected_cmd == -1);
|
|
||||||
if (SmallButton("Dump cmd")) {
|
|
||||||
auto now_time = fmt::localtime(std::time(nullptr));
|
|
||||||
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
|
||||||
magic_enum::enum_name(selected_queue_type),
|
|
||||||
selected_submit_num, selected_queue_num2);
|
|
||||||
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
|
|
||||||
auto& data = frame_dump.queues[selected_cmd].data;
|
|
||||||
if (file.IsOpen()) {
|
|
||||||
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
|
|
||||||
file.Write(data);
|
|
||||||
} else {
|
|
||||||
DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname));
|
|
||||||
LOG_ERROR(Core, "Failed to open file {}", fname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EndDisabled();
|
|
||||||
EndGroup();
|
EndGroup();
|
||||||
|
|
||||||
if (selected_cmd != -1) {
|
if (selected_cmd != -1) {
|
||||||
|
@ -169,21 +169,6 @@ void FrameDumpViewer::Draw() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
End();
|
End();
|
||||||
|
|
||||||
if (cmdb_view.Open && selected_cmd != -1) {
|
|
||||||
auto& cmd = frame_dump.queues[selected_cmd].data;
|
|
||||||
auto cmd_size = cmd.size() * sizeof(u32);
|
|
||||||
MemoryEditor::Sizes s;
|
|
||||||
cmdb_view.CalcSizes(s, cmd_size, (size_t)cmd.data());
|
|
||||||
SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
|
|
||||||
|
|
||||||
char name[64];
|
|
||||||
snprintf(name, sizeof(name), "[GFX] Command buffer %d###cmdbuf_hex_%d", id, id);
|
|
||||||
if (Begin(name, &cmdb_view.Open, ImGuiWindowFlags_NoScrollbar)) {
|
|
||||||
cmdb_view.DrawContents(cmd.data(), cmd_size, (size_t)cmd.data());
|
|
||||||
}
|
|
||||||
End();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core::Devtools::Widget
|
} // namespace Core::Devtools::Widget
|
||||||
|
|
|
@ -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,11 +16,10 @@ 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;
|
||||||
MemoryEditor cmdb_view;
|
|
||||||
|
|
||||||
DebugStateType::QueueType selected_queue_type;
|
DebugStateType::QueueType selected_queue_type;
|
||||||
s32 selected_submit_num;
|
s32 selected_submit_num;
|
||||||
|
@ -31,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();
|
||||||
|
|
||||||
|
|
182
src/core/devtools/widget/reg_popup.cpp
Normal file
182
src/core/devtools/widget/reg_popup.cpp
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
// 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();
|
||||||
|
DrawEnumRow("GetTilingMode()", tiling_mode);
|
||||||
|
DrawRow("IsTiled()", "%X", buffer.IsTiled());
|
||||||
|
DrawEnumRow("NumFormat()", num_format);
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) {
|
||||||
|
const auto& [depth_buffer, depth_control] = depth_data;
|
||||||
|
|
||||||
|
SeparatorText("Depth buffer");
|
||||||
|
|
||||||
|
if (BeginTable("DEPTH_BUFFER", 2, ImGuiTableFlags_Borders)) {
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
DrawEnumRow("Z_INFO.FORMAT", depth_buffer.z_info.format.Value());
|
||||||
|
DrawMultipleRow(
|
||||||
|
"Z_INFO.NUM_SAMPLES", "%X", depth_buffer.z_info.num_samples,
|
||||||
|
"Z_INFO.TILE_SPLIT", "%X", depth_buffer.z_info.tile_split,
|
||||||
|
"Z_INFO.TILE_MODE_INDEX", "%X", depth_buffer.z_info.tile_mode_index,
|
||||||
|
"Z_INFO.DECOMPRESS_ON_N_ZPLANES", "%X", depth_buffer.z_info.decompress_on_n_zplanes,
|
||||||
|
"Z_INFO.ALLOW_EXPCLEAR", "%X", depth_buffer.z_info.allow_expclear,
|
||||||
|
"Z_INFO.READ_SIZE", "%X", depth_buffer.z_info.read_size,
|
||||||
|
"Z_INFO.TILE_SURFACE_EN", "%X", depth_buffer.z_info.tile_surface_en,
|
||||||
|
"Z_INFO.CLEAR_DISALLOWED", "%X", depth_buffer.z_info.clear_disallowed,
|
||||||
|
"Z_INFO.ZRANGE_PRECISION", "%X", depth_buffer.z_info.zrange_precision
|
||||||
|
);
|
||||||
|
|
||||||
|
DrawEnumRow("STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format.Value());
|
||||||
|
|
||||||
|
DrawMultipleRow(
|
||||||
|
"Z_READ_BASE", "%X", depth_buffer.z_read_base,
|
||||||
|
"STENCIL_READ_BASE", "%X", depth_buffer.stencil_read_base,
|
||||||
|
"Z_WRITE_BASE", "%X", depth_buffer.z_write_base,
|
||||||
|
"STENCIL_WRITE_BASE", "%X", depth_buffer.stencil_write_base,
|
||||||
|
"DEPTH_SIZE.PITCH_TILE_MAX", "%X", depth_buffer.depth_size.pitch_tile_max,
|
||||||
|
"DEPTH_SIZE.HEIGHT_TILE_MAX", "%X", depth_buffer.depth_size.height_tile_max,
|
||||||
|
"DEPTH_SLICE.TILE_MAX", "%X", depth_buffer.depth_slice.tile_max,
|
||||||
|
"Pitch()", "%X", depth_buffer.Pitch(),
|
||||||
|
"Height()", "%X", depth_buffer.Height(),
|
||||||
|
"Address()", "%X", depth_buffer.Address(),
|
||||||
|
"NumSamples()", "%X", depth_buffer.NumSamples(),
|
||||||
|
"NumBits()", "%X", depth_buffer.NumBits(),
|
||||||
|
"GetDepthSliceSize()", "%X", depth_buffer.GetDepthSliceSize()
|
||||||
|
);
|
||||||
|
// clang-format on
|
||||||
|
EndTable();
|
||||||
|
}
|
||||||
|
SeparatorText("Depth control");
|
||||||
|
if (BeginTable("DEPTH_CONTROL", 2, ImGuiTableFlags_Borders)) {
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
DrawMultipleRow(
|
||||||
|
"STENCIL_ENABLE", "%X", depth_control.stencil_enable,
|
||||||
|
"DEPTH_ENABLE", "%X", depth_control.depth_enable,
|
||||||
|
"DEPTH_WRITE_ENABLE", "%X", depth_control.depth_write_enable,
|
||||||
|
"DEPTH_BOUNDS_ENABLE", "%X", depth_control.depth_bounds_enable
|
||||||
|
);
|
||||||
|
DrawEnumRow("DEPTH_FUNC", depth_control.depth_func.Value());
|
||||||
|
DrawRow("BACKFACE_ENABLE", "%X", depth_control.backface_enable);
|
||||||
|
DrawEnumRow("STENCIL_FUNC", depth_control.stencil_ref_func.Value());
|
||||||
|
DrawEnumRow("STENCIL_FUNC_BF", depth_control.stencil_bf_func.Value());
|
||||||
|
DrawMultipleRow(
|
||||||
|
"ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", "%X", depth_control.enable_color_writes_on_depth_fail,
|
||||||
|
"DISABLE_COLOR_WRITES_ON_DEPTH_PASS", "%X", depth_control.disable_color_writes_on_depth_pass
|
||||||
|
);
|
||||||
|
// 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::SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer,
|
||||||
|
AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id) {
|
||||||
|
this->data = std::make_tuple(depth_buffer, depth_control);
|
||||||
|
this->title = fmt::format("Batch #{} Depth", batch_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);
|
||||||
|
} else if (const auto* depth_data = std::get_if<DepthBuffer>(&data)) {
|
||||||
|
DrawDepthBuffer(*depth_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
End();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core::Devtools::Widget
|
38
src/core/devtools/widget/reg_popup.h
Normal file
38
src/core/devtools/widget/reg_popup.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
using DepthBuffer = std::tuple<AmdGpu::Liverpool::DepthBuffer, AmdGpu::Liverpool::DepthControl>;
|
||||||
|
|
||||||
|
std::variant<AmdGpu::Liverpool::ColorBuffer, DepthBuffer> data;
|
||||||
|
std::string title{};
|
||||||
|
|
||||||
|
void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer);
|
||||||
|
|
||||||
|
void DrawDepthBuffer(const DepthBuffer& depth_data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool open = false;
|
||||||
|
|
||||||
|
RegPopup();
|
||||||
|
|
||||||
|
void SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id);
|
||||||
|
|
||||||
|
void SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer,
|
||||||
|
AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id);
|
||||||
|
|
||||||
|
void Draw();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Core::Devtools::Widget
|
305
src/core/devtools/widget/reg_view.cpp
Normal file
305
src/core/devtools/widget/reg_view.cpp
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "common/io_file.h"
|
||||||
|
#include "core/devtools/options.h"
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
#include "reg_view.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define popen _popen
|
||||||
|
#define pclose _pclose
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace ImGui;
|
||||||
|
using magic_enum::enum_name;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
auto shader = data.stages[shader_id];
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryEditor hex_view;
|
||||||
|
hex_view.Open = true;
|
||||||
|
hex_view.ReadOnly = true;
|
||||||
|
hex_view.Cols = 16;
|
||||||
|
hex_view.OptShowAscii = false;
|
||||||
|
|
||||||
|
TextEditor dis_view;
|
||||||
|
dis_view.SetPalette(TextEditor::GetDarkPalette());
|
||||||
|
dis_view.SetReadOnly(true);
|
||||||
|
dis_view.SetText(shader_dis);
|
||||||
|
|
||||||
|
ShaderCache cache{
|
||||||
|
.hex_view = hex_view,
|
||||||
|
.dis_view = dis_view,
|
||||||
|
.user_data = shader.user_data.user_data,
|
||||||
|
};
|
||||||
|
shader_decomp.emplace(shader_id, std::move(cache));
|
||||||
|
}
|
||||||
|
void RegView::SelectShader(int id) {
|
||||||
|
selected_shader = id;
|
||||||
|
if (!shader_decomp.contains(id)) {
|
||||||
|
ProcessShader(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegView::DrawRegs() {
|
||||||
|
const auto& regs = data.regs;
|
||||||
|
|
||||||
|
if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) {
|
||||||
|
|
||||||
|
auto& s = regs.screen_scissor;
|
||||||
|
DrawRow("Scissor", "(%d, %d, %d, %d)", s.top_left_x, s.top_left_y, s.bottom_right_x,
|
||||||
|
s.bottom_right_y);
|
||||||
|
|
||||||
|
auto cc_mode = regs.color_control.mode.Value();
|
||||||
|
DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data());
|
||||||
|
|
||||||
|
const auto open_new_popup = [&](int cb, auto... args) {
|
||||||
|
if (GetIO().KeyShift) {
|
||||||
|
auto& pop = extra_reg_popup.emplace_back();
|
||||||
|
pop.SetData(args...);
|
||||||
|
pop.open = true;
|
||||||
|
} else if (last_selected_cb == cb && default_reg_popup.open) {
|
||||||
|
default_reg_popup.open = false;
|
||||||
|
} else {
|
||||||
|
last_selected_cb = cb;
|
||||||
|
default_reg_popup.SetData(args...);
|
||||||
|
if (!default_reg_popup.open) {
|
||||||
|
default_reg_popup.open = true;
|
||||||
|
auto popup_pos =
|
||||||
|
GetCurrentContext()->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f);
|
||||||
|
SetNextWindowPos(popup_pos, ImGuiCond_Always);
|
||||||
|
default_reg_popup.Draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int cb = 0; cb < AmdGpu::Liverpool::NumColorBuffers; ++cb) {
|
||||||
|
PushID(cb);
|
||||||
|
|
||||||
|
TableNextRow();
|
||||||
|
TableNextColumn();
|
||||||
|
|
||||||
|
const auto& buffer = regs.color_buffers[cb];
|
||||||
|
|
||||||
|
Text("Color buffer %d", cb);
|
||||||
|
TableNextColumn();
|
||||||
|
if (!buffer || !regs.color_target_mask.GetMask(cb)) {
|
||||||
|
TextUnformatted("N/A");
|
||||||
|
} else {
|
||||||
|
const char* text = last_selected_cb == cb && default_reg_popup.open ? "x" : "->";
|
||||||
|
if (SmallButton(text)) {
|
||||||
|
open_new_popup(cb, buffer, batch_id, cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
TableNextRow();
|
||||||
|
TableNextColumn();
|
||||||
|
TextUnformatted("Depth buffer");
|
||||||
|
TableNextColumn();
|
||||||
|
if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) {
|
||||||
|
TextUnformatted("N/A");
|
||||||
|
} else {
|
||||||
|
constexpr auto depth_id = 0xF3;
|
||||||
|
const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->";
|
||||||
|
if (SmallButton(text)) {
|
||||||
|
open_new_popup(depth_id, regs.depth_buffer, regs.depth_control, batch_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegView::RegView() {
|
||||||
|
static int unique_id = 0;
|
||||||
|
id = unique_id++;
|
||||||
|
|
||||||
|
char name[128];
|
||||||
|
snprintf(name, sizeof(name), "BatchView###reg_dump_%d", id);
|
||||||
|
SetNextWindowPos({400.0f, 200.0f});
|
||||||
|
SetNextWindowSize({450.0f, 500.0f});
|
||||||
|
ImGuiID root_dock_id;
|
||||||
|
Begin(name);
|
||||||
|
{
|
||||||
|
char dock_name[64];
|
||||||
|
snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id);
|
||||||
|
root_dock_id = ImHashStr(dock_name);
|
||||||
|
DockSpace(root_dock_id);
|
||||||
|
}
|
||||||
|
End();
|
||||||
|
|
||||||
|
ImGuiID up1, down1;
|
||||||
|
|
||||||
|
DockBuilderRemoveNodeChildNodes(root_dock_id);
|
||||||
|
DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.2f, &up1, &down1);
|
||||||
|
|
||||||
|
snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id);
|
||||||
|
DockBuilderDockWindow(name, up1);
|
||||||
|
|
||||||
|
snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id);
|
||||||
|
DockBuilderDockWindow(name, down1);
|
||||||
|
|
||||||
|
snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id);
|
||||||
|
DockBuilderDockWindow(name, down1);
|
||||||
|
|
||||||
|
DockBuilderFinish(root_dock_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegView::SetData(DebugStateType::RegDump data, u32 batch_id) {
|
||||||
|
this->data = std::move(data);
|
||||||
|
this->batch_id = batch_id;
|
||||||
|
// clear cache
|
||||||
|
selected_shader = -1;
|
||||||
|
shader_decomp.clear();
|
||||||
|
default_reg_popup.open = false;
|
||||||
|
extra_reg_popup.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegView::Draw() {
|
||||||
|
|
||||||
|
char name[128];
|
||||||
|
snprintf(name, sizeof(name), "BatchView %u###reg_dump_%d", batch_id, id);
|
||||||
|
if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) {
|
||||||
|
const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"};
|
||||||
|
|
||||||
|
if (BeginMenuBar()) {
|
||||||
|
if (BeginMenu("Stage")) {
|
||||||
|
for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) {
|
||||||
|
if (data.regs.stage_enable.IsStageEnabled(i)) {
|
||||||
|
bool selected = selected_shader == i;
|
||||||
|
if (Selectable(names[i], &selected)) {
|
||||||
|
SelectShader(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
if (BeginMenu("Windows")) {
|
||||||
|
Checkbox("Registers", &show_registers);
|
||||||
|
Checkbox("User data", &show_user_data);
|
||||||
|
Checkbox("Disassembly", &show_disassembly);
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
char dock_name[64];
|
||||||
|
snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id);
|
||||||
|
auto root_dock_id = ImHashStr(dock_name);
|
||||||
|
DockSpace(root_dock_id);
|
||||||
|
}
|
||||||
|
End();
|
||||||
|
|
||||||
|
auto get_shader = [&]() -> ShaderCache* {
|
||||||
|
auto shader_cache = shader_decomp.find(selected_shader);
|
||||||
|
if (shader_cache == shader_decomp.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &shader_cache->second;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (show_user_data) {
|
||||||
|
snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id);
|
||||||
|
if (Begin(name, &show_user_data)) {
|
||||||
|
auto shader = get_shader();
|
||||||
|
if (!shader) {
|
||||||
|
Text("Select a stage");
|
||||||
|
} else {
|
||||||
|
shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
End();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_disassembly) {
|
||||||
|
snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id);
|
||||||
|
if (Begin(name, &show_disassembly)) {
|
||||||
|
auto shader = get_shader();
|
||||||
|
if (!shader) {
|
||||||
|
Text("Select a stage");
|
||||||
|
} else {
|
||||||
|
shader->dis_view.Render("Disassembly", GetContentRegionAvail());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
End();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_registers) {
|
||||||
|
snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id);
|
||||||
|
if (Begin(name, &show_registers)) {
|
||||||
|
DrawRegs();
|
||||||
|
}
|
||||||
|
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
|
50
src/core/devtools/widget/reg_view.h
Normal file
50
src/core/devtools/widget/reg_view.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "core/debug_state.h"
|
||||||
|
#include "imgui_memory_editor.h"
|
||||||
|
#include "reg_popup.h"
|
||||||
|
#include "text_editor.h"
|
||||||
|
|
||||||
|
namespace Core::Devtools::Widget {
|
||||||
|
|
||||||
|
struct ShaderCache {
|
||||||
|
MemoryEditor hex_view;
|
||||||
|
TextEditor dis_view;
|
||||||
|
Vulkan::Liverpool::UserData user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RegView {
|
||||||
|
int id;
|
||||||
|
|
||||||
|
DebugStateType::RegDump data;
|
||||||
|
u32 batch_id{~0u};
|
||||||
|
|
||||||
|
std::unordered_map<int, ShaderCache> shader_decomp;
|
||||||
|
int selected_shader{-1};
|
||||||
|
RegPopup default_reg_popup;
|
||||||
|
int last_selected_cb{-1};
|
||||||
|
std::vector<RegPopup> extra_reg_popup;
|
||||||
|
|
||||||
|
bool show_registers{true};
|
||||||
|
bool show_user_data{true};
|
||||||
|
bool show_disassembly{true};
|
||||||
|
|
||||||
|
void ProcessShader(int shader_id);
|
||||||
|
|
||||||
|
void SelectShader(int shader_id);
|
||||||
|
|
||||||
|
void DrawRegs();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool open = false;
|
||||||
|
|
||||||
|
RegView();
|
||||||
|
|
||||||
|
void SetData(DebugStateType::RegDump data, u32 batch_id);
|
||||||
|
|
||||||
|
void Draw();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Core::Devtools::Widget
|
2334
src/core/devtools/widget/text_editor.cpp
Normal file
2334
src/core/devtools/widget/text_editor.cpp
Normal file
File diff suppressed because it is too large
Load diff
408
src/core/devtools/widget/text_editor.h
Normal file
408
src/core/devtools/widget/text_editor.h
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright (c) 2017 BalazsJako
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// source: https://github.com/BalazsJako/ImGuiColorTextEdit
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
namespace Core::Devtools::Widget {
|
||||||
|
|
||||||
|
class TextEditor {
|
||||||
|
public:
|
||||||
|
enum class PaletteIndex {
|
||||||
|
Default,
|
||||||
|
Keyword,
|
||||||
|
Number,
|
||||||
|
String,
|
||||||
|
CharLiteral,
|
||||||
|
Punctuation,
|
||||||
|
Preprocessor,
|
||||||
|
Identifier,
|
||||||
|
KnownIdentifier,
|
||||||
|
PreprocIdentifier,
|
||||||
|
Comment,
|
||||||
|
MultiLineComment,
|
||||||
|
Background,
|
||||||
|
Cursor,
|
||||||
|
Selection,
|
||||||
|
ErrorMarker,
|
||||||
|
Breakpoint,
|
||||||
|
LineNumber,
|
||||||
|
CurrentLineFill,
|
||||||
|
CurrentLineFillInactive,
|
||||||
|
CurrentLineEdge,
|
||||||
|
Max
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SelectionMode { Normal, Word, Line };
|
||||||
|
|
||||||
|
struct Breakpoint {
|
||||||
|
int mLine;
|
||||||
|
bool mEnabled;
|
||||||
|
std::string mCondition;
|
||||||
|
|
||||||
|
Breakpoint() : mLine(-1), mEnabled(false) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents a character coordinate from the user's point of view,
|
||||||
|
// i. e. consider an uniform grid (assuming fixed-width font) on the
|
||||||
|
// screen as it is rendered, and each cell has its own coordinate, starting from 0.
|
||||||
|
// Tabs are counted as [1..mTabSize] count empty spaces, depending on
|
||||||
|
// how many space is necessary to reach the next tab stop.
|
||||||
|
// For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize
|
||||||
|
// = 4, because it is rendered as " ABC" on the screen.
|
||||||
|
struct Coordinates {
|
||||||
|
int mLine, mColumn;
|
||||||
|
Coordinates() : mLine(0), mColumn(0) {}
|
||||||
|
Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn) {
|
||||||
|
ASSERT(aLine >= 0);
|
||||||
|
ASSERT(aColumn >= 0);
|
||||||
|
}
|
||||||
|
static Coordinates Invalid() {
|
||||||
|
static Coordinates invalid(-1, -1);
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Coordinates& o) const {
|
||||||
|
return mLine == o.mLine && mColumn == o.mColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Coordinates& o) const {
|
||||||
|
return mLine != o.mLine || mColumn != o.mColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Coordinates& o) const {
|
||||||
|
if (mLine != o.mLine)
|
||||||
|
return mLine < o.mLine;
|
||||||
|
return mColumn < o.mColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const Coordinates& o) const {
|
||||||
|
if (mLine != o.mLine)
|
||||||
|
return mLine > o.mLine;
|
||||||
|
return mColumn > o.mColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const Coordinates& o) const {
|
||||||
|
if (mLine != o.mLine)
|
||||||
|
return mLine < o.mLine;
|
||||||
|
return mColumn <= o.mColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const Coordinates& o) const {
|
||||||
|
if (mLine != o.mLine)
|
||||||
|
return mLine > o.mLine;
|
||||||
|
return mColumn >= o.mColumn;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Identifier {
|
||||||
|
Coordinates mLocation;
|
||||||
|
std::string mDeclaration;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::string String;
|
||||||
|
typedef std::unordered_map<std::string, Identifier> Identifiers;
|
||||||
|
typedef std::unordered_set<std::string> Keywords;
|
||||||
|
typedef std::map<int, std::string> ErrorMarkers;
|
||||||
|
typedef std::unordered_set<int> Breakpoints;
|
||||||
|
typedef std::array<ImU32, (unsigned)PaletteIndex::Max> Palette;
|
||||||
|
typedef uint8_t Char;
|
||||||
|
|
||||||
|
struct Glyph {
|
||||||
|
Char mChar;
|
||||||
|
PaletteIndex mColorIndex = PaletteIndex::Default;
|
||||||
|
bool mComment : 1;
|
||||||
|
bool mMultiLineComment : 1;
|
||||||
|
bool mPreprocessor : 1;
|
||||||
|
|
||||||
|
Glyph(Char aChar, PaletteIndex aColorIndex)
|
||||||
|
: mChar(aChar), mColorIndex(aColorIndex), mComment(false), mMultiLineComment(false),
|
||||||
|
mPreprocessor(false) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<Glyph> Line;
|
||||||
|
typedef std::vector<Line> Lines;
|
||||||
|
|
||||||
|
struct LanguageDefinition {
|
||||||
|
typedef std::pair<std::string, PaletteIndex> TokenRegexString;
|
||||||
|
typedef std::vector<TokenRegexString> TokenRegexStrings;
|
||||||
|
typedef bool (*TokenizeCallback)(const char* in_begin, const char* in_end,
|
||||||
|
const char*& out_begin, const char*& out_end,
|
||||||
|
PaletteIndex& paletteIndex);
|
||||||
|
|
||||||
|
std::string mName;
|
||||||
|
Keywords mKeywords;
|
||||||
|
Identifiers mIdentifiers;
|
||||||
|
Identifiers mPreprocIdentifiers;
|
||||||
|
std::string mCommentStart, mCommentEnd, mSingleLineComment;
|
||||||
|
char mPreprocChar;
|
||||||
|
bool mAutoIndentation;
|
||||||
|
|
||||||
|
TokenizeCallback mTokenize;
|
||||||
|
|
||||||
|
TokenRegexStrings mTokenRegexStrings;
|
||||||
|
|
||||||
|
bool mCaseSensitive;
|
||||||
|
|
||||||
|
LanguageDefinition()
|
||||||
|
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {}
|
||||||
|
|
||||||
|
static const LanguageDefinition& GLSL();
|
||||||
|
};
|
||||||
|
|
||||||
|
TextEditor();
|
||||||
|
~TextEditor();
|
||||||
|
|
||||||
|
void SetLanguageDefinition(const LanguageDefinition& aLanguageDef);
|
||||||
|
const LanguageDefinition& GetLanguageDefinition() const {
|
||||||
|
return mLanguageDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Palette& GetPalette() const {
|
||||||
|
return mPaletteBase;
|
||||||
|
}
|
||||||
|
void SetPalette(const Palette& aValue);
|
||||||
|
|
||||||
|
void SetErrorMarkers(const ErrorMarkers& aMarkers) {
|
||||||
|
mErrorMarkers = aMarkers;
|
||||||
|
}
|
||||||
|
void SetBreakpoints(const Breakpoints& aMarkers) {
|
||||||
|
mBreakpoints = aMarkers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false);
|
||||||
|
void SetText(const std::string& aText);
|
||||||
|
std::string GetText() const;
|
||||||
|
|
||||||
|
void SetTextLines(const std::vector<std::string>& aLines);
|
||||||
|
std::vector<std::string> GetTextLines() const;
|
||||||
|
|
||||||
|
std::string GetSelectedText() const;
|
||||||
|
std::string GetCurrentLineText() const;
|
||||||
|
|
||||||
|
int GetTotalLines() const {
|
||||||
|
return (int)mLines.size();
|
||||||
|
}
|
||||||
|
bool IsOverwrite() const {
|
||||||
|
return mOverwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetReadOnly(bool aValue);
|
||||||
|
bool IsReadOnly() const {
|
||||||
|
return mReadOnly;
|
||||||
|
}
|
||||||
|
bool IsTextChanged() const {
|
||||||
|
return mTextChanged;
|
||||||
|
}
|
||||||
|
bool IsCursorPositionChanged() const {
|
||||||
|
return mCursorPositionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsColorizerEnabled() const {
|
||||||
|
return mColorizerEnabled;
|
||||||
|
}
|
||||||
|
void SetColorizerEnable(bool aValue);
|
||||||
|
|
||||||
|
Coordinates GetCursorPosition() const {
|
||||||
|
return GetActualCursorCoordinates();
|
||||||
|
}
|
||||||
|
void SetCursorPosition(const Coordinates& aPosition);
|
||||||
|
|
||||||
|
inline void SetHandleMouseInputs(bool aValue) {
|
||||||
|
mHandleMouseInputs = aValue;
|
||||||
|
}
|
||||||
|
inline bool IsHandleMouseInputsEnabled() const {
|
||||||
|
return mHandleKeyboardInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetHandleKeyboardInputs(bool aValue) {
|
||||||
|
mHandleKeyboardInputs = aValue;
|
||||||
|
}
|
||||||
|
inline bool IsHandleKeyboardInputsEnabled() const {
|
||||||
|
return mHandleKeyboardInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetImGuiChildIgnored(bool aValue) {
|
||||||
|
mIgnoreImGuiChild = aValue;
|
||||||
|
}
|
||||||
|
inline bool IsImGuiChildIgnored() const {
|
||||||
|
return mIgnoreImGuiChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetShowWhitespaces(bool aValue) {
|
||||||
|
mShowWhitespaces = aValue;
|
||||||
|
}
|
||||||
|
inline bool IsShowingWhitespaces() const {
|
||||||
|
return mShowWhitespaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTabSize(int aValue);
|
||||||
|
inline int GetTabSize() const {
|
||||||
|
return mTabSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertText(const std::string& aValue);
|
||||||
|
void InsertText(const char* aValue);
|
||||||
|
|
||||||
|
void MoveUp(int aAmount = 1, bool aSelect = false);
|
||||||
|
void MoveDown(int aAmount = 1, bool aSelect = false);
|
||||||
|
void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||||
|
void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||||
|
void MoveTop(bool aSelect = false);
|
||||||
|
void MoveBottom(bool aSelect = false);
|
||||||
|
void MoveHome(bool aSelect = false);
|
||||||
|
void MoveEnd(bool aSelect = false);
|
||||||
|
|
||||||
|
void SetSelectionStart(const Coordinates& aPosition);
|
||||||
|
void SetSelectionEnd(const Coordinates& aPosition);
|
||||||
|
void SetSelection(const Coordinates& aStart, const Coordinates& aEnd,
|
||||||
|
SelectionMode aMode = SelectionMode::Normal);
|
||||||
|
void SelectWordUnderCursor();
|
||||||
|
void SelectAll();
|
||||||
|
bool HasSelection() const;
|
||||||
|
|
||||||
|
void Copy();
|
||||||
|
void Cut();
|
||||||
|
void Paste();
|
||||||
|
void Delete();
|
||||||
|
|
||||||
|
bool CanUndo() const;
|
||||||
|
bool CanRedo() const;
|
||||||
|
void Undo(int aSteps = 1);
|
||||||
|
void Redo(int aSteps = 1);
|
||||||
|
|
||||||
|
static const Palette& GetDarkPalette();
|
||||||
|
static const Palette& GetLightPalette();
|
||||||
|
static const Palette& GetRetroBluePalette();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::vector<std::pair<std::regex, PaletteIndex>> RegexList;
|
||||||
|
|
||||||
|
struct EditorState {
|
||||||
|
Coordinates mSelectionStart;
|
||||||
|
Coordinates mSelectionEnd;
|
||||||
|
Coordinates mCursorPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UndoRecord {
|
||||||
|
public:
|
||||||
|
UndoRecord() {}
|
||||||
|
~UndoRecord() {}
|
||||||
|
|
||||||
|
UndoRecord(const std::string& aAdded, const TextEditor::Coordinates aAddedStart,
|
||||||
|
const TextEditor::Coordinates aAddedEnd,
|
||||||
|
|
||||||
|
const std::string& aRemoved, const TextEditor::Coordinates aRemovedStart,
|
||||||
|
const TextEditor::Coordinates aRemovedEnd,
|
||||||
|
|
||||||
|
TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter);
|
||||||
|
|
||||||
|
void Undo(TextEditor* aEditor);
|
||||||
|
void Redo(TextEditor* aEditor);
|
||||||
|
|
||||||
|
std::string mAdded;
|
||||||
|
Coordinates mAddedStart;
|
||||||
|
Coordinates mAddedEnd;
|
||||||
|
|
||||||
|
std::string mRemoved;
|
||||||
|
Coordinates mRemovedStart;
|
||||||
|
Coordinates mRemovedEnd;
|
||||||
|
|
||||||
|
EditorState mBefore;
|
||||||
|
EditorState mAfter;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<UndoRecord> UndoBuffer;
|
||||||
|
|
||||||
|
void ProcessInputs();
|
||||||
|
void Colorize(int aFromLine = 0, int aCount = -1);
|
||||||
|
void ColorizeRange(int aFromLine = 0, int aToLine = 0);
|
||||||
|
void ColorizeInternal();
|
||||||
|
float TextDistanceToLineStart(const Coordinates& aFrom) const;
|
||||||
|
void EnsureCursorVisible();
|
||||||
|
int GetPageSize() const;
|
||||||
|
std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const;
|
||||||
|
Coordinates GetActualCursorCoordinates() const;
|
||||||
|
Coordinates SanitizeCoordinates(const Coordinates& aValue) const;
|
||||||
|
void Advance(Coordinates& aCoordinates) const;
|
||||||
|
void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd);
|
||||||
|
int InsertTextAt(Coordinates& aWhere, const char* aValue);
|
||||||
|
void AddUndo(UndoRecord& aValue);
|
||||||
|
Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const;
|
||||||
|
Coordinates FindWordStart(const Coordinates& aFrom) const;
|
||||||
|
Coordinates FindWordEnd(const Coordinates& aFrom) const;
|
||||||
|
Coordinates FindNextWord(const Coordinates& aFrom) const;
|
||||||
|
int GetCharacterIndex(const Coordinates& aCoordinates) const;
|
||||||
|
int GetCharacterColumn(int aLine, int aIndex) const;
|
||||||
|
int GetLineCharacterCount(int aLine) const;
|
||||||
|
int GetLineMaxColumn(int aLine) const;
|
||||||
|
bool IsOnWordBoundary(const Coordinates& aAt) const;
|
||||||
|
void RemoveLine(int aStart, int aEnd);
|
||||||
|
void RemoveLine(int aIndex);
|
||||||
|
Line& InsertLine(int aIndex);
|
||||||
|
void EnterCharacter(ImWchar aChar, bool aShift);
|
||||||
|
void Backspace();
|
||||||
|
void DeleteSelection();
|
||||||
|
std::string GetWordUnderCursor() const;
|
||||||
|
std::string GetWordAt(const Coordinates& aCoords) const;
|
||||||
|
ImU32 GetGlyphColor(const Glyph& aGlyph) const;
|
||||||
|
|
||||||
|
void HandleKeyboardInputs();
|
||||||
|
void HandleMouseInputs();
|
||||||
|
void Render();
|
||||||
|
|
||||||
|
float mLineSpacing;
|
||||||
|
Lines mLines;
|
||||||
|
EditorState mState;
|
||||||
|
UndoBuffer mUndoBuffer;
|
||||||
|
int mUndoIndex;
|
||||||
|
|
||||||
|
int mTabSize;
|
||||||
|
bool mOverwrite;
|
||||||
|
bool mReadOnly;
|
||||||
|
bool mWithinRender;
|
||||||
|
bool mScrollToCursor;
|
||||||
|
bool mScrollToTop;
|
||||||
|
bool mTextChanged;
|
||||||
|
bool mColorizerEnabled;
|
||||||
|
float mTextStart; // position (in pixels) where a code line starts relative to the left of the
|
||||||
|
// TextEditor.
|
||||||
|
int mLeftMargin;
|
||||||
|
bool mCursorPositionChanged;
|
||||||
|
int mColorRangeMin, mColorRangeMax;
|
||||||
|
SelectionMode mSelectionMode;
|
||||||
|
bool mHandleKeyboardInputs;
|
||||||
|
bool mHandleMouseInputs;
|
||||||
|
bool mIgnoreImGuiChild;
|
||||||
|
bool mShowWhitespaces;
|
||||||
|
|
||||||
|
Palette mPaletteBase;
|
||||||
|
Palette mPalette;
|
||||||
|
LanguageDefinition mLanguageDefinition;
|
||||||
|
RegexList mRegexList;
|
||||||
|
|
||||||
|
bool mCheckComments;
|
||||||
|
Breakpoints mBreakpoints;
|
||||||
|
ErrorMarkers mErrorMarkers;
|
||||||
|
ImVec2 mCharAdvance;
|
||||||
|
Coordinates mInteractiveStart, mInteractiveEnd;
|
||||||
|
std::string mLineBuffer;
|
||||||
|
uint64_t mStartTime;
|
||||||
|
|
||||||
|
float mLastClick;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Core::Devtools::Widget
|
|
@ -75,7 +75,7 @@ public:
|
||||||
std::min(io.DisplaySize.y, 300.0f),
|
std::min(io.DisplaySize.y, 300.0f),
|
||||||
};
|
};
|
||||||
|
|
||||||
CentralizeWindow();
|
CentralizeNextWindow();
|
||||||
SetNextWindowSize(window_size);
|
SetNextWindowSize(window_size);
|
||||||
SetNextWindowCollapsed(false);
|
SetNextWindowCollapsed(false);
|
||||||
if (first_render || !io.NavActive) {
|
if (first_render || !io.NavActive) {
|
||||||
|
|
|
@ -519,10 +519,12 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
|
||||||
// Dumping them using the current ring pointer would result in files containing only the
|
// Dumping them using the current ring pointer would result in files containing only the
|
||||||
// `IndirectBuffer` command. To access the actual command stream, we need to unwrap the IB.
|
// `IndirectBuffer` command. To access the actual command stream, we need to unwrap the IB.
|
||||||
auto acb = acb_span;
|
auto acb = acb_span;
|
||||||
|
auto base_addr = reinterpret_cast<uintptr_t>(acb_ptr);
|
||||||
const auto* indirect_buffer =
|
const auto* indirect_buffer =
|
||||||
reinterpret_cast<const PM4CmdIndirectBuffer*>(acb_span.data());
|
reinterpret_cast<const PM4CmdIndirectBuffer*>(acb_span.data());
|
||||||
if (indirect_buffer->header.opcode == PM4ItOpcode::IndirectBuffer) {
|
if (indirect_buffer->header.opcode == PM4ItOpcode::IndirectBuffer) {
|
||||||
acb = {indirect_buffer->Address<const u32>(), indirect_buffer->ib_size};
|
base_addr = reinterpret_cast<uintptr_t>(indirect_buffer->Address<const u32>());
|
||||||
|
acb = {reinterpret_cast<const u32*>(base_addr), indirect_buffer->ib_size};
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace DebugStateType;
|
using namespace DebugStateType;
|
||||||
|
@ -532,9 +534,9 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
|
||||||
.submit_num = seq_num,
|
.submit_num = seq_num,
|
||||||
.num2 = gnm_vqid,
|
.num2 = gnm_vqid,
|
||||||
.data = {acb.begin(), acb.end()},
|
.data = {acb.begin(), acb.end()},
|
||||||
|
.base_addr = base_addr,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
liverpool->SubmitAsc(vqid, acb_span);
|
liverpool->SubmitAsc(vqid, acb_span);
|
||||||
|
|
||||||
*asc_queue.read_addr += acb_size;
|
*asc_queue.read_addr += acb_size;
|
||||||
|
@ -1125,10 +1127,26 @@ int PS4_SYSV_ABI sceGnmInsertSetColorMarker() {
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceGnmInsertSetMarker() {
|
s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker) {
|
||||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
LOG_TRACE(Lib_GnmDriver, "called");
|
||||||
|
|
||||||
|
if (cmdbuf && marker) {
|
||||||
|
const auto len = std::strlen(marker);
|
||||||
|
const u32 packet_size = ((len + 8) >> 2) + ((len + 0xc) >> 3) * 2;
|
||||||
|
if (packet_size + 2 == size) {
|
||||||
|
auto* nop = reinterpret_cast<PM4CmdNop*>(cmdbuf);
|
||||||
|
nop->header =
|
||||||
|
PM4Type3Header{PM4ItOpcode::Nop, packet_size, PM4ShaderType::ShaderGraphics};
|
||||||
|
nop->data_block[0] = PM4CmdNop::PayloadType::DebugSetMarker;
|
||||||
|
const auto marker_len = len + 1;
|
||||||
|
std::memcpy(&nop->data_block[1], marker, marker_len);
|
||||||
|
std::memset(reinterpret_cast<u8*>(&nop->data_block[1]) + marker_len, 0,
|
||||||
|
packet_size * 4 - marker_len);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker() {
|
int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker() {
|
||||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
||||||
|
@ -2163,15 +2181,16 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[
|
||||||
.submit_num = seq_num,
|
.submit_num = seq_num,
|
||||||
.num2 = cbpair,
|
.num2 = cbpair,
|
||||||
.data = {dcb_span.begin(), dcb_span.end()},
|
.data = {dcb_span.begin(), dcb_span.end()},
|
||||||
|
.base_addr = reinterpret_cast<uintptr_t>(dcb_gpu_addrs[cbpair]),
|
||||||
});
|
});
|
||||||
DebugState.PushQueueDump({
|
DebugState.PushQueueDump({
|
||||||
.type = QueueType::ccb,
|
.type = QueueType::ccb,
|
||||||
.submit_num = seq_num,
|
.submit_num = seq_num,
|
||||||
.num2 = cbpair,
|
.num2 = cbpair,
|
||||||
.data = {ccb_span.begin(), ccb_span.end()},
|
.data = {ccb_span.begin(), ccb_span.end()},
|
||||||
|
.base_addr = reinterpret_cast<uintptr_t>(ccb),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
liverpool->SubmitGfx(dcb_span, ccb_span);
|
liverpool->SubmitGfx(dcb_span, ccb_span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size);
|
||||||
s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color);
|
s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color);
|
||||||
s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker);
|
s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker);
|
||||||
int PS4_SYSV_ABI sceGnmInsertSetColorMarker();
|
int PS4_SYSV_ABI sceGnmInsertSetColorMarker();
|
||||||
int PS4_SYSV_ABI sceGnmInsertSetMarker();
|
s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker);
|
||||||
int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker();
|
int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker();
|
||||||
s32 PS4_SYSV_ABI sceGnmInsertWaitFlipDone(u32* cmdbuf, u32 size, s32 vo_handle, u32 buf_idx);
|
s32 PS4_SYSV_ABI sceGnmInsertWaitFlipDone(u32* cmdbuf, u32 size, s32 vo_handle, u32 buf_idx);
|
||||||
int PS4_SYSV_ABI sceGnmIsCoredumpValid();
|
int PS4_SYSV_ABI sceGnmIsCoredumpValid();
|
||||||
|
|
|
@ -98,7 +98,7 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) {
|
||||||
param_sfo.Open(param_sfo_path);
|
param_sfo.Open(param_sfo_path);
|
||||||
|
|
||||||
auto last_write = param_sfo.GetLastWrite();
|
auto last_write = param_sfo.GetLastWrite();
|
||||||
#ifdef _WIN32
|
#if defined(_WIN32) && !defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__)
|
||||||
auto utc_time = std::chrono::file_clock::to_utc(last_write);
|
auto utc_time = std::chrono::file_clock::to_utc(last_write);
|
||||||
#else
|
#else
|
||||||
auto utc_time = std::chrono::file_clock::to_sys(last_write);
|
auto utc_time = std::chrono::file_clock::to_sys(last_write);
|
||||||
|
@ -402,7 +402,7 @@ void SaveDialogUi::Draw() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
CentralizeWindow();
|
CentralizeNextWindow();
|
||||||
SetNextWindowSize(window_size);
|
SetNextWindowSize(window_size);
|
||||||
SetNextWindowCollapsed(false);
|
SetNextWindowCollapsed(false);
|
||||||
if (first_render || !io.NavActive) {
|
if (first_render || !io.NavActive) {
|
||||||
|
|
|
@ -256,7 +256,7 @@ void MsgDialogUi::Draw() {
|
||||||
std::min(io.DisplaySize.y, 300.0f),
|
std::min(io.DisplaySize.y, 300.0f),
|
||||||
};
|
};
|
||||||
|
|
||||||
CentralizeWindow();
|
CentralizeNextWindow();
|
||||||
SetNextWindowSize(window_size);
|
SetNextWindowSize(window_size);
|
||||||
SetNextWindowCollapsed(false);
|
SetNextWindowCollapsed(false);
|
||||||
if (first_render || !io.NavActive) {
|
if (first_render || !io.NavActive) {
|
||||||
|
|
|
@ -25,11 +25,16 @@ inline float FastInFastOutCubic(float x) {
|
||||||
|
|
||||||
} // namespace Easing
|
} // namespace Easing
|
||||||
|
|
||||||
inline void CentralizeWindow() {
|
inline void CentralizeNextWindow() {
|
||||||
const auto display_size = GetIO().DisplaySize;
|
const auto display_size = GetIO().DisplaySize;
|
||||||
SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f});
|
SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void CentralizeWindow() {
|
||||||
|
const auto display_size = GetIO().DisplaySize;
|
||||||
|
SetWindowPos(display_size / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) {
|
inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) {
|
||||||
const auto cur_pos = GetWindowPos();
|
const auto cur_pos = GetWindowPos();
|
||||||
if (cur_pos.x < 0.0f || cur_pos.y < 0.0f) {
|
if (cur_pos.x < 0.0f || cur_pos.y < 0.0f) {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <externals/stb_image.h>
|
#include <externals/stb_image.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/config.h"
|
||||||
#include "common/io_file.h"
|
#include "common/io_file.h"
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "imgui_impl_vulkan.h"
|
#include "imgui_impl_vulkan.h"
|
||||||
|
@ -147,6 +148,11 @@ void WorkerLoop() {
|
||||||
g_job_list.pop_front();
|
g_job_list.pop_front();
|
||||||
g_job_list_mtx.unlock();
|
g_job_list_mtx.unlock();
|
||||||
|
|
||||||
|
if (Config::vkCrashDiagnosticEnabled()) {
|
||||||
|
// FIXME: Crash diagnostic hangs when building the command buffer here
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!path.empty()) { // Decode PNG from file
|
if (!path.empty()) { // Decode PNG from file
|
||||||
Common::FS::IOFile file(path, Common::FS::FileAccessMode::Read);
|
Common::FS::IOFile file(path, Common::FS::FileAccessMode::Read);
|
||||||
if (!file.IsOpen()) {
|
if (!file.IsOpen()) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
#include "core/debug_state.h"
|
||||||
#include "core/libraries/videoout/driver.h"
|
#include "core/libraries/videoout/driver.h"
|
||||||
#include "video_core/amdgpu/liverpool.h"
|
#include "video_core/amdgpu/liverpool.h"
|
||||||
#include "video_core/amdgpu/pm4_cmds.h"
|
#include "video_core/amdgpu/pm4_cmds.h"
|
||||||
|
@ -187,6 +188,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
TracyFiberEnter(dcb_task_name);
|
TracyFiberEnter(dcb_task_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto base_addr = reinterpret_cast<uintptr_t>(dcb.data());
|
||||||
while (!dcb.empty()) {
|
while (!dcb.empty()) {
|
||||||
const auto* header = reinterpret_cast<const PM4Header*>(dcb.data());
|
const auto* header = reinterpret_cast<const PM4Header*>(dcb.data());
|
||||||
const u32 type = header->type;
|
const u32 type = header->type;
|
||||||
|
@ -359,6 +361,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
regs.index_base_address.base_addr_hi.Assign(draw_index->index_base_hi);
|
regs.index_base_address.base_addr_hi.Assign(draw_index->index_base_hi);
|
||||||
regs.num_indices = draw_index->index_count;
|
regs.num_indices = draw_index->index_count;
|
||||||
regs.draw_initiator = draw_index->draw_initiator;
|
regs.draw_initiator = draw_index->draw_initiator;
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address));
|
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address));
|
||||||
|
@ -373,6 +378,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
regs.max_index_size = draw_index_off->max_size;
|
regs.max_index_size = draw_index_off->max_size;
|
||||||
regs.num_indices = draw_index_off->index_count;
|
regs.num_indices = draw_index_off->index_count;
|
||||||
regs.draw_initiator = draw_index_off->draw_initiator;
|
regs.draw_initiator = draw_index_off->draw_initiator;
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(
|
rasterizer->ScopeMarkerBegin(
|
||||||
|
@ -386,6 +394,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
const auto* draw_index = reinterpret_cast<const PM4CmdDrawIndexAuto*>(header);
|
const auto* draw_index = reinterpret_cast<const PM4CmdDrawIndexAuto*>(header);
|
||||||
regs.num_indices = draw_index->index_count;
|
regs.num_indices = draw_index->index_count;
|
||||||
regs.draw_initiator = draw_index->draw_initiator;
|
regs.draw_initiator = draw_index->draw_initiator;
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address));
|
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address));
|
||||||
|
@ -399,6 +410,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
const auto offset = draw_indirect->data_offset;
|
const auto offset = draw_indirect->data_offset;
|
||||||
const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr;
|
const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr;
|
||||||
const auto size = sizeof(PM4CmdDrawIndirect::DrawInstancedArgs);
|
const auto size = sizeof(PM4CmdDrawIndirect::DrawInstancedArgs);
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address));
|
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address));
|
||||||
|
@ -413,6 +427,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
const auto offset = draw_index_indirect->data_offset;
|
const auto offset = draw_index_indirect->data_offset;
|
||||||
const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr;
|
const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr;
|
||||||
const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs);
|
const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs);
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(
|
rasterizer->ScopeMarkerBegin(
|
||||||
|
@ -428,6 +445,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
regs.cs_program.dim_y = dispatch_direct->dim_y;
|
regs.cs_program.dim_y = dispatch_direct->dim_y;
|
||||||
regs.cs_program.dim_z = dispatch_direct->dim_z;
|
regs.cs_program.dim_z = dispatch_direct->dim_z;
|
||||||
regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator;
|
regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator;
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
|
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address));
|
rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address));
|
||||||
|
@ -442,6 +462,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
const auto offset = dispatch_indirect->data_offset;
|
const auto offset = dispatch_indirect->data_offset;
|
||||||
const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr;
|
const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr;
|
||||||
const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions);
|
const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions);
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
|
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(
|
rasterizer->ScopeMarkerBegin(
|
||||||
|
@ -576,6 +599,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||||
Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, int vqid) {
|
Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, int vqid) {
|
||||||
TracyFiberEnter(acb_task_name);
|
TracyFiberEnter(acb_task_name);
|
||||||
|
|
||||||
|
auto base_addr = reinterpret_cast<uintptr_t>(acb.data());
|
||||||
while (!acb.empty()) {
|
while (!acb.empty()) {
|
||||||
const auto* header = reinterpret_cast<const PM4Header*>(acb.data());
|
const auto* header = reinterpret_cast<const PM4Header*>(acb.data());
|
||||||
const u32 type = header->type;
|
const u32 type = header->type;
|
||||||
|
@ -620,6 +644,9 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, int vqid) {
|
||||||
regs.cs_program.dim_y = dispatch_direct->dim_y;
|
regs.cs_program.dim_y = dispatch_direct->dim_y;
|
||||||
regs.cs_program.dim_z = dispatch_direct->dim_z;
|
regs.cs_program.dim_z = dispatch_direct->dim_z;
|
||||||
regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator;
|
regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator;
|
||||||
|
if (DebugState.DumpingCurrentReg()) {
|
||||||
|
DebugState.PushRegsDump(base_addr, reinterpret_cast<uintptr_t>(header), regs);
|
||||||
|
}
|
||||||
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
|
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
|
||||||
const auto cmd_address = reinterpret_cast<const void*>(header);
|
const auto cmd_address = reinterpret_cast<const void*>(header);
|
||||||
rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address));
|
rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address));
|
||||||
|
|
|
@ -772,6 +772,8 @@ struct Liverpool {
|
||||||
BitField<27, 1, u32> fmask_compress_1frag_only;
|
BitField<27, 1, u32> fmask_compress_1frag_only;
|
||||||
BitField<28, 1, u32> dcc_enable;
|
BitField<28, 1, u32> dcc_enable;
|
||||||
BitField<29, 1, u32> cmask_addr_type;
|
BitField<29, 1, u32> cmask_addr_type;
|
||||||
|
|
||||||
|
u32 u32all;
|
||||||
} info;
|
} info;
|
||||||
union Color0Attrib {
|
union Color0Attrib {
|
||||||
BitField<0, 5, TilingMode> tile_mode_index;
|
BitField<0, 5, TilingMode> tile_mode_index;
|
||||||
|
@ -780,6 +782,8 @@ struct Liverpool {
|
||||||
BitField<12, 3, u32> num_samples_log2;
|
BitField<12, 3, u32> num_samples_log2;
|
||||||
BitField<15, 2, u32> num_fragments_log2;
|
BitField<15, 2, u32> num_fragments_log2;
|
||||||
BitField<17, 1, u32> force_dst_alpha_1;
|
BitField<17, 1, u32> force_dst_alpha_1;
|
||||||
|
|
||||||
|
u32 u32all;
|
||||||
} attrib;
|
} attrib;
|
||||||
INSERT_PADDING_WORDS(1);
|
INSERT_PADDING_WORDS(1);
|
||||||
u32 cmask_base_address;
|
u32 cmask_base_address;
|
||||||
|
@ -935,7 +939,7 @@ struct Liverpool {
|
||||||
BitField<5, 1, u32> gs_en;
|
BitField<5, 1, u32> gs_en;
|
||||||
BitField<6, 1, u32> vs_en;
|
BitField<6, 1, u32> vs_en;
|
||||||
|
|
||||||
bool IsStageEnabled(u32 stage) {
|
bool IsStageEnabled(u32 stage) const {
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
@ -213,6 +213,7 @@ struct PM4CmdNop {
|
||||||
enum PayloadType : u32 {
|
enum PayloadType : u32 {
|
||||||
DebugMarkerPush = 0x68750001u, ///< Begin of GPU event scope
|
DebugMarkerPush = 0x68750001u, ///< Begin of GPU event scope
|
||||||
DebugMarkerPop = 0x68750002u, ///< End of GPU event scope
|
DebugMarkerPop = 0x68750002u, ///< End of GPU event scope
|
||||||
|
DebugSetMarker = 0x68750003u, ///< Set GPU event marker
|
||||||
SetVsharpInUdata = 0x68750004u, ///< Indicates that V# will be set in the next packet
|
SetVsharpInUdata = 0x68750004u, ///< Indicates that V# will be set in the next packet
|
||||||
SetTsharpInUdata = 0x68750005u, ///< Indicates that T# will be set in the next packet
|
SetTsharpInUdata = 0x68750005u, ///< Indicates that T# will be set in the next packet
|
||||||
SetSsharpInUdata = 0x68750006u, ///< Indicates that S# will be set in the next packet
|
SetSsharpInUdata = 0x68750006u, ///< Indicates that S# will be set in the next packet
|
||||||
|
|
Loading…
Reference in a new issue