// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include "common/bit_field.h" #include "common/io_file.h" #include "common/types.h" #include "core/debug_state.h" #include "video_core/amdgpu/pm4_opcodes.h" #if defined(_WIN32) #define popen _popen #define pclose _pclose #endif namespace Core::Devtools::Widget { /* * Generic PM4 header */ 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; template 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 void DrawValueRow(const char* text, T value) { if constexpr (std::is_enum_v) { return DrawRow(text, "%X (%s)", value, magic_enum::enum_name(value).data()); } else if constexpr (std::is_integral_v) { return DrawRow(text, "%X", value); } else if constexpr (std::is_base_of_v, T>) { return DrawValueRow(text, value.Value()); } else { static_assert(false, "Unsupported type"); } } template void DrawValueRowList(const char* text, V arg, Extra&&... extra_args) { DrawValueRow(text, arg); if constexpr (sizeof...(extra_args) > 0) { DrawValueRowList(std::forward(extra_args)...); } } template 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)...); 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; } } inline std::optional exec_cli(const char* cli) { std::array buffer{}; std::string output; const auto f = popen(cli, "r"); if (!f) { pclose(f); return {}; } while (fgets(buffer.data(), buffer.size(), f)) { output += buffer.data(); } pclose(f); return output; } inline std::string RunDisassembler(const std::string& disassembler_cli, const std::vector& shader_code) { std::string shader_dis; if (disassembler_cli.empty()) { shader_dis = "No disassembler set"; } else { auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; constexpr std::string_view src_arg = "{src}"; std::string cli = disassembler_cli; const auto pos = cli.find(src_arg); if (pos == std::string::npos) { DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" + disassembler_cli); } else { cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); file.Write(shader_code); file.Close(); auto result = exec_cli(cli.c_str()); shader_dis = result.value_or("Could not disassemble shader"); if (shader_dis.empty()) { shader_dis = "Disassembly empty or failed"; } std::filesystem::remove(bin_path); } } return shader_dis; } } // namespace Core::Devtools::Widget