// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include #include "common/types.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #include using ThreadID = DWORD; #else #include #include using ThreadID = pthread_t; #endif namespace Core::Devtools { class Layer; namespace Widget { class FrameGraph; class ShaderList; } // namespace Widget } // namespace Core::Devtools namespace DebugStateType { enum class QueueType { dcb = 0, ccb = 1, acb = 2, }; struct QueueDump { QueueType type; u32 submit_num; u32 num2; // acb: queue_num; else: buffer_in_submit std::vector data; uintptr_t base_addr; }; struct PipelineShaderProgramDump { Vulkan::Liverpool::ShaderProgram user_data{}; std::vector code{}; }; struct PipelineComputerProgramDump { Vulkan::Liverpool::ComputeProgram cs_program{}; std::vector code{}; }; struct RegDump { bool is_compute{false}; static constexpr size_t MaxShaderStages = 5; Vulkan::Liverpool::Regs regs{}; std::array stages{}; PipelineComputerProgramDump cs_data{}; }; struct FrameDump { u32 frame_id; std::vector queues; std::unordered_map regs; // address -> reg dump }; struct ShaderDump { std::string name; vk::ShaderModule module; std::vector spv; std::vector isa; std::vector patch_spv; std::string patch_source{}; bool loaded_data = false; bool is_patched = false; std::string cache_spv_disasm{}; std::string cache_isa_disasm{}; std::string cache_patch_disasm{}; ShaderDump(std::string name, vk::ShaderModule module, std::vector spv, std::vector isa, std::vector patch_spv, bool is_patched) : name(std::move(name)), module(module), spv(std::move(spv)), isa(std::move(isa)), patch_spv(std::move(patch_spv)), is_patched(is_patched) {} ShaderDump(const ShaderDump& other) = delete; ShaderDump(ShaderDump&& other) noexcept : name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)}, isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)}, patch_source{std::move(other.patch_source)}, cache_spv_disasm{std::move(other.cache_spv_disasm)}, cache_isa_disasm{std::move(other.cache_isa_disasm)}, cache_patch_disasm{std::move(other.cache_patch_disasm)} {} ShaderDump& operator=(const ShaderDump& other) = delete; ShaderDump& operator=(ShaderDump&& other) noexcept { if (this == &other) return *this; name = std::move(other.name); module = std::move(other.module); spv = std::move(other.spv); isa = std::move(other.isa); patch_spv = std::move(other.patch_spv); patch_source = std::move(other.patch_source); cache_spv_disasm = std::move(other.cache_spv_disasm); cache_isa_disasm = std::move(other.cache_isa_disasm); cache_patch_disasm = std::move(other.cache_patch_disasm); return *this; } }; class DebugStateImpl { friend class Core::Devtools::Layer; friend class Core::Devtools::Widget::FrameGraph; friend class Core::Devtools::Widget::ShaderList; std::queue debug_message_popup; std::mutex guest_threads_mutex{}; std::vector guest_threads{}; std::atomic_bool is_guest_threads_paused = false; u64 pause_time{}; std::atomic_int32_t flip_frame_count = 0; std::atomic_int32_t gnm_frame_count = 0; s32 gnm_frame_dump_request_count = -1; std::unordered_map waiting_reg_dumps; std::unordered_map waiting_reg_dumps_dbg; bool waiting_submit_pause = false; bool should_show_frame_dump = false; std::shared_mutex frame_dump_list_mutex; std::vector frame_dump_list{}; std::vector shader_dump_list{}; public: void ShowDebugMessage(std::string message) { if (message.empty()) { return; } debug_message_popup.push(std::move(message)); } void AddCurrentThreadToGuestList(); void RemoveCurrentThreadFromGuestList(); void PauseGuestThreads(); void ResumeGuestThreads(); bool IsGuestThreadsPaused() const { return is_guest_threads_paused; } void IncFlipFrameNum() { ++flip_frame_count; } void IncGnmFrameNum() { ++gnm_frame_count; --gnm_frame_dump_request_count; } u32 GetFrameNum() const { return flip_frame_count; } bool DumpingCurrentFrame() const { 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 { return waiting_submit_pause && gnm_frame_dump_request_count == 0; } void RequestFrameDump(s32 count = 1); FrameDump& GetFrameDump() { return frame_dump_list[frame_dump_list.size() - gnm_frame_dump_request_count]; } void PushQueueDump(QueueDump dump); void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); void CollectShader(const std::string& name, vk::ShaderModule module, std::span spv, std::span raw_code, std::span patch_spv, bool is_patched); }; } // namespace DebugStateType extern DebugStateType::DebugStateImpl& DebugState;