mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2024-12-26 17:37:03 +00:00
Devtools: PM4 Explorer (#1094)
* Devtools: Pause system * Devtools: pm4 viewer - new menu bar - refactored video_info layer - dump & inspect pm4 packets - removed dumpPM4 config - renamed System to DebugState - add docking space - simple video info constrained to window size * Devtools: pm4 viewer - add combo to select the queue * Devtools: pm4 viewer - add hex editor * Devtools: pm4 viewer - dump current cmd * add monospaced font to devtools * Devtools: pm4 viewer - use spec op name avoid some allocations
This commit is contained in:
parent
009f956d8d
commit
af398e3684
|
@ -338,6 +338,19 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
|
|||
src/core/libraries/screenshot/screenshot.h
|
||||
)
|
||||
|
||||
set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||
src/core/devtools/layer.h
|
||||
src/core/devtools/gcn/gcn_context_regs.cpp
|
||||
src/core/devtools/gcn/gcn_op_names.cpp
|
||||
src/core/devtools/gcn/gcn_shader_regs.cpp
|
||||
src/core/devtools/widget/cmd_list.cpp
|
||||
src/core/devtools/widget/cmd_list.h
|
||||
src/core/devtools/widget/frame_dump.cpp
|
||||
src/core/devtools/widget/frame_dump.h
|
||||
src/core/devtools/widget/frame_graph.cpp
|
||||
src/core/devtools/widget/frame_graph.h
|
||||
)
|
||||
|
||||
set(COMMON src/common/logging/backend.cpp
|
||||
src/common/logging/backend.h
|
||||
src/common/logging/filter.cpp
|
||||
|
@ -449,6 +462,9 @@ set(CORE src/core/aerolib/stubs.cpp
|
|||
${USBD_LIB}
|
||||
${MISC_LIBS}
|
||||
${DIALOGS_LIB}
|
||||
${DEV_TOOLS}
|
||||
src/core/debug_state.cpp
|
||||
src/core/debug_state.h
|
||||
src/core/linker.cpp
|
||||
src/core/linker.h
|
||||
src/core/memory.cpp
|
||||
|
@ -623,8 +639,6 @@ set(IMGUI src/imgui/imgui_config.h
|
|||
src/imgui/imgui_layer.h
|
||||
src/imgui/imgui_std.h
|
||||
src/imgui/imgui_texture.h
|
||||
src/imgui/layer/video_info.cpp
|
||||
src/imgui/layer/video_info.h
|
||||
src/imgui/renderer/imgui_core.cpp
|
||||
src/imgui/renderer/imgui_core.h
|
||||
src/imgui/renderer/imgui_impl_sdl3.cpp
|
||||
|
@ -723,7 +737,7 @@ endif()
|
|||
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui)
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
|
|
56
REUSE.toml
56
REUSE.toml
|
@ -3,16 +3,16 @@ version = 1
|
|||
[[annotations]]
|
||||
path = [
|
||||
"REUSE.toml",
|
||||
"CMakeSettings.json",
|
||||
".github/FUNDING.yml",
|
||||
".github/shadps4.desktop",
|
||||
".github/shadps4.png",
|
||||
".gitmodules",
|
||||
"documents/changelog.txt",
|
||||
"documents/Quickstart/2.png",
|
||||
"documents/Screenshots/*",
|
||||
"scripts/ps4_names.txt",
|
||||
"src/images/about_icon.png",
|
||||
"CMakeSettings.json",
|
||||
".github/FUNDING.yml",
|
||||
".github/shadps4.desktop",
|
||||
".github/shadps4.png",
|
||||
".gitmodules",
|
||||
"documents/changelog.txt",
|
||||
"documents/Quickstart/2.png",
|
||||
"documents/Screenshots/*",
|
||||
"scripts/ps4_names.txt",
|
||||
"src/images/about_icon.png",
|
||||
"src/images/controller_icon.png",
|
||||
"src/images/dump_icon.png",
|
||||
"src/images/exit_icon.png",
|
||||
|
@ -26,18 +26,18 @@ path = [
|
|||
"src/images/folder_icon.png",
|
||||
"src/images/grid_icon.png",
|
||||
"src/images/iconsize_icon.png",
|
||||
"src/images/list_icon.png",
|
||||
"src/images/list_mode_icon.png",
|
||||
"src/images/pause_icon.png",
|
||||
"src/images/play_icon.png",
|
||||
"src/images/refresh_icon.png",
|
||||
"src/images/settings_icon.png",
|
||||
"src/images/stop_icon.png",
|
||||
"src/images/shadPS4.icns",
|
||||
"src/images/shadps4.ico",
|
||||
"src/images/themes_icon.png",
|
||||
"src/images/update_icon.png",
|
||||
"src/shadps4.qrc",
|
||||
"src/images/list_icon.png",
|
||||
"src/images/list_mode_icon.png",
|
||||
"src/images/pause_icon.png",
|
||||
"src/images/play_icon.png",
|
||||
"src/images/refresh_icon.png",
|
||||
"src/images/settings_icon.png",
|
||||
"src/images/stop_icon.png",
|
||||
"src/images/shadPS4.icns",
|
||||
"src/images/shadps4.ico",
|
||||
"src/images/themes_icon.png",
|
||||
"src/images/update_icon.png",
|
||||
"src/shadps4.qrc",
|
||||
"src/shadps4.rc",
|
||||
]
|
||||
precedence = "aggregate"
|
||||
|
@ -73,3 +73,15 @@ path = "src/imgui/renderer/fonts/NotoSansJP-Regular.ttf"
|
|||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2012 Google Inc. All Rights Reserved."
|
||||
SPDX-License-Identifier = "OFL-1.1"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/imgui/renderer/fonts/ProggyVector-Regular.ttf"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "Copyright (c) 2004, 2005 Tristan Grimmer"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/gcn/include/**"
|
||||
SPDX-FileCopyrightText = "NONE"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
|
|
5
externals/CMakeLists.txt
vendored
5
externals/CMakeLists.txt
vendored
|
@ -182,4 +182,7 @@ add_subdirectory(tracy)
|
|||
# pugixml
|
||||
if (NOT TARGET pugixml::pugixml)
|
||||
add_subdirectory(pugixml)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GCN Headers
|
||||
add_subdirectory(gcn)
|
8
externals/gcn/CMakeLists.txt
vendored
Normal file
8
externals/gcn/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
project(gcn LANGUAGES CXX)
|
||||
|
||||
add_library(gcn dummy.cpp)
|
||||
|
||||
target_include_directories(gcn INTERFACE include)
|
2
externals/gcn/dummy.cpp
vendored
Normal file
2
externals/gcn/dummy.cpp
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
15339
externals/gcn/include/gcn/si_ci_vi_merged_offset.h
vendored
Normal file
15339
externals/gcn/include/gcn/si_ci_vi_merged_offset.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
117
externals/gcn/include/gcn/si_ci_vi_merged_pm4_it_opcodes.h
vendored
Normal file
117
externals/gcn/include/gcn/si_ci_vi_merged_pm4_it_opcodes.h
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
***********************************************************************************************************************
|
||||
*
|
||||
* Copyright (c) 2015-2021 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
**********************************************************************************************************************/
|
||||
|
||||
#ifndef PM4_IT_OPCODES_H
|
||||
#define PM4_IT_OPCODES_H
|
||||
|
||||
enum IT_OpCodeType {
|
||||
IT_NOP = 0x10,
|
||||
IT_SET_BASE = 0x11,
|
||||
IT_CLEAR_STATE = 0x12,
|
||||
IT_INDEX_BUFFER_SIZE = 0x13,
|
||||
IT_DISPATCH_DIRECT = 0x15,
|
||||
IT_DISPATCH_INDIRECT = 0x16,
|
||||
IT_ATOMIC_GDS = 0x1D,
|
||||
IT_ATOMIC = 0x1E,
|
||||
IT_OCCLUSION_QUERY = 0x1F,
|
||||
IT_SET_PREDICATION = 0x20,
|
||||
IT_REG_RMW = 0x21,
|
||||
IT_COND_EXEC = 0x22,
|
||||
IT_PRED_EXEC = 0x23,
|
||||
IT_DRAW_INDIRECT = 0x24,
|
||||
IT_DRAW_INDEX_INDIRECT = 0x25,
|
||||
IT_INDEX_BASE = 0x26,
|
||||
IT_DRAW_INDEX_2 = 0x27,
|
||||
IT_CONTEXT_CONTROL = 0x28,
|
||||
IT_INDEX_TYPE = 0x2A,
|
||||
IT_DRAW_INDIRECT_MULTI = 0x2C,
|
||||
IT_DRAW_INDEX_AUTO = 0x2D,
|
||||
IT_NUM_INSTANCES = 0x2F,
|
||||
IT_DRAW_INDEX_MULTI_AUTO = 0x30,
|
||||
IT_INDIRECT_BUFFER_CNST = 0x33,
|
||||
IT_STRMOUT_BUFFER_UPDATE = 0x34,
|
||||
IT_DRAW_INDEX_OFFSET_2 = 0x35,
|
||||
IT_WRITE_DATA = 0x37,
|
||||
IT_DRAW_INDEX_INDIRECT_MULTI = 0x38,
|
||||
IT_MEM_SEMAPHORE = 0x39,
|
||||
IT_COPY_DW__SI__CI = 0x3B,
|
||||
IT_WAIT_REG_MEM = 0x3C,
|
||||
IT_INDIRECT_BUFFER = 0x3F,
|
||||
IT_COND_INDIRECT_BUFFER = 0x3F,
|
||||
IT_COPY_DATA = 0x40,
|
||||
IT_CP_DMA = 0x41,
|
||||
IT_PFP_SYNC_ME = 0x42,
|
||||
IT_SURFACE_SYNC = 0x43,
|
||||
IT_COND_WRITE = 0x45,
|
||||
IT_EVENT_WRITE = 0x46,
|
||||
IT_EVENT_WRITE_EOP = 0x47,
|
||||
IT_EVENT_WRITE_EOS = 0x48,
|
||||
IT_PREAMBLE_CNTL = 0x4A,
|
||||
IT_CONTEXT_REG_RMW = 0x51,
|
||||
IT_LOAD_SH_REG = 0x5F,
|
||||
IT_LOAD_CONFIG_REG = 0x60,
|
||||
IT_LOAD_CONTEXT_REG = 0x61,
|
||||
IT_SET_CONFIG_REG = 0x68,
|
||||
IT_SET_CONTEXT_REG = 0x69,
|
||||
IT_SET_CONTEXT_REG_INDIRECT = 0x73,
|
||||
IT_SET_SH_REG = 0x76,
|
||||
IT_SET_SH_REG_OFFSET = 0x77,
|
||||
IT_SCRATCH_RAM_WRITE = 0x7D,
|
||||
IT_SCRATCH_RAM_READ = 0x7E,
|
||||
IT_LOAD_CONST_RAM = 0x80,
|
||||
IT_WRITE_CONST_RAM = 0x81,
|
||||
IT_DUMP_CONST_RAM = 0x83,
|
||||
IT_INCREMENT_CE_COUNTER = 0x84,
|
||||
IT_INCREMENT_DE_COUNTER = 0x85,
|
||||
IT_WAIT_ON_CE_COUNTER = 0x86,
|
||||
IT_WAIT_ON_DE_COUNTER__SI = 0x87,
|
||||
IT_WAIT_ON_DE_COUNTER_DIFF = 0x88,
|
||||
IT_SWITCH_BUFFER = 0x8B,
|
||||
IT_DRAW_PREAMBLE__CI__VI = 0x36,
|
||||
IT_RELEASE_MEM__CI__VI = 0x49,
|
||||
IT_DMA_DATA__CI__VI = 0x50,
|
||||
IT_ACQUIRE_MEM__CI__VI = 0x58,
|
||||
IT_REWIND__CI__VI = 0x59,
|
||||
IT_LOAD_UCONFIG_REG__CI__VI = 0x5E,
|
||||
IT_SET_QUEUE_REG__CI__VI = 0x78,
|
||||
IT_SET_UCONFIG_REG__CI__VI = 0x79,
|
||||
IT_INDEX_ATTRIBUTES_INDIRECT__CI__VI = 0x91,
|
||||
IT_SET_SH_REG_INDEX__CI__VI = 0x9B,
|
||||
IT_SET_RESOURCES__CI__VI = 0xA0,
|
||||
IT_MAP_PROCESS__CI__VI = 0xA1,
|
||||
IT_MAP_QUEUES__CI__VI = 0xA2,
|
||||
IT_UNMAP_QUEUES__CI__VI = 0xA3,
|
||||
IT_QUERY_STATUS__CI__VI = 0xA4,
|
||||
IT_RUN_LIST__CI__VI = 0xA5,
|
||||
IT_LOAD_SH_REG_INDEX__VI = 0x63,
|
||||
IT_LOAD_CONTEXT_REG_INDEX__VI = 0x9F,
|
||||
IT_DUMP_CONST_RAM_OFFSET__VI = 0x9E,
|
||||
};
|
||||
|
||||
#define PM4_TYPE_0 0
|
||||
#define PM4_TYPE_2 2
|
||||
#define PM4_TYPE_3 3
|
||||
|
||||
#endif
|
|
@ -49,7 +49,6 @@ static bool isAutoUpdate = false;
|
|||
static bool isNullGpu = false;
|
||||
static bool shouldCopyGPUBuffers = false;
|
||||
static bool shouldDumpShaders = false;
|
||||
static bool shouldDumpPM4 = false;
|
||||
static u32 vblankDivider = 1;
|
||||
static bool vkValidation = false;
|
||||
static bool vkValidationSync = false;
|
||||
|
@ -156,10 +155,6 @@ bool dumpShaders() {
|
|||
return shouldDumpShaders;
|
||||
}
|
||||
|
||||
bool dumpPM4() {
|
||||
return shouldDumpPM4;
|
||||
}
|
||||
|
||||
bool isRdocEnabled() {
|
||||
return rdocEnable;
|
||||
}
|
||||
|
@ -228,10 +223,6 @@ void setDumpShaders(bool enable) {
|
|||
shouldDumpShaders = enable;
|
||||
}
|
||||
|
||||
void setDumpPM4(bool enable) {
|
||||
shouldDumpPM4 = enable;
|
||||
}
|
||||
|
||||
void setVkValidation(bool enable) {
|
||||
vkValidation = enable;
|
||||
}
|
||||
|
@ -461,7 +452,6 @@ void load(const std::filesystem::path& path) {
|
|||
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false);
|
||||
shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", false);
|
||||
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
|
||||
shouldDumpPM4 = toml::find_or<bool>(gpu, "dumpPM4", false);
|
||||
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
|
||||
}
|
||||
|
||||
|
@ -550,7 +540,6 @@ void save(const std::filesystem::path& path) {
|
|||
data["GPU"]["nullGpu"] = isNullGpu;
|
||||
data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers;
|
||||
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
||||
data["GPU"]["dumpPM4"] = shouldDumpPM4;
|
||||
data["GPU"]["vblankDivider"] = vblankDivider;
|
||||
data["Vulkan"]["gpuId"] = gpuId;
|
||||
data["Vulkan"]["validation"] = vkValidation;
|
||||
|
@ -609,7 +598,6 @@ void setDefaultValues() {
|
|||
isAutoUpdate = false;
|
||||
isNullGpu = false;
|
||||
shouldDumpShaders = false;
|
||||
shouldDumpPM4 = false;
|
||||
vblankDivider = 1;
|
||||
vkValidation = false;
|
||||
vkValidationSync = false;
|
||||
|
|
|
@ -34,7 +34,6 @@ bool autoUpdate();
|
|||
bool nullGpu();
|
||||
bool copyGPUCmdBuffers();
|
||||
bool dumpShaders();
|
||||
bool dumpPM4();
|
||||
bool isRdocEnabled();
|
||||
u32 vblankDiv();
|
||||
|
||||
|
@ -44,7 +43,6 @@ void setAutoUpdate(bool enable);
|
|||
void setNullGpu(bool enable);
|
||||
void setCopyGPUCmdBuffers(bool enable);
|
||||
void setDumpShaders(bool enable);
|
||||
void setDumpPM4(bool enable);
|
||||
void setVblankDiv(u32 value);
|
||||
void setGpuId(s32 selectedGpuId);
|
||||
void setScreenWidth(u32 width);
|
||||
|
|
|
@ -110,7 +110,6 @@ static auto UserPaths = [] {
|
|||
create_path(PathType::LogDir, user_dir / LOG_DIR);
|
||||
create_path(PathType::ScreenshotsDir, user_dir / SCREENSHOTS_DIR);
|
||||
create_path(PathType::ShaderDir, user_dir / SHADER_DIR);
|
||||
create_path(PathType::PM4Dir, user_dir / PM4_DIR);
|
||||
create_path(PathType::SaveDataDir, user_dir / SAVEDATA_DIR);
|
||||
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
||||
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
||||
|
|
|
@ -17,7 +17,6 @@ enum class PathType {
|
|||
LogDir, // Where log files are stored.
|
||||
ScreenshotsDir, // Where screenshots are stored.
|
||||
ShaderDir, // Where shaders are stored.
|
||||
PM4Dir, // Where command lists are stored.
|
||||
SaveDataDir, // Where guest save data is stored.
|
||||
TempDataDir, // Where game temp data is stored.
|
||||
GameDataDir, // Where game data is stored.
|
||||
|
@ -35,7 +34,6 @@ constexpr auto PORTABLE_DIR = "user";
|
|||
constexpr auto LOG_DIR = "log";
|
||||
constexpr auto SCREENSHOTS_DIR = "screenshots";
|
||||
constexpr auto SHADER_DIR = "shader";
|
||||
constexpr auto PM4_DIR = "pm4";
|
||||
constexpr auto SAVEDATA_DIR = "savedata";
|
||||
constexpr auto GAMEDATA_DIR = "data";
|
||||
constexpr auto TEMPDATA_DIR = "temp";
|
||||
|
|
102
src/core/debug_state.cpp
Normal file
102
src/core/debug_state.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/native_clock.h"
|
||||
#include "common/singleton.h"
|
||||
#include "debug_state.h"
|
||||
#include "libraries/kernel/event_queues.h"
|
||||
#include "libraries/kernel/time_management.h"
|
||||
#include "libraries/system/msgdialog.h"
|
||||
|
||||
using namespace DebugStateType;
|
||||
|
||||
DebugStateImpl& DebugState = *Common::Singleton<DebugStateImpl>::Instance();
|
||||
|
||||
static ThreadID ThisThreadID() {
|
||||
#ifdef _WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void PauseThread(ThreadID id) {
|
||||
#ifdef _WIN32
|
||||
auto handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
|
||||
SuspendThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ResumeThread(ThreadID id) {
|
||||
#ifdef _WIN32
|
||||
auto handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, id);
|
||||
ResumeThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DebugStateImpl::AddCurrentThreadToGuestList() {
|
||||
std::lock_guard lock{guest_threads_mutex};
|
||||
const ThreadID id = ThisThreadID();
|
||||
guest_threads.push_back(id);
|
||||
}
|
||||
|
||||
void DebugStateImpl::RemoveCurrentThreadFromGuestList() {
|
||||
std::lock_guard lock{guest_threads_mutex};
|
||||
const ThreadID id = ThisThreadID();
|
||||
std::erase_if(guest_threads, [&](const ThreadID& v) { return v == id; });
|
||||
}
|
||||
|
||||
void DebugStateImpl::PauseGuestThreads() {
|
||||
using namespace Libraries::MsgDialog;
|
||||
std::unique_lock lock{guest_threads_mutex};
|
||||
if (is_guest_threads_paused) {
|
||||
return;
|
||||
}
|
||||
if (ShouldPauseInSubmit()) {
|
||||
waiting_submit_pause = false;
|
||||
should_show_frame_dump = true;
|
||||
}
|
||||
bool self_guest = false;
|
||||
ThreadID self_id = ThisThreadID();
|
||||
for (const auto& id : guest_threads) {
|
||||
if (id == self_id) {
|
||||
self_guest = true;
|
||||
} else {
|
||||
PauseThread(id);
|
||||
}
|
||||
}
|
||||
pause_time = Libraries::Kernel::Dev::GetClock()->GetUptime();
|
||||
is_guest_threads_paused = true;
|
||||
lock.unlock();
|
||||
if (self_guest) {
|
||||
PauseThread(self_id);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugStateImpl::ResumeGuestThreads() {
|
||||
std::lock_guard lock{guest_threads_mutex};
|
||||
if (!is_guest_threads_paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 delta_time = Libraries::Kernel::Dev::GetClock()->GetUptime() - pause_time;
|
||||
Libraries::Kernel::Dev::GetInitialPtc() += delta_time;
|
||||
for (const auto& id : guest_threads) {
|
||||
ResumeThread(id);
|
||||
}
|
||||
is_guest_threads_paused = false;
|
||||
}
|
||||
|
||||
void DebugStateImpl::RequestFrameDump(s32 count) {
|
||||
gnm_frame_dump_request_count = count;
|
||||
frame_dump_list.clear();
|
||||
frame_dump_list.resize(count);
|
||||
waiting_submit_pause = true;
|
||||
}
|
126
src/core/debug_state.h
Normal file
126
src/core/debug_state.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
using ThreadID = DWORD;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
using ThreadID = pthread_t;
|
||||
#endif
|
||||
|
||||
namespace Core::Devtools {
|
||||
class Layer;
|
||||
namespace Widget {
|
||||
class FrameGraph;
|
||||
}
|
||||
} // namespace Core::Devtools
|
||||
|
||||
namespace DebugStateType {
|
||||
|
||||
enum class QueueType {
|
||||
acb,
|
||||
dcb,
|
||||
ccb,
|
||||
};
|
||||
|
||||
struct QueueDump {
|
||||
QueueType type;
|
||||
u32 submit_num;
|
||||
u32 num2; // acb: queue_num; else: buffer_in_submit
|
||||
std::vector<u32> data;
|
||||
};
|
||||
|
||||
struct FrameDump {
|
||||
std::vector<QueueDump> queues;
|
||||
};
|
||||
|
||||
class DebugStateImpl {
|
||||
friend class Core::Devtools::Layer;
|
||||
friend class Core::Devtools::Widget::FrameGraph;
|
||||
|
||||
std::mutex guest_threads_mutex{};
|
||||
std::vector<ThreadID> 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;
|
||||
bool waiting_submit_pause = false;
|
||||
bool should_show_frame_dump = false;
|
||||
|
||||
std::mutex frame_dump_list_mutex;
|
||||
std::vector<FrameDump> frame_dump_list{};
|
||||
|
||||
std::queue<std::string> debug_message_popup;
|
||||
|
||||
public:
|
||||
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 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) {
|
||||
std::unique_lock lock{frame_dump_list_mutex};
|
||||
GetFrameDump().queues.push_back(std::move(dump));
|
||||
}
|
||||
|
||||
void ShowDebugMessage(std::string message) {
|
||||
if (message.empty()) {
|
||||
return;
|
||||
}
|
||||
debug_message_popup.push(std::move(message));
|
||||
}
|
||||
};
|
||||
} // namespace DebugStateType
|
||||
|
||||
extern DebugStateType::DebugStateImpl& DebugState;
|
297
src/core/devtools/gcn/gcn_context_regs.cpp
Normal file
297
src/core/devtools/gcn/gcn_context_regs.cpp
Normal file
|
@ -0,0 +1,297 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#include "common/types.h"
|
||||
#include "gcn/si_ci_vi_merged_offset.h"
|
||||
|
||||
using namespace Pal::Gfx6;
|
||||
|
||||
namespace Core::Devtools::Gcn {
|
||||
const char* GetContextRegName(u32 reg_offset) {
|
||||
switch (reg_offset) {
|
||||
case mmDB_SHADER_CONTROL:
|
||||
return "mmDB_SHADER_CONTROL";
|
||||
case mmCB_SHADER_MASK:
|
||||
return "mmCB_SHADER_MASK";
|
||||
case mmPA_CL_CLIP_CNTL:
|
||||
return "mmPA_CL_CLIP_CNTL";
|
||||
case mmVGT_INSTANCE_STEP_RATE_0:
|
||||
return "mmVGT_INSTANCE_STEP_RATE_0";
|
||||
case mmVGT_INSTANCE_STEP_RATE_1:
|
||||
return "mmVGT_INSTANCE_STEP_RATE_1";
|
||||
case mmVGT_INDX_OFFSET:
|
||||
return "mmVGT_INDX_OFFSET";
|
||||
case mmVGT_SHADER_STAGES_EN:
|
||||
return "mmVGT_SHADER_STAGES_EN";
|
||||
case mmVGT_GS_MODE:
|
||||
return "mmVGT_GS_MODE";
|
||||
case mmVGT_STRMOUT_CONFIG:
|
||||
return "mmVGT_STRMOUT_CONFIG";
|
||||
case mmVGT_OUT_DEALLOC_CNTL:
|
||||
return "mmVGT_OUT_DEALLOC_CNTL";
|
||||
case mmVGT_VTX_CNT_EN:
|
||||
return "mmVGT_VTX_CNT_EN";
|
||||
case mmVGT_MAX_VTX_INDX:
|
||||
return "mmVGT_MAX_VTX_INDX";
|
||||
case mmVGT_MULTI_PRIM_IB_RESET_INDX:
|
||||
return "mmVGT_MULTI_PRIM_IB_RESET_INDX";
|
||||
case mmVGT_OUTPUT_PATH_CNTL:
|
||||
return "mmVGT_OUTPUT_PATH_CNTL";
|
||||
case mmVGT_GS_PER_ES:
|
||||
return "mmVGT_GS_PER_ES";
|
||||
case mmVGT_ES_PER_GS:
|
||||
return "mmVGT_ES_PER_GS";
|
||||
case mmVGT_GS_PER_VS:
|
||||
return "mmVGT_GS_PER_VS";
|
||||
case mmCB_COLOR0_BASE:
|
||||
return "mmCB_COLOR0_BASE";
|
||||
case mmCB_COLOR0_INFO:
|
||||
return "mmCB_COLOR0_INFO";
|
||||
case mmCB_COLOR0_CMASK_SLICE:
|
||||
return "mmCB_COLOR0_CMASK_SLICE";
|
||||
case mmCB_COLOR0_CLEAR_WORD0:
|
||||
return "mmCB_COLOR0_CLEAR_WORD0";
|
||||
case mmCB_COLOR0_CLEAR_WORD1:
|
||||
return "mmCB_COLOR0_CLEAR_WORD1";
|
||||
case mmCB_COLOR0_PITCH:
|
||||
return "mmCB_COLOR0_PITCH";
|
||||
case mmCB_COLOR0_SLICE:
|
||||
return "mmCB_COLOR0_SLICE";
|
||||
case mmCB_COLOR0_VIEW:
|
||||
return "mmCB_COLOR0_VIEW";
|
||||
case mmCB_COLOR0_DCC_CONTROL__VI:
|
||||
return "mmCB_COLOR0_DCC_CONTROL";
|
||||
case mmCB_COLOR0_CMASK:
|
||||
return "mmCB_COLOR0_CMASK";
|
||||
case mmCB_COLOR0_FMASK_SLICE:
|
||||
return "mmCB_COLOR0_FMASK_SLICE";
|
||||
case mmCB_COLOR0_FMASK:
|
||||
return "mmCB_COLOR0_FMASK";
|
||||
case mmCB_COLOR0_DCC_BASE__VI:
|
||||
return "mmCB_COLOR0_DCC_BASE";
|
||||
case mmCB_COLOR0_ATTRIB:
|
||||
return "mmCB_COLOR0_ATTRIB";
|
||||
case mmCB_COLOR1_BASE:
|
||||
return "mmCB_COLOR1_BASE";
|
||||
case mmCB_COLOR1_INFO:
|
||||
return "mmCB_COLOR1_INFO";
|
||||
case mmCB_COLOR1_ATTRIB:
|
||||
return "mmCB_COLOR1_ATTRIB";
|
||||
case mmCB_COLOR1_CMASK_SLICE:
|
||||
return "mmCB_COLOR1_CMASK_SLICE";
|
||||
case mmCB_COLOR1_CLEAR_WORD0:
|
||||
return "mmCB_COLOR1_CLEAR_WORD0";
|
||||
case mmCB_COLOR1_CLEAR_WORD1:
|
||||
return "mmCB_COLOR1_CLEAR_WORD1";
|
||||
case mmCB_COLOR1_PITCH:
|
||||
return "mmCB_COLOR1_PITCH";
|
||||
case mmCB_COLOR1_VIEW:
|
||||
return "mmCB_COLOR1_VIEW";
|
||||
case mmCB_COLOR2_INFO:
|
||||
return "mmCB_COLOR2_INFO";
|
||||
case mmCB_COLOR2_ATTRIB:
|
||||
return "mmCB_COLOR2_ATTRIB";
|
||||
case mmCB_COLOR2_CMASK_SLICE:
|
||||
return "mmCB_COLOR2_CMASK_SLICE";
|
||||
case mmCB_COLOR2_CLEAR_WORD0:
|
||||
return "mmCB_COLOR2_CLEAR_WORD0";
|
||||
case mmCB_COLOR2_CLEAR_WORD1:
|
||||
return "mmCB_COLOR2_CLEAR_WORD1";
|
||||
case mmCB_COLOR2_PITCH:
|
||||
return "mmCB_COLOR2_PITCH";
|
||||
case mmCB_COLOR2_VIEW:
|
||||
return "mmCB_COLOR2_VIEW";
|
||||
case mmCB_COLOR3_INFO:
|
||||
return "mmCB_COLOR3_INFO";
|
||||
case mmCB_COLOR3_CMASK_SLICE:
|
||||
return "mmCB_COLOR3_CMASK_SLICE";
|
||||
case mmCB_COLOR4_INFO:
|
||||
return "mmCB_COLOR4_INFO";
|
||||
case mmCB_COLOR5_INFO:
|
||||
return "mmCB_COLOR5_INFO";
|
||||
case mmCB_COLOR6_INFO:
|
||||
return "mmCB_COLOR6_INFO";
|
||||
case mmCB_COLOR7_INFO:
|
||||
return "mmCB_COLOR7_INFO";
|
||||
case mmDB_SRESULTS_COMPARE_STATE0:
|
||||
return "mmDB_SRESULTS_COMPARE_STATE0";
|
||||
case mmDB_SRESULTS_COMPARE_STATE1:
|
||||
return "mmDB_SRESULTS_COMPARE_STATE1";
|
||||
case mmDB_DEPTH_CONTROL:
|
||||
return "mmDB_DEPTH_CONTROL";
|
||||
case mmDB_EQAA:
|
||||
return "mmDB_EQAA";
|
||||
case mmPA_SU_POINT_SIZE:
|
||||
return "mmPA_SU_POINT_SIZE";
|
||||
case mmPA_SU_POINT_MINMAX:
|
||||
return "mmPA_SU_POINT_MINMAX";
|
||||
case mmPA_SU_SC_MODE_CNTL:
|
||||
return "mmPA_SU_SC_MODE_CNTL";
|
||||
case mmPA_SU_POLY_OFFSET_DB_FMT_CNTL:
|
||||
return "mmPA_SU_POLY_OFFSET_DB_FMT_CNTL";
|
||||
case mmPA_SC_CLIPRECT_RULE:
|
||||
return "mmPA_SC_CLIPRECT_RULE";
|
||||
case mmPA_SC_MODE_CNTL_0:
|
||||
return "mmPA_SC_MODE_CNTL_0";
|
||||
case mmPA_SC_MODE_CNTL_1:
|
||||
return "mmPA_SC_MODE_CNTL_1";
|
||||
case mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0:
|
||||
return "mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0";
|
||||
case mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0:
|
||||
return "mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0";
|
||||
case mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0:
|
||||
return "mmPA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0";
|
||||
case mmPA_SC_AA_MASK_X0Y0_X1Y0:
|
||||
return "mmPA_SC_AA_MASK_X0Y0_X1Y0";
|
||||
case mmPA_SC_AA_MASK_X0Y1_X1Y1:
|
||||
return "mmPA_SC_AA_MASK_X0Y1_X1Y1";
|
||||
case mmPA_SC_CENTROID_PRIORITY_0:
|
||||
return "mmPA_SC_CENTROID_PRIORITY_0";
|
||||
case mmPA_SC_CENTROID_PRIORITY_1:
|
||||
return "mmPA_SC_CENTROID_PRIORITY_1";
|
||||
case mmPA_SC_AA_CONFIG:
|
||||
return "mmPA_SC_AA_CONFIG";
|
||||
case mmDB_RENDER_CONTROL:
|
||||
return "mmDB_RENDER_CONTROL";
|
||||
case mmDB_STENCIL_CONTROL:
|
||||
return "mmDB_STENCIL_CONTROL";
|
||||
case mmDB_STENCILREFMASK:
|
||||
return "mmDB_STENCILREFMASK";
|
||||
case mmDB_STENCILREFMASK_BF:
|
||||
return "mmDB_STENCILREFMASK_BF";
|
||||
case mmDB_STENCIL_CLEAR:
|
||||
return "mmDB_STENCIL_CLEAR";
|
||||
case mmDB_DEPTH_CLEAR:
|
||||
return "mmDB_DEPTH_CLEAR";
|
||||
case mmCB_TARGET_MASK:
|
||||
return "mmCB_TARGET_MASK";
|
||||
case mmDB_Z_INFO:
|
||||
return "mmDB_Z_INFO";
|
||||
case mmDB_STENCIL_INFO:
|
||||
return "mmDB_STENCIL_INFO";
|
||||
case mmDB_Z_READ_BASE:
|
||||
return "mmDB_Z_READ_BASE";
|
||||
case mmDB_STENCIL_READ_BASE:
|
||||
return "mmDB_STENCIL_READ_BASE";
|
||||
case mmDB_Z_WRITE_BASE:
|
||||
return "mmDB_Z_WRITE_BASE";
|
||||
case mmDB_STENCIL_WRITE_BASE:
|
||||
return "mmDB_STENCIL_WRITE_BASE";
|
||||
case mmDB_DEPTH_INFO:
|
||||
return "mmDB_DEPTH_INFO";
|
||||
case mmDB_DEPTH_VIEW:
|
||||
return "mmDB_DEPTH_VIEW";
|
||||
case mmDB_DEPTH_SLICE:
|
||||
return "mmDB_DEPTH_SLICE";
|
||||
case mmDB_DEPTH_SIZE:
|
||||
return "mmDB_DEPTH_SIZE";
|
||||
case mmTA_BC_BASE_ADDR:
|
||||
return "mmTA_BC_BASE_ADDR";
|
||||
case mmCB_BLEND_RED:
|
||||
return "mmCB_BLEND_RED";
|
||||
case mmCB_BLEND_GREEN:
|
||||
return "mmCB_BLEND_GREEN";
|
||||
case mmCB_BLEND_BLUE:
|
||||
return "mmCB_BLEND_BLUE";
|
||||
case mmDB_ALPHA_TO_MASK:
|
||||
return "mmDB_ALPHA_TO_MASK";
|
||||
case mmCB_BLEND0_CONTROL:
|
||||
return "mmCB_BLEND0_CONTROL";
|
||||
case mmCB_BLEND1_CONTROL:
|
||||
return "mmCB_BLEND1_CONTROL";
|
||||
case mmCB_BLEND2_CONTROL:
|
||||
return "mmCB_BLEND2_CONTROL";
|
||||
case mmCB_BLEND3_CONTROL:
|
||||
return "mmCB_BLEND3_CONTROL";
|
||||
case mmCB_BLEND4_CONTROL:
|
||||
return "mmCB_BLEND4_CONTROL";
|
||||
case mmCB_BLEND5_CONTROL:
|
||||
return "mmCB_BLEND5_CONTROL";
|
||||
case mmCB_BLEND6_CONTROL:
|
||||
return "mmCB_BLEND6_CONTROL";
|
||||
case mmCB_BLEND7_CONTROL:
|
||||
return "mmCB_BLEND7_CONTROL";
|
||||
case mmDB_HTILE_DATA_BASE:
|
||||
return "mmDB_HTILE_DATA_BASE";
|
||||
case mmDB_HTILE_SURFACE:
|
||||
return "mmDB_HTILE_SURFACE";
|
||||
case mmPA_SU_LINE_CNTL:
|
||||
return "mmPA_SU_LINE_CNTL";
|
||||
case mmPA_SC_VPORT_ZMIN_0:
|
||||
return "mmPA_SC_VPORT_ZMIN_0";
|
||||
case mmPA_SC_VPORT_ZMAX_0:
|
||||
return "mmPA_SC_VPORT_ZMAX_0";
|
||||
case mmPA_SC_VPORT_SCISSOR_0_TL:
|
||||
return "mmPA_SC_VPORT_SCISSOR_0_TL";
|
||||
case mmPA_SC_VPORT_SCISSOR_0_BR:
|
||||
return "mmPA_SC_VPORT_SCISSOR_0_BR";
|
||||
case mmPA_SC_GENERIC_SCISSOR_TL:
|
||||
return "mmPA_SC_GENERIC_SCISSOR_TL";
|
||||
case mmPA_SC_GENERIC_SCISSOR_BR:
|
||||
return "mmPA_SC_GENERIC_SCISSOR_BR";
|
||||
case mmPA_CL_VPORT_XSCALE:
|
||||
return "mmPA_CL_VPORT_XSCALE";
|
||||
case mmPA_CL_VPORT_YSCALE:
|
||||
return "mmPA_CL_VPORT_YSCALE";
|
||||
case mmPA_CL_VPORT_ZSCALE:
|
||||
return "mmPA_CL_VPORT_ZSCALE";
|
||||
case mmPA_CL_VPORT_XOFFSET:
|
||||
return "mmPA_CL_VPORT_XOFFSET";
|
||||
case mmPA_CL_VPORT_YOFFSET:
|
||||
return "mmPA_CL_VPORT_YOFFSET";
|
||||
case mmPA_CL_VPORT_ZOFFSET:
|
||||
return "mmPA_CL_VPORT_ZOFFSET";
|
||||
case mmPA_CL_VTE_CNTL:
|
||||
return "mmPA_CL_VTE_CNTL";
|
||||
case mmPA_SC_SCREEN_SCISSOR_TL:
|
||||
return "mmPA_SC_SCREEN_SCISSOR_TL";
|
||||
case mmPA_SC_SCREEN_SCISSOR_BR:
|
||||
return "mmPA_SC_SCREEN_SCISSOR_BR";
|
||||
case mmPA_SU_HARDWARE_SCREEN_OFFSET:
|
||||
return "mmPA_SU_HARDWARE_SCREEN_OFFSET";
|
||||
case mmPA_SU_VTX_CNTL:
|
||||
return "mmPA_SU_VTX_CNTL";
|
||||
case mmPA_CL_GB_VERT_CLIP_ADJ:
|
||||
return "mmPA_CL_GB_VERT_CLIP_ADJ";
|
||||
case mmPA_CL_GB_HORZ_CLIP_ADJ:
|
||||
return "mmPA_CL_GB_HORZ_CLIP_ADJ";
|
||||
case mmPA_CL_GB_VERT_DISC_ADJ:
|
||||
return "mmPA_CL_GB_VERT_DISC_ADJ";
|
||||
case mmPA_CL_GB_HORZ_DISC_ADJ:
|
||||
return "mmPA_CL_GB_HORZ_DISC_ADJ";
|
||||
case mmCB_COLOR_CONTROL:
|
||||
return "mmCB_COLOR_CONTROL";
|
||||
case mmSPI_SHADER_Z_FORMAT:
|
||||
return "mmSPI_SHADER_Z_FORMAT";
|
||||
case mmSPI_SHADER_COL_FORMAT:
|
||||
return "mmSPI_SHADER_COL_FORMAT";
|
||||
case mmPA_CL_VS_OUT_CNTL:
|
||||
return "mmPA_CL_VS_OUT_CNTL";
|
||||
case mmSPI_VS_OUT_CONFIG:
|
||||
return "mmSPI_VS_OUT_CONFIG";
|
||||
case mmSPI_SHADER_POS_FORMAT:
|
||||
return "mmSPI_SHADER_POS_FORMAT";
|
||||
case mmSPI_PS_INPUT_ENA:
|
||||
return "mmSPI_PS_INPUT_ENA";
|
||||
case mmSPI_PS_INPUT_ADDR:
|
||||
return "mmSPI_PS_INPUT_ADDR";
|
||||
case mmSPI_PS_IN_CONTROL:
|
||||
return "mmSPI_PS_IN_CONTROL";
|
||||
case mmSPI_BARYC_CNTL:
|
||||
return "mmSPI_BARYC_CNTL";
|
||||
case mmSPI_PS_INPUT_CNTL_0:
|
||||
return "mmSPI_PS_INPUT_CNTL_0";
|
||||
case mmSPI_PS_INPUT_CNTL_1:
|
||||
return "mmSPI_PS_INPUT_CNTL_1";
|
||||
case mmSPI_PS_INPUT_CNTL_2:
|
||||
return "mmSPI_PS_INPUT_CNTL_2";
|
||||
case mmSPI_PS_INPUT_CNTL_3:
|
||||
return "mmSPI_PS_INPUT_CNTL_3";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<UNK>";
|
||||
}
|
||||
} // namespace Core::Devtools::Gcn
|
118
src/core/devtools/gcn/gcn_op_names.cpp
Normal file
118
src/core/devtools/gcn/gcn_op_names.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#include "common/types.h"
|
||||
#include "gcn/si_ci_vi_merged_pm4_it_opcodes.h"
|
||||
|
||||
namespace Core::Devtools::Gcn {
|
||||
const char* GetOpCodeName(u32 op) {
|
||||
switch (op) {
|
||||
case IT_NOP:
|
||||
return "IT_NOP";
|
||||
case IT_SET_BASE:
|
||||
return "IT_SET_BASE";
|
||||
case IT_INDEX_BUFFER_SIZE:
|
||||
return "IT_INDEX_BUFFER_SIZE";
|
||||
case IT_SET_PREDICATION:
|
||||
return "IT_SET_PREDICATION";
|
||||
case IT_COND_EXEC:
|
||||
return "IT_COND_EXEC";
|
||||
case IT_INDEX_BASE:
|
||||
return "IT_INDEX_BASE";
|
||||
case IT_INDEX_TYPE:
|
||||
return "IT_INDEX_TYPE";
|
||||
case IT_NUM_INSTANCES:
|
||||
return "IT_NUM_INSTANCES";
|
||||
case IT_STRMOUT_BUFFER_UPDATE:
|
||||
return "IT_STRMOUT_BUFFER_UPDATE";
|
||||
case IT_WRITE_DATA:
|
||||
return "IT_WRITE_DATA";
|
||||
case IT_MEM_SEMAPHORE:
|
||||
return "IT_MEM_SEMAPHORE";
|
||||
case IT_WAIT_REG_MEM:
|
||||
return "IT_WAIT_REG_MEM";
|
||||
case IT_INDIRECT_BUFFER:
|
||||
return "IT_INDIRECT_BUFFER";
|
||||
case IT_PFP_SYNC_ME:
|
||||
return "IT_PFP_SYNC_ME";
|
||||
case IT_EVENT_WRITE:
|
||||
return "IT_EVENT_WRITE";
|
||||
case IT_EVENT_WRITE_EOP:
|
||||
return "IT_EVENT_WRITE_EOP";
|
||||
case IT_EVENT_WRITE_EOS:
|
||||
return "IT_EVENT_WRITE_EOS";
|
||||
case IT_DMA_DATA__CI__VI:
|
||||
return "IT_DMA_DATA";
|
||||
case IT_ACQUIRE_MEM__CI__VI:
|
||||
return "IT_ACQUIRE_MEM";
|
||||
case IT_REWIND__CI__VI:
|
||||
return "IT_REWIND";
|
||||
case IT_SET_CONFIG_REG:
|
||||
return "IT_SET_CONFIG_REG";
|
||||
case IT_SET_CONTEXT_REG:
|
||||
return "IT_SET_CONTEXT_REG";
|
||||
case IT_SET_SH_REG:
|
||||
return "IT_SET_SH_REG";
|
||||
case IT_SET_UCONFIG_REG__CI__VI:
|
||||
return "IT_SET_UCONFIG_REG";
|
||||
case IT_INCREMENT_DE_COUNTER:
|
||||
return "IT_INCREMENT_DE_COUNTER";
|
||||
case IT_WAIT_ON_CE_COUNTER:
|
||||
return "IT_WAIT_ON_CE_COUNTER";
|
||||
case IT_DISPATCH_DIRECT:
|
||||
return "IT_DISPATCH_DIRECT";
|
||||
case IT_DISPATCH_INDIRECT:
|
||||
return "IT_DISPATCH_INDIRECT";
|
||||
case IT_OCCLUSION_QUERY:
|
||||
return "IT_OCCLUSION_QUERY";
|
||||
case IT_REG_RMW:
|
||||
return "IT_REG_RMW";
|
||||
case IT_PRED_EXEC:
|
||||
return "IT_PRED_EXEC";
|
||||
case IT_DRAW_INDIRECT:
|
||||
return "IT_DRAW_INDIRECT";
|
||||
case IT_DRAW_INDEX_INDIRECT:
|
||||
return "IT_DRAW_INDEX_INDIRECT";
|
||||
case IT_DRAW_INDEX_2:
|
||||
return "IT_DRAW_INDEX_2";
|
||||
case IT_DRAW_INDEX_OFFSET_2:
|
||||
return "IT_DRAW_INDEX_OFFSET_2";
|
||||
case IT_CONTEXT_CONTROL:
|
||||
return "IT_CONTEXT_CONTROL";
|
||||
case IT_DRAW_INDIRECT_MULTI:
|
||||
return "IT_DRAW_INDIRECT_MULTI";
|
||||
case IT_DRAW_INDEX_AUTO:
|
||||
return "IT_DRAW_INDEX_AUTO";
|
||||
case IT_DRAW_INDEX_MULTI_AUTO:
|
||||
return "IT_DRAW_INDEX_MULTI_AUTO";
|
||||
case IT_COPY_DATA:
|
||||
return "IT_COPY_DATA";
|
||||
case IT_CP_DMA:
|
||||
return "IT_CP_DMA";
|
||||
case IT_SURFACE_SYNC:
|
||||
return "IT_SURFACE_SYNC";
|
||||
case IT_COND_WRITE:
|
||||
return "IT_COND_WRITE";
|
||||
case IT_RELEASE_MEM__CI__VI:
|
||||
return "IT_RELEASE_MEM";
|
||||
case IT_WRITE_CONST_RAM:
|
||||
return "IT_WRITE_CONST_RAM"; // used in CCB
|
||||
case IT_WAIT_ON_DE_COUNTER_DIFF:
|
||||
return "IT_WAIT_ON_DE_COUNTER_DIFF"; // used in CCB
|
||||
case IT_DUMP_CONST_RAM:
|
||||
return "IT_DUMP_CONST_RAM"; // used in CCB
|
||||
case IT_INCREMENT_CE_COUNTER:
|
||||
return "IT_INCREMENT_CE_COUNTER"; // used in CCB
|
||||
case IT_CLEAR_STATE:
|
||||
return "IT_CLEAR_STATE";
|
||||
case 0xFF:
|
||||
return "<STUB (TMP)>";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "<UNK>";
|
||||
}
|
||||
} // namespace Core::Devtools::Gcn
|
171
src/core/devtools/gcn/gcn_shader_regs.cpp
Normal file
171
src/core/devtools/gcn/gcn_shader_regs.cpp
Normal file
|
@ -0,0 +1,171 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#include "common/types.h"
|
||||
#include "gcn/si_ci_vi_merged_offset.h"
|
||||
|
||||
using namespace Pal::Gfx6;
|
||||
|
||||
namespace Core::Devtools::Gcn {
|
||||
const char* GetShaderRegName(u32 reg_offset) {
|
||||
switch (reg_offset) {
|
||||
case mmSPI_SHADER_PGM_LO_VS:
|
||||
return "mmSPI_SHADER_PGM_LO_VS";
|
||||
case mmSPI_SHADER_PGM_HI_VS:
|
||||
return "mmSPI_SHADER_PGM_HI_VS";
|
||||
case mmSPI_SHADER_PGM_LO_PS:
|
||||
return "mmSPI_SHADER_PGM_LO_PS";
|
||||
case mmSPI_SHADER_PGM_HI_PS:
|
||||
return "mmSPI_SHADER_PGM_HI_PS";
|
||||
case mmSPI_SHADER_PGM_RSRC1_VS:
|
||||
return "mmSPI_SHADER_PGM_RSRC1_VS";
|
||||
case mmSPI_SHADER_PGM_RSRC2_VS:
|
||||
return "mmSPI_SHADER_PGM_RSRC2_VS";
|
||||
case mmSPI_SHADER_PGM_RSRC3_VS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_VS__CI__VI";
|
||||
case mmSPI_SHADER_PGM_RSRC1_PS:
|
||||
return "mmSPI_SHADER_PGM_RSRC1_PS";
|
||||
case mmSPI_SHADER_PGM_RSRC2_PS:
|
||||
return "mmSPI_SHADER_PGM_RSRC2_PS";
|
||||
case mmSPI_SHADER_PGM_RSRC3_PS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_PS__CI__VI";
|
||||
case mmSPI_SHADER_USER_DATA_PS_0:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_0";
|
||||
case mmSPI_SHADER_USER_DATA_PS_1:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_1";
|
||||
case mmSPI_SHADER_USER_DATA_PS_2:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_2";
|
||||
case mmSPI_SHADER_USER_DATA_PS_3:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_3";
|
||||
case mmSPI_SHADER_USER_DATA_PS_4:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_4";
|
||||
case mmSPI_SHADER_USER_DATA_PS_5:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_5";
|
||||
case mmSPI_SHADER_USER_DATA_PS_6:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_6";
|
||||
case mmSPI_SHADER_USER_DATA_PS_7:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_7";
|
||||
case mmSPI_SHADER_USER_DATA_PS_8:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_8";
|
||||
case mmSPI_SHADER_USER_DATA_PS_9:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_9";
|
||||
case mmSPI_SHADER_USER_DATA_PS_10:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_10";
|
||||
case mmSPI_SHADER_USER_DATA_PS_11:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_11";
|
||||
case mmSPI_SHADER_USER_DATA_PS_12:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_12";
|
||||
case mmSPI_SHADER_USER_DATA_PS_13:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_13";
|
||||
case mmSPI_SHADER_USER_DATA_PS_14:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_14";
|
||||
case mmSPI_SHADER_USER_DATA_PS_15:
|
||||
return "mmSPI_SHADER_USER_DATA_PS_15";
|
||||
case mmCOMPUTE_TMPRING_SIZE:
|
||||
return "mmCOMPUTE_TMPRING_SIZE";
|
||||
case mmCOMPUTE_PGM_LO:
|
||||
return "mmCOMPUTE_PGM_LO";
|
||||
case mmCOMPUTE_PGM_HI:
|
||||
return "mmCOMPUTE_PGM_HI";
|
||||
case mmCOMPUTE_PGM_RSRC1:
|
||||
return "mmCOMPUTE_PGM_RSRC1";
|
||||
case mmCOMPUTE_PGM_RSRC2:
|
||||
return "mmCOMPUTE_PGM_RSRC2";
|
||||
case mmCOMPUTE_USER_DATA_0:
|
||||
return "mmCOMPUTE_USER_DATA_0";
|
||||
case mmCOMPUTE_USER_DATA_1:
|
||||
return "mmCOMPUTE_USER_DATA_1";
|
||||
case mmCOMPUTE_USER_DATA_2:
|
||||
return "mmCOMPUTE_USER_DATA_2";
|
||||
case mmCOMPUTE_USER_DATA_3:
|
||||
return "mmCOMPUTE_USER_DATA_3";
|
||||
case mmCOMPUTE_USER_DATA_4:
|
||||
return "mmCOMPUTE_USER_DATA_4";
|
||||
case mmCOMPUTE_USER_DATA_5:
|
||||
return "mmCOMPUTE_USER_DATA_5";
|
||||
case mmCOMPUTE_USER_DATA_6:
|
||||
return "mmCOMPUTE_USER_DATA_6";
|
||||
case mmCOMPUTE_USER_DATA_7:
|
||||
return "mmCOMPUTE_USER_DATA_7";
|
||||
case mmCOMPUTE_USER_DATA_8:
|
||||
return "mmCOMPUTE_USER_DATA_8";
|
||||
case mmCOMPUTE_USER_DATA_9:
|
||||
return "mmCOMPUTE_USER_DATA_9";
|
||||
case mmCOMPUTE_USER_DATA_10:
|
||||
return "mmCOMPUTE_USER_DATA_10";
|
||||
case mmCOMPUTE_USER_DATA_11:
|
||||
return "mmCOMPUTE_USER_DATA_11";
|
||||
case mmCOMPUTE_USER_DATA_12:
|
||||
return "mmCOMPUTE_USER_DATA_12";
|
||||
case mmCOMPUTE_USER_DATA_13:
|
||||
return "mmCOMPUTE_USER_DATA_13";
|
||||
case mmCOMPUTE_USER_DATA_14:
|
||||
return "mmCOMPUTE_USER_DATA_14";
|
||||
case mmCOMPUTE_USER_DATA_15:
|
||||
return "mmCOMPUTE_USER_DATA_15";
|
||||
case mmCOMPUTE_NUM_THREAD_X:
|
||||
return "mmCOMPUTE_NUM_THREAD_X";
|
||||
case mmCOMPUTE_NUM_THREAD_Y:
|
||||
return "mmCOMPUTE_NUM_THREAD_Y";
|
||||
case mmCOMPUTE_NUM_THREAD_Z:
|
||||
return "mmCOMPUTE_NUM_THREAD_Z";
|
||||
case mmCOMPUTE_STATIC_THREAD_MGMT_SE0:
|
||||
return "mmCOMPUTE_STATIC_THREAD_MGMT_SE0";
|
||||
case mmCOMPUTE_STATIC_THREAD_MGMT_SE1:
|
||||
return "mmCOMPUTE_STATIC_THREAD_MGMT_SE1";
|
||||
case mmCOMPUTE_RESOURCE_LIMITS:
|
||||
return "mmCOMPUTE_RESOURCE_LIMITS";
|
||||
case mmSPI_SHADER_USER_DATA_VS_0:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_0";
|
||||
case mmSPI_SHADER_USER_DATA_VS_1:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_1";
|
||||
case mmSPI_SHADER_USER_DATA_VS_2:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_2";
|
||||
case mmSPI_SHADER_USER_DATA_VS_3:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_3";
|
||||
case mmSPI_SHADER_USER_DATA_VS_4:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_4";
|
||||
case mmSPI_SHADER_USER_DATA_VS_5:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_5";
|
||||
case mmSPI_SHADER_USER_DATA_VS_6:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_6";
|
||||
case mmSPI_SHADER_USER_DATA_VS_7:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_7";
|
||||
case mmSPI_SHADER_USER_DATA_VS_8:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_8";
|
||||
case mmSPI_SHADER_USER_DATA_VS_9:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_9";
|
||||
case mmSPI_SHADER_USER_DATA_VS_10:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_10";
|
||||
case mmSPI_SHADER_USER_DATA_VS_11:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_11";
|
||||
case mmSPI_SHADER_USER_DATA_VS_12:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_12";
|
||||
case mmSPI_SHADER_USER_DATA_VS_13:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_13";
|
||||
case mmSPI_SHADER_USER_DATA_VS_14:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_14";
|
||||
case mmSPI_SHADER_USER_DATA_VS_15:
|
||||
return "mmSPI_SHADER_USER_DATA_VS_15";
|
||||
case mmSPI_SHADER_USER_DATA_HS_0:
|
||||
return "mmSPI_SHADER_USER_DATA_HS_0";
|
||||
case mmSPI_SHADER_USER_DATA_HS_1:
|
||||
return "mmSPI_SHADER_USER_DATA_HS_1";
|
||||
case mmSPI_SHADER_USER_DATA_HS_9:
|
||||
return "mmSPI_SHADER_USER_DATA_HS_9";
|
||||
case mmSPI_SHADER_PGM_RSRC3_GS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_GS__CI__VI";
|
||||
case mmSPI_SHADER_PGM_RSRC3_ES__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_ES__CI__VI";
|
||||
case mmSPI_SHADER_PGM_RSRC3_LS__CI__VI:
|
||||
return "mmSPI_SHADER_PGM_RSRC3_LS__CI__VI";
|
||||
case mmSPI_SHADER_LATE_ALLOC_VS__CI__VI:
|
||||
return "mmSPI_SHADER_LATE_ALLOC_VS__CI__VI";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<UNK>";
|
||||
}
|
||||
} // namespace Core::Devtools::Gcn
|
215
src/core/devtools/layer.cpp
Normal file
215
src/core/devtools/layer.cpp
Normal file
|
@ -0,0 +1,215 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "layer.h"
|
||||
#include "widget/frame_dump.h"
|
||||
#include "widget/frame_graph.h"
|
||||
|
||||
using namespace ImGui;
|
||||
using namespace Core::Devtools;
|
||||
using L = Core::Devtools::Layer;
|
||||
|
||||
static bool show_simple_fps = false;
|
||||
static bool show_advanced_debug = false;
|
||||
|
||||
static int dump_frame_count = 1;
|
||||
|
||||
static Widget::FrameGraph frame_graph;
|
||||
static std::vector<Widget::FrameDumpViewer> frame_viewers;
|
||||
|
||||
static float debug_popup_timing = 3.0f;
|
||||
|
||||
void L::DrawMenuBar() {
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
if (BeginMainMenuBar()) {
|
||||
if (BeginMenu("Options")) {
|
||||
if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) {
|
||||
if (isSystemPaused) {
|
||||
DebugState.ResumeGuestThreads();
|
||||
} else {
|
||||
DebugState.PauseGuestThreads();
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (BeginMenu("GPU Tools")) {
|
||||
MenuItem("Show frame info", nullptr, &frame_graph.is_open);
|
||||
if (BeginMenu("Dump frames")) {
|
||||
SliderInt("Count", &dump_frame_count, 1, 5);
|
||||
if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) {
|
||||
DebugState.RequestFrameDump(dump_frame_count);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
EndMainMenuBar();
|
||||
}
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_F9, false)) {
|
||||
if (io.KeyCtrl && io.KeyAlt) {
|
||||
if (!DebugState.ShouldPauseInSubmit()) {
|
||||
DebugState.RequestFrameDump(dump_frame_count);
|
||||
}
|
||||
}
|
||||
if (!io.KeyCtrl && !io.KeyAlt) {
|
||||
if (isSystemPaused) {
|
||||
DebugState.ResumeGuestThreads();
|
||||
} else {
|
||||
DebugState.PauseGuestThreads();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawAdvanced() {
|
||||
DrawMenuBar();
|
||||
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
frame_graph.Draw();
|
||||
|
||||
if (isSystemPaused) {
|
||||
GetForegroundDrawList(GetMainViewport())
|
||||
->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused");
|
||||
}
|
||||
|
||||
if (DebugState.should_show_frame_dump) {
|
||||
DebugState.should_show_frame_dump = false;
|
||||
std::unique_lock lock{DebugState.frame_dump_list_mutex};
|
||||
while (!DebugState.frame_dump_list.empty()) {
|
||||
auto frame_dump = std::move(DebugState.frame_dump_list.back());
|
||||
DebugState.frame_dump_list.pop_back();
|
||||
frame_viewers.emplace_back(frame_dump);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = frame_viewers.begin(); it != frame_viewers.end();) {
|
||||
if (it->is_open) {
|
||||
it->Draw();
|
||||
++it;
|
||||
} else {
|
||||
it = frame_viewers.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
if (!DebugState.debug_message_popup.empty()) {
|
||||
if (debug_popup_timing > 0.0f) {
|
||||
debug_popup_timing -= io.DeltaTime;
|
||||
if (Begin("##devtools_msg", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove)) {
|
||||
BringWindowToDisplayFront(GetCurrentWindow());
|
||||
const auto display_size = io.DisplaySize;
|
||||
const auto& msg = DebugState.debug_message_popup.front();
|
||||
const auto padding = GetStyle().WindowPadding;
|
||||
const auto txt_size = CalcTextSize(&msg.front(), &msg.back() + 1, false, 250.0f);
|
||||
SetWindowPos({display_size.x - padding.x * 2.0f - txt_size.x, 50.0f});
|
||||
SetWindowSize({txt_size.x + padding.x * 2.0f, txt_size.y + padding.y * 2.0f});
|
||||
PushTextWrapPos(250.0f);
|
||||
TextEx(&msg.front(), &msg.back() + 1);
|
||||
PopTextWrapPos();
|
||||
}
|
||||
End();
|
||||
} else {
|
||||
DebugState.debug_message_popup.pop();
|
||||
debug_popup_timing = 3.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawSimple() {
|
||||
const auto io = GetIO();
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
}
|
||||
|
||||
void L::SetupSettings() {
|
||||
frame_graph.is_open = true;
|
||||
|
||||
ImGuiSettingsHandler handler{};
|
||||
handler.TypeName = "DevtoolsLayer";
|
||||
handler.TypeHash = ImHashStr(handler.TypeName);
|
||||
handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
|
||||
return std::string_view("Data") == name ? (void*)1 : nullptr;
|
||||
};
|
||||
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void*, const char* line) {
|
||||
int v;
|
||||
if (sscanf(line, "show_simple_fps=%d", &v) == 1) {
|
||||
show_simple_fps = v != 0;
|
||||
} 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) {
|
||||
buf->appendf("[%s][Data]\n", handler->TypeName);
|
||||
buf->appendf("show_simple_fps=%d\n", show_simple_fps);
|
||||
buf->appendf("show_advanced_debug=%d\n", show_advanced_debug);
|
||||
buf->appendf("show_frame_graph=%d\n", frame_graph.is_open);
|
||||
buf->appendf("dump_frame_count=%d\n", dump_frame_count);
|
||||
buf->append("\n");
|
||||
};
|
||||
AddSettingsHandler(&handler);
|
||||
|
||||
const ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||
DockBuilderAddNode(dock_id, 0);
|
||||
DockBuilderSetNodePos(dock_id, ImVec2{50.0, 50.0});
|
||||
DockBuilderFinish(dock_id);
|
||||
}
|
||||
|
||||
void L::Draw() {
|
||||
const auto io = GetIO();
|
||||
PushID("DevtoolsLayer");
|
||||
|
||||
if (!DebugState.IsGuestThreadsPaused()) {
|
||||
const auto fn = DebugState.flip_frame_count.load();
|
||||
frame_graph.AddFrame(fn, io.DeltaTime);
|
||||
}
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
||||
if (io.KeyCtrl) {
|
||||
show_advanced_debug = !show_advanced_debug;
|
||||
} else {
|
||||
show_simple_fps = !show_simple_fps;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_simple_fps) {
|
||||
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_FirstUseEver);
|
||||
if (Begin("Video Info", nullptr,
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
||||
KeepWindowInside();
|
||||
DrawSimple();
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_advanced_debug) {
|
||||
PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]);
|
||||
PushID("DevtoolsLayer");
|
||||
DrawAdvanced();
|
||||
PopID();
|
||||
PopFont();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
24
src/core/devtools/layer.h
Normal file
24
src/core/devtools/layer.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
namespace Core::Devtools {
|
||||
|
||||
class Layer final : public ImGui::Layer {
|
||||
|
||||
static void DrawMenuBar();
|
||||
|
||||
static void DrawAdvanced();
|
||||
|
||||
static void DrawSimple();
|
||||
|
||||
public:
|
||||
static void SetupSettings();
|
||||
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools
|
1223
src/core/devtools/widget/cmd_list.cpp
Normal file
1223
src/core/devtools/widget/cmd_list.cpp
Normal file
File diff suppressed because it is too large
Load diff
63
src/core/devtools/widget/cmd_list.h
Normal file
63
src/core/devtools/widget/cmd_list.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Credits to https://github.com/psucien/tlg-emu-tools/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
union PM4Type3Header;
|
||||
enum class PM4ItOpcode : u32;
|
||||
} // namespace AmdGpu
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class FrameDumpViewer;
|
||||
|
||||
class CmdListViewer {
|
||||
/*
|
||||
* Generic PM4 header
|
||||
*/
|
||||
union PM4Header {
|
||||
struct {
|
||||
u32 reserved : 16;
|
||||
u32 count : 14;
|
||||
u32 type : 2; // PM4_TYPE
|
||||
};
|
||||
u32 u32All;
|
||||
};
|
||||
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;
|
||||
std::vector<BatchInfo> batches{};
|
||||
uintptr_t cmdb_addr;
|
||||
size_t cmdb_size;
|
||||
|
||||
int batch_bp{-1};
|
||||
int vqid{255};
|
||||
|
||||
void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
|
||||
public:
|
||||
explicit CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cmd_list);
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
191
src/core/devtools/widget/frame_dump.cpp
Normal file
191
src/core/devtools/widget/frame_dump.cpp
Normal file
|
@ -0,0 +1,191 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdio>
|
||||
#include <fmt/chrono.h>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "frame_dump.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
using namespace ImGui;
|
||||
using namespace DebugStateType;
|
||||
|
||||
#define C_V(label, value, var, out) \
|
||||
if (Selectable(label, var == value)) { \
|
||||
var = value; \
|
||||
selected_cmd = -1; \
|
||||
out = true; \
|
||||
}
|
||||
|
||||
// 00 to 99
|
||||
static std::array<char, 3> small_int_to_str(const s32 i) {
|
||||
std::array<char, 3> label{};
|
||||
if (i == -1) {
|
||||
label[0] = 'N';
|
||||
label[1] = 'A';
|
||||
} else {
|
||||
label[0] = i / 10 + '0';
|
||||
label[1] = i % 10 + '0';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_frame_dump)) {
|
||||
static int unique_id = 0;
|
||||
id = unique_id++;
|
||||
|
||||
selected_queue_type = QueueType::dcb;
|
||||
selected_submit_num = 0;
|
||||
selected_queue_num2 = 0;
|
||||
|
||||
cmd_list_viewer.reserve(frame_dump.queues.size());
|
||||
for (const auto& cmd : frame_dump.queues) {
|
||||
cmd_list_viewer.emplace_back(this, cmd.data);
|
||||
if (cmd.type == QueueType::dcb && cmd.submit_num == selected_submit_num &&
|
||||
cmd.num2 == selected_queue_num2) {
|
||||
selected_cmd = cmd_list_viewer.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
cmdb_view.Open = false;
|
||||
cmdb_view.ReadOnly = true;
|
||||
}
|
||||
|
||||
FrameDumpViewer::~FrameDumpViewer() {}
|
||||
|
||||
void FrameDumpViewer::Draw() {
|
||||
if (!is_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
char name[32];
|
||||
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 (IsWindowAppearing()) {
|
||||
auto window = GetCurrentWindow();
|
||||
SetWindowSize(window, ImVec2{470.0f, 600.0f});
|
||||
}
|
||||
BeginGroup();
|
||||
TextEx("Queue type");
|
||||
SameLine();
|
||||
if (BeginCombo("##select_queue_type", magic_enum::enum_name(selected_queue_type).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
bool selected = false;
|
||||
#define COMBO(x) C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected)
|
||||
COMBO(QueueType::acb)
|
||||
COMBO(QueueType::dcb);
|
||||
COMBO(QueueType::ccb);
|
||||
if (selected) {
|
||||
selected_submit_num = selected_queue_num2 = -1;
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
SameLine();
|
||||
TextEx("Submit num");
|
||||
SameLine();
|
||||
if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
std::array<bool, 32> available_submits{};
|
||||
for (const auto& cmd : frame_dump.queues) {
|
||||
if (cmd.type == selected_queue_type) {
|
||||
available_submits[cmd.submit_num] = true;
|
||||
}
|
||||
}
|
||||
bool selected = false;
|
||||
for (int i = 0; i < available_submits.size(); ++i) {
|
||||
if (available_submits[i]) {
|
||||
char label[3]{};
|
||||
label[0] = i / 10 + '0';
|
||||
label[1] = i % 10 + '0';
|
||||
C_V(label, i, selected_submit_num, selected);
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
selected_queue_num2 = -1;
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
SameLine();
|
||||
TextEx(selected_queue_type == QueueType::acb ? "Queue num" : "Buffer num");
|
||||
SameLine();
|
||||
if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
std::array<bool, 32> available_queues{};
|
||||
for (const auto& cmd : frame_dump.queues) {
|
||||
if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) {
|
||||
available_queues[cmd.num2] = true;
|
||||
}
|
||||
}
|
||||
bool selected = false;
|
||||
for (int i = 0; i < available_queues.size(); ++i) {
|
||||
if (available_queues[i]) {
|
||||
char label[3]{};
|
||||
label[0] = i / 10 + '0';
|
||||
label[1] = i % 10 + '0';
|
||||
C_V(label, i, selected_queue_num2, selected);
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
const auto it = std::ranges::find_if(frame_dump.queues, [&](const auto& cmd) {
|
||||
return cmd.type == selected_queue_type &&
|
||||
cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2;
|
||||
});
|
||||
if (it != frame_dump.queues.end()) {
|
||||
selected_cmd = std::distance(frame_dump.queues.begin(), it);
|
||||
}
|
||||
}
|
||||
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();
|
||||
|
||||
if (selected_cmd != -1) {
|
||||
cmd_list_viewer[selected_cmd].Draw();
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
#undef C_V
|
41
src/core/devtools/widget/frame_dump.h
Normal file
41
src/core/devtools/widget/frame_dump.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "cmd_list.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class CmdListViewer;
|
||||
|
||||
class FrameDumpViewer {
|
||||
friend class CmdListViewer;
|
||||
|
||||
DebugStateType::FrameDump frame_dump;
|
||||
int id;
|
||||
|
||||
std::vector<CmdListViewer> cmd_list_viewer;
|
||||
MemoryEditor cmdb_view;
|
||||
|
||||
DebugStateType::QueueType selected_queue_type;
|
||||
s32 selected_submit_num;
|
||||
s32 selected_queue_num2;
|
||||
s32 selected_cmd = -1;
|
||||
|
||||
public:
|
||||
bool is_open = true;
|
||||
|
||||
explicit FrameDumpViewer(DebugStateType::FrameDump frame_dump);
|
||||
|
||||
~FrameDumpViewer();
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
99
src/core/devtools/widget/frame_graph.cpp
Normal file
99
src/core/devtools/widget/frame_graph.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "frame_graph.h"
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
constexpr float TARGET_FPS = 60.0f;
|
||||
constexpr float BAR_WIDTH_MULT = 1.4f;
|
||||
constexpr float BAR_HEIGHT_MULT = 1.25f;
|
||||
constexpr float FRAME_GRAPH_PADDING_Y = 3.0f;
|
||||
constexpr static float FRAME_GRAPH_HEIGHT = 50.0f;
|
||||
|
||||
void FrameGraph::Draw() {
|
||||
if (!is_open) {
|
||||
return;
|
||||
}
|
||||
SetNextWindowSize({340.0, 185.0f}, ImGuiCond_FirstUseEver);
|
||||
if (Begin("Video debug info", &is_open)) {
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
const auto& window = *ctx.CurrentWindow;
|
||||
auto& draw_list = *window.DrawList;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
static float deltaTime;
|
||||
static float frameRate;
|
||||
|
||||
if (!isSystemPaused) {
|
||||
deltaTime = io.DeltaTime * 1000.0f;
|
||||
frameRate = 1000.0f / deltaTime;
|
||||
}
|
||||
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate);
|
||||
Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(),
|
||||
DebugState.gnm_frame_count.load());
|
||||
SeparatorText("Frame graph");
|
||||
|
||||
const float full_width = GetContentRegionAvail().x;
|
||||
// Frame graph - inspired by
|
||||
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
|
||||
auto pos = GetCursorScreenPos();
|
||||
const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f};
|
||||
ItemSize(size);
|
||||
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
|
||||
float cur_pos_x = pos.x + full_width;
|
||||
pos.y += FRAME_GRAPH_PADDING_Y;
|
||||
const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT;
|
||||
|
||||
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
|
||||
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
|
||||
IM_COL32(0x33, 0x33, 0x33, 0xFF));
|
||||
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
|
||||
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
|
||||
const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE];
|
||||
const float dt_factor = target_dt / frame_info.delta;
|
||||
|
||||
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
|
||||
const float height =
|
||||
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT;
|
||||
|
||||
ImU32 color;
|
||||
if (dt_factor >= 0.95f) { // BLUE
|
||||
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
|
||||
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
|
||||
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
|
||||
int r = (int)(0xFF * t);
|
||||
color = IM_COL32(r, 0xFF, 0, 0xFF);
|
||||
} else { // YELLOW <> RED
|
||||
float t = dt_factor * 2.0f;
|
||||
int g = (int)(0xFF * t);
|
||||
color = IM_COL32(0xFF, g, 0, 0xFF);
|
||||
}
|
||||
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height},
|
||||
{cur_pos_x, final_pos_y}, color);
|
||||
cur_pos_x -= width;
|
||||
if (cur_pos_x < width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
draw_list.PopClipRect();
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
29
src/core/devtools/widget/frame_graph.h
Normal file
29
src/core/devtools/widget/frame_graph.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class FrameGraph {
|
||||
static constexpr u32 FRAME_BUFFER_SIZE = 1024;
|
||||
struct FrameInfo {
|
||||
u32 num;
|
||||
float delta;
|
||||
};
|
||||
|
||||
std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list{};
|
||||
|
||||
public:
|
||||
bool is_open = true;
|
||||
|
||||
void Draw();
|
||||
|
||||
void AddFrame(u32 num, float delta) {
|
||||
frame_list[num % FRAME_BUFFER_SIZE] = FrameInfo{num, delta};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
942
src/core/devtools/widget/imgui_memory_editor.h
Normal file
942
src/core/devtools/widget/imgui_memory_editor.h
Normal file
|
@ -0,0 +1,942 @@
|
|||
// SPDX-FileCopyrightText: 2024 Dear ImGui Club Contributors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Mini memory editor for Dear ImGui (to embed in your game/tools)
|
||||
// Get latest version at http://www.github.com/ocornut/imgui_club
|
||||
// Licensed under The MIT License (MIT)
|
||||
|
||||
// Right-click anywhere to access the Options menu!
|
||||
// You can adjust the keyboard repeat delay/rate in ImGuiIO.
|
||||
// The code assume a mono-space font for simplicity!
|
||||
// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font
|
||||
// before calling this.
|
||||
//
|
||||
// Usage:
|
||||
// // Create a window and draw memory editor inside it:
|
||||
// static MemoryEditor mem_edit_1;
|
||||
// static char data[0x10000];
|
||||
// size_t data_size = 0x10000;
|
||||
// mem_edit_1.DrawWindow("Memory Editor", data, data_size);
|
||||
//
|
||||
// Usage:
|
||||
// // If you already have a window, use DrawContents() instead:
|
||||
// static MemoryEditor mem_edit_2;
|
||||
// ImGui::Begin("MyWindow")
|
||||
// mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);
|
||||
// ImGui::End();
|
||||
//
|
||||
// Changelog:
|
||||
// - v0.10: initial version
|
||||
// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write.
|
||||
// - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since
|
||||
// imgui 1.61).
|
||||
// - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns".
|
||||
// - v0.26 (2018/08/02): fixed clicking on hex region
|
||||
// - v0.30 (2018/08/02): added data preview for common data types
|
||||
// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display
|
||||
// [@samhocevar]
|
||||
// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char*
|
||||
// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting.
|
||||
// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble]
|
||||
// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69.
|
||||
// - v0.36 (2020/05/05): minor tweaks, minor refactor.
|
||||
// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor
|
||||
// position appears on left-side of edit box. option popup appears on mouse release. fix MSVC
|
||||
// warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions.
|
||||
// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled.
|
||||
// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out.
|
||||
// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the
|
||||
// editor [@leiradel]
|
||||
// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.
|
||||
// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper,
|
||||
// relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.
|
||||
// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
// (#34)
|
||||
// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87.
|
||||
// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents().
|
||||
// warning fixes. (#35)
|
||||
// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2])
|
||||
// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from
|
||||
// highlighted selection. (#27) [@StrikerX3]
|
||||
// added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3]
|
||||
// fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight
|
||||
// color when using data preview. *BREAKING* added UserData field passed to
|
||||
// all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn.
|
||||
// (#50) [@silverweed]
|
||||
//
|
||||
// TODO:
|
||||
// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some
|
||||
// day.
|
||||
// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for
|
||||
// working out idioms of how to mix natural nav and our own...
|
||||
// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text
|
||||
// cursor appear at position 1 for one frame.
|
||||
// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // uint8_t, etc.
|
||||
#include <stdio.h> // sprintf, scanf
|
||||
|
||||
#if defined(_MSC_VER) || defined(_UCRT)
|
||||
#define _PRISizeT "I"
|
||||
#define ImSnprintf _snprintf
|
||||
#else
|
||||
#define _PRISizeT "z"
|
||||
#define ImSnprintf snprintf
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) || defined(_UCRT)
|
||||
#pragma warning(push)
|
||||
#pragma warning( \
|
||||
disable : 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||
#endif
|
||||
|
||||
struct MemoryEditor {
|
||||
enum DataFormat {
|
||||
DataFormat_Bin = 0,
|
||||
DataFormat_Dec = 1,
|
||||
DataFormat_Hex = 2,
|
||||
DataFormat_COUNT
|
||||
};
|
||||
|
||||
// Settings
|
||||
bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using
|
||||
// DrawWindow().
|
||||
bool ReadOnly; // = false // disable any editing.
|
||||
int Cols; // = 16 // number of columns to display.
|
||||
bool OptShowOptions; // = true // display options button/context menu. when disabled, options
|
||||
// will be locked unless you provide your own UI for them.
|
||||
bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float
|
||||
// representation of the currently selected bytes.
|
||||
bool OptShowHexII; // = false // display values in HexII representation instead of regular
|
||||
// hexadecimal: hide null/zero bytes, ascii values as ".X".
|
||||
bool OptShowAscii; // = true // display ASCII representation on the right side.
|
||||
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
|
||||
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
|
||||
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
|
||||
int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated
|
||||
// based on maximum displayed addr).
|
||||
float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add
|
||||
// custom widgets
|
||||
ImU32 HighlightColor; // // background color of highlighted bytes.
|
||||
|
||||
// Function handlers
|
||||
ImU8 (*ReadFn)(const ImU8* mem, size_t off,
|
||||
void* user_data); // = 0 // optional handler to read bytes.
|
||||
void (*WriteFn)(ImU8* mem, size_t off, ImU8 d,
|
||||
void* user_data); // = 0 // optional handler to write bytes.
|
||||
bool (*HighlightFn)(const ImU8* mem, size_t off,
|
||||
void* user_data); // = 0 // optional handler to return Highlight
|
||||
// property (to support non-contiguous highlighting).
|
||||
ImU32 (*BgColorFn)(const ImU8* mem, size_t off,
|
||||
void* user_data); // = 0 // optional handler to return custom background
|
||||
// color of individual bytes.
|
||||
void* UserData; // = NULL // user data forwarded to the function handlers
|
||||
|
||||
// Public read-only data
|
||||
bool MouseHovered; // set when mouse is hovering a value.
|
||||
size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set.
|
||||
|
||||
// [Internal State]
|
||||
bool ContentsWidthChanged;
|
||||
size_t DataPreviewAddr;
|
||||
size_t DataEditingAddr;
|
||||
bool DataEditingTakeFocus;
|
||||
char DataInputBuf[32];
|
||||
char AddrInputBuf[32];
|
||||
size_t GotoAddr;
|
||||
size_t HighlightMin, HighlightMax;
|
||||
int PreviewEndianness;
|
||||
ImGuiDataType PreviewDataType;
|
||||
|
||||
MemoryEditor() {
|
||||
// Settings
|
||||
Open = true;
|
||||
ReadOnly = false;
|
||||
Cols = 16;
|
||||
OptShowOptions = true;
|
||||
OptShowDataPreview = false;
|
||||
OptShowHexII = false;
|
||||
OptShowAscii = true;
|
||||
OptGreyOutZeroes = true;
|
||||
OptUpperCaseHex = true;
|
||||
OptMidColsCount = 8;
|
||||
OptAddrDigitsCount = 0;
|
||||
OptFooterExtraHeight = 0.0f;
|
||||
HighlightColor = IM_COL32(255, 255, 255, 50);
|
||||
ReadFn = nullptr;
|
||||
WriteFn = nullptr;
|
||||
HighlightFn = nullptr;
|
||||
BgColorFn = nullptr;
|
||||
UserData = nullptr;
|
||||
|
||||
// State/Internals
|
||||
ContentsWidthChanged = false;
|
||||
DataPreviewAddr = DataEditingAddr = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
memset(DataInputBuf, 0, sizeof(DataInputBuf));
|
||||
memset(AddrInputBuf, 0, sizeof(AddrInputBuf));
|
||||
GotoAddr = (size_t)-1;
|
||||
MouseHovered = false;
|
||||
MouseHoveredAddr = 0;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
PreviewEndianness = 0;
|
||||
PreviewDataType = ImGuiDataType_S32;
|
||||
}
|
||||
|
||||
void GotoAddrAndHighlight(size_t addr_min, size_t addr_max) {
|
||||
GotoAddr = addr_min;
|
||||
HighlightMin = addr_min;
|
||||
HighlightMax = addr_max;
|
||||
}
|
||||
|
||||
struct Sizes {
|
||||
int AddrDigitsCount;
|
||||
float LineHeight;
|
||||
float GlyphWidth;
|
||||
float HexCellWidth;
|
||||
float SpacingBetweenMidCols;
|
||||
float PosHexStart;
|
||||
float PosHexEnd;
|
||||
float PosAsciiStart;
|
||||
float PosAsciiEnd;
|
||||
float WindowWidth;
|
||||
|
||||
Sizes() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
};
|
||||
|
||||
void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr) {
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
s.AddrDigitsCount = OptAddrDigitsCount;
|
||||
if (s.AddrDigitsCount == 0)
|
||||
for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4)
|
||||
s.AddrDigitsCount++;
|
||||
s.LineHeight = ImGui::GetTextLineHeight();
|
||||
s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space
|
||||
s.HexCellWidth =
|
||||
(float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to
|
||||
// easily catch clicks everywhere
|
||||
s.SpacingBetweenMidCols =
|
||||
(float)(int)(s.HexCellWidth *
|
||||
0.25f); // Every OptMidColsCount columns we add a bit of extra spacing
|
||||
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
||||
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
||||
if (OptShowAscii) {
|
||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||
if (OptMidColsCount > 0)
|
||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) *
|
||||
s.SpacingBetweenMidCols;
|
||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
||||
}
|
||||
s.WindowWidth =
|
||||
s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||
}
|
||||
|
||||
// Standalone Memory Editor window
|
||||
void DrawWindow(const char* title, void* mem_data, size_t mem_size,
|
||||
size_t base_display_addr = 0x0000) {
|
||||
Sizes s;
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f),
|
||||
ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
|
||||
|
||||
Open = true;
|
||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar)) {
|
||||
DrawContents(mem_data, mem_size, base_display_addr);
|
||||
if (ContentsWidthChanged) {
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y));
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Memory Editor contents only
|
||||
void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000) {
|
||||
if (Cols < 1)
|
||||
Cols = 1;
|
||||
|
||||
ImU8* mem_data = (ImU8*)mem_data_void;
|
||||
Sizes s;
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos();
|
||||
|
||||
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent
|
||||
// click from moving the window. This is used as a facility since our main click detection
|
||||
// code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
||||
const float height_separator = style.ItemSpacing.y;
|
||||
float footer_height = OptFooterExtraHeight;
|
||||
if (OptShowOptions)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||
if (OptShowDataPreview)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 +
|
||||
ImGui::GetTextLineHeightWithSpacing() * 3;
|
||||
ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
// We are not really using the clipper API correctly here, because we rely on
|
||||
// visible_start_addr/visible_end_addr for our scrolling function.
|
||||
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(line_total_count, s.LineHeight);
|
||||
|
||||
bool data_next = false;
|
||||
|
||||
if (DataEditingAddr >= mem_size)
|
||||
DataEditingAddr = (size_t)-1;
|
||||
if (DataPreviewAddr >= mem_size)
|
||||
DataPreviewAddr = (size_t)-1;
|
||||
|
||||
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
|
||||
|
||||
size_t data_editing_addr_next = (size_t)-1;
|
||||
if (DataEditingAddr != (size_t)-1) {
|
||||
// Move cursor but only apply on next frame so scrolling with be synchronized (because
|
||||
// currently we can't change the scrolling while the window is being rendered)
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) &&
|
||||
(ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) {
|
||||
data_editing_addr_next = DataEditingAddr - Cols;
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) &&
|
||||
(ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols) {
|
||||
data_editing_addr_next = DataEditingAddr + Cols;
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) &&
|
||||
(ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) {
|
||||
data_editing_addr_next = DataEditingAddr - 1;
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) &&
|
||||
(ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) {
|
||||
data_editing_addr_next = DataEditingAddr + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw vertical separator
|
||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||
if (OptShowAscii)
|
||||
draw_list->AddLine(
|
||||
ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y),
|
||||
ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999),
|
||||
ImGui::GetColorU32(ImGuiCol_Border));
|
||||
|
||||
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
const ImU32 color_disabled =
|
||||
OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
||||
|
||||
const char* format_address =
|
||||
OptUpperCaseHex ? "%0*" _PRISizeT "X: " : "%0*" _PRISizeT "x: ";
|
||||
const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x";
|
||||
const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x";
|
||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||
|
||||
MouseHovered = false;
|
||||
MouseHoveredAddr = 0;
|
||||
|
||||
while (clipper.Step())
|
||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd;
|
||||
line_i++) // display only visible lines
|
||||
{
|
||||
size_t addr = (size_t)line_i * Cols;
|
||||
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||
|
||||
// Draw Hexadecimal
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++) {
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
|
||||
// Draw highlight or custom background color
|
||||
const bool is_highlight_from_user_range =
|
||||
(addr >= HighlightMin && addr < HighlightMax);
|
||||
const bool is_highlight_from_user_func =
|
||||
(HighlightFn && HighlightFn(mem_data, addr, UserData));
|
||||
const bool is_highlight_from_preview =
|
||||
(addr >= DataPreviewAddr &&
|
||||
addr < DataPreviewAddr + preview_data_type_size);
|
||||
|
||||
ImU32 bg_color = 0;
|
||||
bool is_next_byte_highlighted = false;
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func ||
|
||||
is_highlight_from_preview) {
|
||||
is_next_byte_highlighted =
|
||||
(addr + 1 < mem_size) &&
|
||||
((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) ||
|
||||
(HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) ||
|
||||
(addr + 1 < DataPreviewAddr + preview_data_type_size));
|
||||
bg_color = HighlightColor;
|
||||
} else if (BgColorFn != nullptr) {
|
||||
is_next_byte_highlighted =
|
||||
(addr + 1 < mem_size) &&
|
||||
((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0);
|
||||
bg_color = BgColorFn(mem_data, addr, UserData);
|
||||
}
|
||||
if (bg_color != 0) {
|
||||
float bg_width = s.GlyphWidth * 2;
|
||||
if (is_next_byte_highlighted || (n + 1 == Cols)) {
|
||||
bg_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols &&
|
||||
((n + 1) % OptMidColsCount) == 0)
|
||||
bg_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
draw_list->AddRectFilled(
|
||||
pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color);
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr) {
|
||||
// Display text input on current byte
|
||||
bool data_write = false;
|
||||
ImGui::PushID((void*)addr);
|
||||
if (DataEditingTakeFocus) {
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount,
|
||||
base_display_addr + addr);
|
||||
ImSnprintf(DataInputBuf, 32, format_byte,
|
||||
ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||
}
|
||||
struct InputTextUserData {
|
||||
// FIXME: We should have a way to retrieve the text edit cursor position
|
||||
// more easily in the API, this is rather tedious. This is such a ugly
|
||||
// mess we may be better off not using InputText() at all here.
|
||||
static int Callback(ImGuiInputTextCallbackData* data) {
|
||||
InputTextUserData* user_data = (InputTextUserData*)data->UserData;
|
||||
if (!data->HasSelection())
|
||||
user_data->CursorPos = data->CursorPos;
|
||||
#if IMGUI_VERSION_NUM < 19102
|
||||
if (data->Flags & ImGuiInputTextFlags_ReadOnly)
|
||||
return 0;
|
||||
#endif
|
||||
if (data->SelectionStart == 0 &&
|
||||
data->SelectionEnd == data->BufTextLen) {
|
||||
// When not editing a byte, always refresh its InputText content
|
||||
// pulled from underlying memory data (this is a bit tricky,
|
||||
// since InputText technically "owns" the master copy of the
|
||||
// buffer we edit it in there)
|
||||
data->DeleteChars(0, data->BufTextLen);
|
||||
data->InsertChars(0, user_data->CurrentBufOverwrite);
|
||||
data->SelectionStart = 0;
|
||||
data->SelectionEnd = 2;
|
||||
data->CursorPos = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char CurrentBufOverwrite[3]; // Input
|
||||
int CursorPos; // Output
|
||||
};
|
||||
InputTextUserData input_text_user_data;
|
||||
input_text_user_data.CursorPos = -1;
|
||||
ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte,
|
||||
ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal |
|
||||
ImGuiInputTextFlags_EnterReturnsTrue |
|
||||
ImGuiInputTextFlags_AutoSelectAll |
|
||||
ImGuiInputTextFlags_NoHorizontalScroll |
|
||||
ImGuiInputTextFlags_CallbackAlways;
|
||||
if (ReadOnly)
|
||||
flags |= ImGuiInputTextFlags_ReadOnly;
|
||||
flags |=
|
||||
ImGuiInputTextFlags_AlwaysOverwrite; // was
|
||||
// ImGuiInputTextFlags_AlwaysInsertMode
|
||||
ImGui::SetNextItemWidth(s.GlyphWidth * 2);
|
||||
if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf),
|
||||
flags, InputTextUserData::Callback,
|
||||
&input_text_user_data))
|
||||
data_write = data_next = true;
|
||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
if (input_text_user_data.CursorPos >= 2)
|
||||
data_write = data_next = true;
|
||||
if (data_editing_addr_next != (size_t)-1)
|
||||
data_write = data_next = false;
|
||||
unsigned int data_input_value = 0;
|
||||
if (!ReadOnly && data_write &&
|
||||
sscanf(DataInputBuf, "%X", &data_input_value) == 1) {
|
||||
if (WriteFn)
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);
|
||||
else
|
||||
mem_data[addr] = (ImU8)data_input_value;
|
||||
}
|
||||
ImGui::PopID();
|
||||
} else {
|
||||
// NB: The trailing space is not visible but ensure there's no gap that the
|
||||
// mouse cannot click on.
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||
|
||||
if (OptShowHexII) {
|
||||
if ((b >= 32 && b < 128))
|
||||
ImGui::Text(".%c ", b);
|
||||
else if (b == 0xFF && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("## ");
|
||||
else if (b == 0x00)
|
||||
ImGui::Text(" ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
} else {
|
||||
if (b == 0 && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("00 ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = addr;
|
||||
if (ImGui::IsMouseClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OptShowAscii) {
|
||||
// Draw ASCII values
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = (size_t)line_i * Cols;
|
||||
|
||||
const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x;
|
||||
const size_t mouse_addr =
|
||||
(mouse_off_x >= 0.0f && mouse_off_x < s.PosAsciiEnd - s.PosAsciiStart)
|
||||
? addr + (size_t)(mouse_off_x / s.GlyphWidth)
|
||||
: (size_t)-1;
|
||||
|
||||
ImGui::PushID(line_i);
|
||||
if (ImGui::InvisibleButton(
|
||||
"ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight))) {
|
||||
DataEditingAddr = DataPreviewAddr = mouse_addr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
MouseHovered = true;
|
||||
MouseHoveredAddr = mouse_addr;
|
||||
}
|
||||
ImGui::PopID();
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++) {
|
||||
if (addr == DataEditingAddr) {
|
||||
draw_list->AddRectFilled(
|
||||
pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight),
|
||||
ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(
|
||||
pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight),
|
||||
ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
} else if (BgColorFn) {
|
||||
draw_list->AddRectFilled(
|
||||
pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight),
|
||||
BgColorFn(mem_data, addr, UserData));
|
||||
}
|
||||
unsigned char c =
|
||||
ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled,
|
||||
&display_c, &display_c + 1);
|
||||
pos.x += s.GlyphWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
const float child_width = ImGui::GetWindowSize().x;
|
||||
ImGui::EndChild();
|
||||
|
||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to
|
||||
// get the contents size from the child)
|
||||
ImGui::SetCursorPosX(s.WindowWidth);
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
|
||||
if (data_next && DataEditingAddr + 1 < mem_size) {
|
||||
DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1;
|
||||
DataEditingTakeFocus = true;
|
||||
} else if (data_editing_addr_next != (size_t)-1) {
|
||||
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
|
||||
const bool lock_show_data_preview = OptShowDataPreview;
|
||||
if (OptShowOptions) {
|
||||
ImGui::Separator();
|
||||
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
|
||||
if (lock_show_data_preview) {
|
||||
ImGui::Separator();
|
||||
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
|
||||
const ImVec2 contents_pos_end(contents_pos_start.x + child_width,
|
||||
ImGui::GetCursorScreenPos().y);
|
||||
// ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end,
|
||||
// IM_COL32(255, 0, 0, 255));
|
||||
if (OptShowOptions)
|
||||
if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end))
|
||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) &&
|
||||
ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("OptionsPopup");
|
||||
|
||||
if (ImGui::BeginPopup("OptionsPopup")) {
|
||||
ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);
|
||||
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) {
|
||||
ContentsWidthChanged = true;
|
||||
if (Cols < 1)
|
||||
Cols = 1;
|
||||
}
|
||||
ImGui::Checkbox("Show Data Preview", &OptShowDataPreview);
|
||||
ImGui::Checkbox("Show HexII", &OptShowHexII);
|
||||
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) {
|
||||
ContentsWidthChanged = true;
|
||||
}
|
||||
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
|
||||
ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex);
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size,
|
||||
size_t base_display_addr) {
|
||||
IM_UNUSED(mem_data);
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const char* format_range = OptUpperCaseHex ? "Range %0*" _PRISizeT "X..%0*" _PRISizeT "X"
|
||||
: "Range %0*" _PRISizeT "x..%0*" _PRISizeT "x";
|
||||
|
||||
// Options menu
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("OptionsPopup");
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount,
|
||||
base_display_addr + mem_size - 1);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth +
|
||||
style.FramePadding.x * 2.0f);
|
||||
if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf),
|
||||
ImGuiInputTextFlags_CharsHexadecimal |
|
||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
size_t goto_addr;
|
||||
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1) {
|
||||
GotoAddr = goto_addr - base_display_addr;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
if (GotoAddr != (size_t)-1) {
|
||||
if (GotoAddr < mem_size) {
|
||||
ImGui::BeginChild("##scrolling");
|
||||
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y +
|
||||
(GotoAddr / Cols) * ImGui::GetTextLineHeight());
|
||||
ImGui::EndChild();
|
||||
DataEditingAddr = DataPreviewAddr = GotoAddr;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
GotoAddr = (size_t)-1;
|
||||
}
|
||||
|
||||
// if (MouseHovered)
|
||||
//{
|
||||
// ImGui::SameLine();
|
||||
// ImGui::Text("Hovered: %p", MouseHoveredAddr);
|
||||
// }
|
||||
}
|
||||
|
||||
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size,
|
||||
size_t base_display_addr) {
|
||||
IM_UNUSED(base_display_addr);
|
||||
ImU8* mem_data = (ImU8*)mem_data_void;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Preview as:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f +
|
||||
style.ItemInnerSpacing.x);
|
||||
|
||||
static const ImGuiDataType supported_data_types[] = {
|
||||
ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16,
|
||||
ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64,
|
||||
ImGuiDataType_Float, ImGuiDataType_Double};
|
||||
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType),
|
||||
ImGuiComboFlags_HeightLargest)) {
|
||||
for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++) {
|
||||
ImGuiDataType data_type = supported_data_types[n];
|
||||
if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))
|
||||
PreviewDataType = data_type;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f +
|
||||
style.ItemInnerSpacing.x);
|
||||
ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0");
|
||||
|
||||
char buf[128] = "";
|
||||
float x = s.GlyphWidth * 6.0f;
|
||||
bool has_value = DataPreviewAddr != (size_t)-1;
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec,
|
||||
buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
ImGui::Text("Dec");
|
||||
ImGui::SameLine(x);
|
||||
ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex,
|
||||
buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
ImGui::Text("Hex");
|
||||
ImGui::SameLine(x);
|
||||
ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin,
|
||||
buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
buf[IM_ARRAYSIZE(buf) - 1] = 0;
|
||||
ImGui::Text("Bin");
|
||||
ImGui::SameLine(x);
|
||||
ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
}
|
||||
|
||||
// Utilities for Data Preview (since we don't access imgui_internal.h)
|
||||
// FIXME: This technically depends on ImGuiDataType order.
|
||||
const char* DataTypeGetDesc(ImGuiDataType data_type) const {
|
||||
const char* descs[] = {"Int8", "Uint8", "Int16", "Uint16", "Int32",
|
||||
"Uint32", "Int64", "Uint64", "Float", "Double"};
|
||||
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs));
|
||||
return descs[data_type];
|
||||
}
|
||||
|
||||
size_t DataTypeGetSize(ImGuiDataType data_type) const {
|
||||
const size_t sizes[] = {1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double)};
|
||||
IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes));
|
||||
return sizes[data_type];
|
||||
}
|
||||
|
||||
const char* DataFormatGetDesc(DataFormat data_format) const {
|
||||
const char* descs[] = {"Bin", "Dec", "Hex"};
|
||||
IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);
|
||||
return descs[data_format];
|
||||
}
|
||||
|
||||
bool IsBigEndian() const {
|
||||
uint16_t x = 1;
|
||||
char c[2];
|
||||
memcpy(c, &x, 2);
|
||||
return c[0] != 0;
|
||||
}
|
||||
|
||||
static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian) {
|
||||
if (is_little_endian) {
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
} else {
|
||||
return memcpy(_dst, _src, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s,
|
||||
int is_little_endian) {
|
||||
if (is_little_endian) {
|
||||
return memcpy(_dst, _src, s);
|
||||
} else {
|
||||
uint8_t* dst = (uint8_t*)_dst;
|
||||
uint8_t* src = (uint8_t*)_src + s - 1;
|
||||
for (int i = 0, n = (int)s; i < n; ++i)
|
||||
memcpy(dst++, src--, 1);
|
||||
return _dst;
|
||||
}
|
||||
}
|
||||
|
||||
void* EndiannessCopy(void* dst, void* src, size_t size) const {
|
||||
static void* (*fp)(void*, void*, size_t, int) = nullptr;
|
||||
if (fp == nullptr)
|
||||
fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianness);
|
||||
}
|
||||
|
||||
const char* FormatBinary(const uint8_t* buf, int width) const {
|
||||
IM_ASSERT(width <= 64);
|
||||
size_t out_n = 0;
|
||||
static char out_buf[64 + 8 + 1];
|
||||
int n = width / 8;
|
||||
for (int j = n - 1; j >= 0; --j) {
|
||||
for (int i = 0; i < 8; ++i)
|
||||
out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';
|
||||
out_buf[out_n++] = ' ';
|
||||
}
|
||||
IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));
|
||||
out_buf[out_n] = 0;
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
// [Internal]
|
||||
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size,
|
||||
ImGuiDataType data_type, DataFormat data_format, char* out_buf,
|
||||
size_t out_buf_size) const {
|
||||
uint8_t buf[8];
|
||||
size_t elem_size = DataTypeGetSize(data_type);
|
||||
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
||||
if (ReadFn)
|
||||
for (int i = 0, n = (int)size; i < n; ++i)
|
||||
buf[i] = ReadFn(mem_data, addr + i, UserData);
|
||||
else
|
||||
memcpy(buf, mem_data + addr, size);
|
||||
|
||||
if (data_format == DataFormat_Bin) {
|
||||
uint8_t binbuf[8];
|
||||
EndiannessCopy(binbuf, buf, size);
|
||||
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
||||
return;
|
||||
}
|
||||
|
||||
out_buf[0] = 0;
|
||||
switch (data_type) {
|
||||
case ImGuiDataType_S8: {
|
||||
int8_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%hhd", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U8: {
|
||||
uint8_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%hhu", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S16: {
|
||||
int16_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%hd", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U16: {
|
||||
uint16_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%hu", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S32: {
|
||||
int32_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%d", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%08x", data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U32: {
|
||||
uint32_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%u", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%08x", data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S64: {
|
||||
int64_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U64: {
|
||||
uint64_t data = 0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Float: {
|
||||
float data = 0.0f;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%f", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%a", data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Double: {
|
||||
double data = 0.0;
|
||||
EndiannessCopy(&data, buf, size);
|
||||
if (data_format == DataFormat_Dec) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%f", data);
|
||||
return;
|
||||
}
|
||||
if (data_format == DataFormat_Hex) {
|
||||
ImSnprintf(out_buf, out_buf_size, "%a", data);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case ImGuiDataType_COUNT:
|
||||
break;
|
||||
} // Switch
|
||||
IM_ASSERT(0); // Shouldn't reach
|
||||
}
|
||||
};
|
||||
|
||||
#undef _PRISizeT
|
||||
#undef ImSnprintf
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
|
@ -11,6 +11,7 @@
|
|||
#include "common/path_util.h"
|
||||
#include "common/slot_vector.h"
|
||||
#include "core/address_space.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
@ -320,20 +321,6 @@ static void WaitGpuIdle() {
|
|||
cv_lock.wait(lock, [] { return submission_lock == 0; });
|
||||
}
|
||||
|
||||
static void DumpCommandList(std::span<const u32> cmd_list, const std::string& postfix) {
|
||||
using namespace Common::FS;
|
||||
const auto dump_dir = GetUserPath(PathType::PM4Dir);
|
||||
if (!std::filesystem::exists(dump_dir)) {
|
||||
std::filesystem::create_directories(dump_dir);
|
||||
}
|
||||
if (cmd_list.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto filename = fmt::format("{:08}_{}", frames_submitted, postfix);
|
||||
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write};
|
||||
file.WriteSpan(cmd_list);
|
||||
}
|
||||
|
||||
// Write a special ending NOP packet with N DWs data block
|
||||
template <u32 data_block_size>
|
||||
static inline u32* WriteTrailingNop(u32* cmdbuf) {
|
||||
|
@ -507,16 +494,18 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
|
|||
|
||||
WaitGpuIdle();
|
||||
|
||||
/* Suspend logic goes here */
|
||||
if (DebugState.ShouldPauseInSubmit()) {
|
||||
DebugState.PauseGuestThreads();
|
||||
}
|
||||
|
||||
auto vqid = gnm_vqid - 1;
|
||||
auto& asc_queue = asc_queues[{vqid}];
|
||||
const auto* acb_ptr = reinterpret_cast<const u32*>(asc_queue.map_addr + *asc_queue.read_addr);
|
||||
const auto acb_size = next_offs_dw ? (next_offs_dw << 2u) - *asc_queue.read_addr
|
||||
: (asc_queue.ring_size_dw << 2u) - *asc_queue.read_addr;
|
||||
const std::span<const u32> acb_span{acb_ptr, acb_size >> 2u};
|
||||
const std::span acb_span{acb_ptr, acb_size >> 2u};
|
||||
|
||||
if (Config::dumpPM4()) {
|
||||
if (DebugState.DumpingCurrentFrame()) {
|
||||
static auto last_frame_num = -1LL;
|
||||
static u32 seq_num{};
|
||||
if (last_frame_num == frames_submitted) {
|
||||
|
@ -536,8 +525,14 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
|
|||
acb = {indirect_buffer->Address<const u32>(), indirect_buffer->ib_size};
|
||||
}
|
||||
|
||||
// File name format is: <queue>_<queue num>_<submit_num>
|
||||
DumpCommandList(acb, fmt::format("acb_{}_{}", gnm_vqid, seq_num));
|
||||
using namespace DebugStateType;
|
||||
|
||||
DebugState.PushQueueDump({
|
||||
.type = QueueType::acb,
|
||||
.submit_num = seq_num,
|
||||
.num2 = gnm_vqid,
|
||||
.data = {acb.begin(), acb.end()},
|
||||
});
|
||||
}
|
||||
|
||||
liverpool->SubmitAsc(vqid, acb_span);
|
||||
|
@ -2108,7 +2103,9 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[
|
|||
|
||||
WaitGpuIdle();
|
||||
|
||||
/* Suspend logic goes here */
|
||||
if (DebugState.ShouldPauseInSubmit()) {
|
||||
DebugState.PauseGuestThreads();
|
||||
}
|
||||
|
||||
if (send_init_packet) {
|
||||
if (sdk_version <= 0x1ffffffu) {
|
||||
|
@ -2128,10 +2125,10 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[
|
|||
const auto dcb_size_dw = dcb_sizes_in_bytes[cbpair] >> 2;
|
||||
const auto ccb_size_dw = ccb_size_in_bytes >> 2;
|
||||
|
||||
const auto& dcb_span = std::span<const u32>{dcb_gpu_addrs[cbpair], dcb_size_dw};
|
||||
const auto& ccb_span = std::span<const u32>{ccb, ccb_size_dw};
|
||||
const auto& dcb_span = std::span{dcb_gpu_addrs[cbpair], dcb_size_dw};
|
||||
const auto& ccb_span = std::span{ccb, ccb_size_dw};
|
||||
|
||||
if (Config::dumpPM4()) {
|
||||
if (DebugState.DumpingCurrentFrame()) {
|
||||
static auto last_frame_num = -1LL;
|
||||
static u32 seq_num{};
|
||||
if (last_frame_num == frames_submitted && cbpair == 0) {
|
||||
|
@ -2141,9 +2138,20 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[
|
|||
seq_num = 0u;
|
||||
}
|
||||
|
||||
// File name format is: <queue>_<submit num>_<buffer_in_submit>
|
||||
DumpCommandList(dcb_span, fmt::format("dcb_{}_{}", seq_num, cbpair));
|
||||
DumpCommandList(ccb_span, fmt::format("ccb_{}_{}", seq_num, cbpair));
|
||||
using DebugStateType::QueueType;
|
||||
|
||||
DebugState.PushQueueDump({
|
||||
.type = QueueType::dcb,
|
||||
.submit_num = seq_num,
|
||||
.num2 = cbpair,
|
||||
.data = {dcb_span.begin(), dcb_span.end()},
|
||||
});
|
||||
DebugState.PushQueueDump({
|
||||
.type = QueueType::ccb,
|
||||
.submit_num = seq_num,
|
||||
.num2 = cbpair,
|
||||
.data = {ccb_span.begin(), ccb_span.end()},
|
||||
});
|
||||
}
|
||||
|
||||
liverpool->SubmitGfx(dcb_span, ccb_span);
|
||||
|
@ -2166,6 +2174,7 @@ int PS4_SYSV_ABI sceGnmSubmitDone() {
|
|||
liverpool->SubmitDone();
|
||||
send_init_packet = true;
|
||||
++frames_submitted;
|
||||
DebugState.IncGnmFrameNum();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
|
@ -988,6 +989,7 @@ static void cleanup_thread(void* arg) {
|
|||
}
|
||||
Core::SetTcbBase(nullptr);
|
||||
thread->is_almost_done = true;
|
||||
DebugState.RemoveCurrentThreadFromGuestList();
|
||||
}
|
||||
|
||||
static void* run_thread(void* arg) {
|
||||
|
@ -998,6 +1000,7 @@ static void* run_thread(void* arg) {
|
|||
g_pthread_self = thread;
|
||||
pthread_cleanup_push(cleanup_thread, thread);
|
||||
thread->is_started = true;
|
||||
DebugState.AddCurrentThreadToGuestList();
|
||||
ret = linker->ExecuteGuest(thread->entry, thread->arg);
|
||||
pthread_cleanup_pop(1);
|
||||
return ret;
|
||||
|
|
|
@ -247,6 +247,17 @@ int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2,
|
|||
return SCE_OK;
|
||||
}
|
||||
|
||||
namespace Dev {
|
||||
u64& GetInitialPtc() {
|
||||
return initial_ptc;
|
||||
}
|
||||
|
||||
Common::NativeClock* GetClock() {
|
||||
return clock.get();
|
||||
}
|
||||
|
||||
} // namespace Dev
|
||||
|
||||
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
clock = std::make_unique<Common::NativeClock>();
|
||||
initial_ptc = clock->GetUptime();
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class NativeClock;
|
||||
}
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
@ -47,6 +51,12 @@ constexpr int ORBIS_CLOCK_EXT_DEBUG_NETWORK = 17;
|
|||
constexpr int ORBIS_CLOCK_EXT_AD_NETWORK = 18;
|
||||
constexpr int ORBIS_CLOCK_EXT_RAW_NETWORK = 19;
|
||||
|
||||
namespace Dev {
|
||||
u64& GetInitialPtc();
|
||||
|
||||
Common::NativeClock* GetClock();
|
||||
} // namespace Dev
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetTscFrequency();
|
||||
u64 PS4_SYSV_ABI sceKernelGetProcessTime();
|
||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <imgui.h>
|
||||
#include "common/assert.h"
|
||||
|
@ -281,10 +282,15 @@ void MsgDialogUi::Draw() {
|
|||
first_render = false;
|
||||
}
|
||||
|
||||
DialogResult Libraries::MsgDialog::ShowMsgDialog(MsgDialogState state, bool block) {
|
||||
DialogResult result{};
|
||||
Status status = Status::RUNNING;
|
||||
MsgDialogUi dialog(&state, &status, &result);
|
||||
DialogResult Libraries::MsgDialog::ShowMsgDialog(MsgDialogState p_state, bool block) {
|
||||
static DialogResult result{};
|
||||
static Status status;
|
||||
static MsgDialogUi dialog;
|
||||
static MsgDialogState state;
|
||||
dialog = MsgDialogUi{};
|
||||
status = Status::RUNNING;
|
||||
state = std::move(p_state);
|
||||
dialog = MsgDialogUi(&state, &status, &result);
|
||||
if (block) {
|
||||
while (status == Status::RUNNING) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/videoout/driver.h"
|
||||
|
@ -284,7 +285,7 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
|
|||
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
|
||||
const auto request = receive_request();
|
||||
if (!request) {
|
||||
if (!main_port.is_open) {
|
||||
if (!main_port.is_open || DebugState.IsGuestThreadsPaused()) {
|
||||
DrawBlankFrame();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "core/memory.h"
|
||||
#include "core/tls.h"
|
||||
#include "core/virtual_memory.h"
|
||||
#include "debug_state.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
@ -88,6 +89,7 @@ void Linker::Execute() {
|
|||
|
||||
// Init primary thread.
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
DebugState.AddCurrentThreadToGuestList();
|
||||
Libraries::Kernel::pthreadInitSelfMainThread();
|
||||
EnsureThreadInitialized(true);
|
||||
|
||||
|
|
|
@ -83,6 +83,12 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
|||
fmt::ptr(code_address), DisassembleInstruction(code_address));
|
||||
}
|
||||
break;
|
||||
case SIGUSR1: { // Sleep thread until signal is received
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGUSR1);
|
||||
sigwait(&sigset, &sig);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -105,6 +111,8 @@ SignalDispatch::SignalDispatch() {
|
|||
"Failed to register access violation signal handler.");
|
||||
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
|
||||
"Failed to register illegal instruction signal handler.");
|
||||
ASSERT_MSG(sigaction(SIGUSR1, &action, nullptr) == 0,
|
||||
"Failed to register sleep signal handler.");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ Emulator::Emulator() {
|
|||
LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode());
|
||||
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
|
||||
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
|
||||
LOG_INFO(Config, "GPU shouldDumpPM4: {}", Config::dumpPM4());
|
||||
LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv());
|
||||
LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId());
|
||||
LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled());
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
#define IM_COL32_GRAY(x) IM_COL32(x, x, x, 0xFF)
|
||||
|
||||
#define IMGUI_FONT_TEXT 0
|
||||
#define IMGUI_FONT_MONO 1
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
namespace Easing {
|
||||
|
@ -27,6 +30,20 @@ inline void CentralizeWindow() {
|
|||
SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f});
|
||||
}
|
||||
|
||||
inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) {
|
||||
const auto cur_pos = GetWindowPos();
|
||||
if (cur_pos.x < 0.0f || cur_pos.y < 0.0f) {
|
||||
SetWindowPos(ImMax(cur_pos, ImVec2(0.0f, 0.0f)));
|
||||
return;
|
||||
}
|
||||
const auto cur_size = GetWindowSize();
|
||||
const auto bottom_right = cur_pos + cur_size;
|
||||
if (bottom_right.x > display_size.x || bottom_right.y > display_size.y) {
|
||||
const auto max_pos = display_size - cur_size;
|
||||
SetWindowPos(ImMin(cur_pos, max_pos));
|
||||
}
|
||||
}
|
||||
|
||||
inline void KeepNavHighlight() {
|
||||
GetCurrentContext()->NavDisableHighlight = false;
|
||||
}
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/types.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "video_info.h"
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
struct FrameInfo {
|
||||
u32 num;
|
||||
float delta;
|
||||
};
|
||||
|
||||
static bool show = false;
|
||||
static bool show_advanced = false;
|
||||
|
||||
static u32 current_frame = 0;
|
||||
constexpr float TARGET_FPS = 60.0f;
|
||||
constexpr u32 FRAME_BUFFER_SIZE = 1024;
|
||||
constexpr float BAR_WIDTH_MULT = 1.4f;
|
||||
constexpr float BAR_HEIGHT_MULT = 1.25f;
|
||||
constexpr float FRAME_GRAPH_PADDING_Y = 3.0f;
|
||||
static std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list;
|
||||
static float frame_graph_height = 50.0f;
|
||||
|
||||
static void DrawSimple() {
|
||||
const auto io = GetIO();
|
||||
Text("FPS: %.1f (%.3f ms)", io.Framerate, 1000.0f / io.Framerate);
|
||||
}
|
||||
|
||||
static void DrawAdvanced() {
|
||||
const auto& ctx = *GetCurrentContext();
|
||||
const auto& io = ctx.IO;
|
||||
const auto& window = *ctx.CurrentWindow;
|
||||
auto& draw_list = *window.DrawList;
|
||||
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
|
||||
SeparatorText("Frame graph");
|
||||
const float full_width = GetContentRegionAvail().x;
|
||||
{ // Frame graph - inspired by
|
||||
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
|
||||
auto pos = GetCursorScreenPos();
|
||||
const ImVec2 size{full_width, frame_graph_height + FRAME_GRAPH_PADDING_Y * 2.0f};
|
||||
ItemSize(size);
|
||||
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
|
||||
float cur_pos_x = pos.x + full_width;
|
||||
pos.y += FRAME_GRAPH_PADDING_Y;
|
||||
const float final_pos_y = pos.y + frame_graph_height;
|
||||
|
||||
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
|
||||
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
|
||||
IM_COL32(0x33, 0x33, 0x33, 0xFF));
|
||||
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
|
||||
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
|
||||
const auto& frame_info = frame_list[(current_frame - i) % FRAME_BUFFER_SIZE];
|
||||
const float dt_factor = target_dt / frame_info.delta;
|
||||
|
||||
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
|
||||
const float height =
|
||||
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * frame_graph_height;
|
||||
|
||||
ImU32 color;
|
||||
if (dt_factor >= 0.95f) { // BLUE
|
||||
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
|
||||
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
|
||||
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
|
||||
int r = (int)(0xFF * t);
|
||||
color = IM_COL32(r, 0xFF, 0, 0xFF);
|
||||
} else { // YELLOW <> RED
|
||||
float t = dt_factor * 2.0f;
|
||||
int g = (int)(0xFF * t);
|
||||
color = IM_COL32(0xFF, g, 0, 0xFF);
|
||||
}
|
||||
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height},
|
||||
{cur_pos_x, final_pos_y}, color);
|
||||
cur_pos_x -= width;
|
||||
if (cur_pos_x < width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
draw_list.PopClipRect();
|
||||
}
|
||||
}
|
||||
|
||||
void Layers::VideoInfo::Draw() {
|
||||
const auto io = GetIO();
|
||||
|
||||
const FrameInfo frame_info{
|
||||
.num = ++current_frame,
|
||||
.delta = io.DeltaTime,
|
||||
};
|
||||
frame_list[current_frame % FRAME_BUFFER_SIZE] = frame_info;
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
||||
const bool changed_ctrl = io.KeyCtrl != show_advanced;
|
||||
show_advanced = io.KeyCtrl;
|
||||
show = changed_ctrl || !show;
|
||||
}
|
||||
|
||||
if (show) {
|
||||
if (show_advanced) {
|
||||
if (Begin("Video Debug Info", nullptr, ImGuiWindowFlags_NoDecoration)) {
|
||||
DrawAdvanced();
|
||||
}
|
||||
} else {
|
||||
if (Begin("Video Info", nullptr, ImGuiWindowFlags_NoDecoration)) {
|
||||
DrawSimple();
|
||||
}
|
||||
}
|
||||
End();
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
namespace Vulkan {
|
||||
class RendererVulkan;
|
||||
}
|
||||
namespace ImGui::Layers {
|
||||
|
||||
class VideoInfo : public Layer {
|
||||
::Vulkan::RendererVulkan* renderer{};
|
||||
|
||||
public:
|
||||
explicit VideoInfo(::Vulkan::RendererVulkan* renderer) : renderer(renderer) {}
|
||||
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
} // namespace ImGui::Layers
|
|
@ -7,6 +7,7 @@ add_executable(Dear_ImGui_FontEmbed ${CMAKE_SOURCE_DIR}/externals/dear_imgui/mis
|
|||
|
||||
set(FONT_LIST
|
||||
NotoSansJP-Regular.ttf
|
||||
ProggyVector-Regular.ttf
|
||||
)
|
||||
|
||||
set(OutputList "")
|
||||
|
|
BIN
src/imgui/renderer/fonts/ProggyVector-Regular.ttf
Normal file
BIN
src/imgui/renderer/fonts/ProggyVector-Regular.ttf
Normal file
Binary file not shown.
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/devtools/layer.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
#include "imgui_core.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
|
@ -16,6 +17,7 @@
|
|||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
|
||||
#include "imgui_fonts/notosansjp_regular.ttf.g.cpp"
|
||||
#include "imgui_fonts/proggyvector_regular.ttf.g.cpp"
|
||||
|
||||
static void CheckVkResult(const vk::Result err) {
|
||||
LOG_ERROR(ImGui, "Vulkan error {}", vk::to_string(err));
|
||||
|
@ -74,12 +76,16 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w
|
|||
ImFontConfig font_cfg{};
|
||||
font_cfg.OversampleH = 2;
|
||||
font_cfg.OversampleV = 1;
|
||||
io.Fonts->AddFontFromMemoryCompressedTTF(imgui_font_notosansjp_regular_compressed_data,
|
||||
imgui_font_notosansjp_regular_compressed_size, 16.0f,
|
||||
&font_cfg, ranges.Data);
|
||||
io.FontDefault = io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||
imgui_font_notosansjp_regular_compressed_data,
|
||||
imgui_font_notosansjp_regular_compressed_size, 16.0f, &font_cfg, ranges.Data);
|
||||
io.Fonts->AddFontFromMemoryCompressedTTF(imgui_font_proggyvector_regular_compressed_data,
|
||||
imgui_font_proggyvector_regular_compressed_size,
|
||||
16.0f);
|
||||
|
||||
StyleColorsDark();
|
||||
|
||||
::Core::Devtools::Layer::SetupSettings();
|
||||
Sdl::Init(window.GetSdlWindow());
|
||||
|
||||
const Vulkan::InitInfo vk_info{
|
||||
|
@ -166,6 +172,8 @@ void NewFrame() {
|
|||
Sdl::NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
DockSpaceOverViewport(0, GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
|
||||
|
||||
for (auto* layer : layers) {
|
||||
layer->Draw();
|
||||
}
|
||||
|
|
|
@ -174,9 +174,6 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
|
|||
|
||||
connect(ui->nullGpuCheckBox, &QCheckBox::stateChanged, this,
|
||||
[](int val) { Config::setNullGpu(val); });
|
||||
|
||||
connect(ui->dumpPM4CheckBox, &QCheckBox::stateChanged, this,
|
||||
[](int val) { Config::setDumpPM4(val); });
|
||||
}
|
||||
|
||||
// DEBUG TAB
|
||||
|
@ -215,7 +212,6 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
|
|||
ui->heightDivider->installEventFilter(this);
|
||||
ui->dumpShadersCheckBox->installEventFilter(this);
|
||||
ui->nullGpuCheckBox->installEventFilter(this);
|
||||
ui->dumpPM4CheckBox->installEventFilter(this);
|
||||
|
||||
// Debug
|
||||
ui->debugDump->installEventFilter(this);
|
||||
|
@ -238,7 +234,6 @@ void SettingsDialog::LoadValuesFromConfig() {
|
|||
ui->vblankSpinBox->setValue(Config::vblankDiv());
|
||||
ui->dumpShadersCheckBox->setChecked(Config::dumpShaders());
|
||||
ui->nullGpuCheckBox->setChecked(Config::nullGpu());
|
||||
ui->dumpPM4CheckBox->setChecked(Config::dumpPM4());
|
||||
ui->playBGMCheckBox->setChecked(Config::getPlayBGM());
|
||||
ui->BGMVolumeSlider->setValue((Config::getBGMvolume()));
|
||||
ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode());
|
||||
|
|
|
@ -664,13 +664,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dumpPM4CheckBox">
|
||||
<property name="text">
|
||||
<string>Enable PM4 Dumping</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -363,13 +363,16 @@ struct Liverpool {
|
|||
Stencil8 = 1,
|
||||
};
|
||||
|
||||
union {
|
||||
union ZInfo {
|
||||
BitField<0, 2, ZFormat> format;
|
||||
BitField<2, 2, u32> num_samples;
|
||||
BitField<13, 3, u32> tile_split;
|
||||
BitField<20, 3, u32> tile_mode_index;
|
||||
BitField<23, 4, u32> decompress_on_n_zplanes;
|
||||
BitField<27, 1, u32> allow_expclear;
|
||||
BitField<28, 1, u32> read_size;
|
||||
BitField<29, 1, u32> tile_surface_en;
|
||||
BitField<30, 1, u32> clear_disallowed;
|
||||
BitField<31, 1, u32> zrange_precision;
|
||||
} z_info;
|
||||
union {
|
||||
|
@ -472,6 +475,8 @@ struct Liverpool {
|
|||
BitField<13, 1, u32> enable_polygon_offset_para;
|
||||
BitField<16, 1, u32> enable_window_offset;
|
||||
BitField<19, 1, ProvokingVtxLast> provoking_vtx_last;
|
||||
BitField<20, 1, u32> persp_corr_dis;
|
||||
BitField<21, 1, u32> multi_prim_ib_ena;
|
||||
|
||||
PolygonMode PolyMode() const {
|
||||
return enable_polygon_mode ? polygon_mode_front.Value() : PolygonMode::Fill;
|
||||
|
@ -634,6 +639,7 @@ struct Liverpool {
|
|||
BitField<8, 1, u32> xy_transformed;
|
||||
BitField<9, 1, u32> z_transformed;
|
||||
BitField<10, 1, u32> w_transformed;
|
||||
BitField<11, 1, u32> perfcounter_ref;
|
||||
};
|
||||
|
||||
struct ClipUserData {
|
||||
|
@ -689,6 +695,7 @@ struct Liverpool {
|
|||
BitField<24, 5, BlendFactor> alpha_dst_factor;
|
||||
BitField<29, 1, u32> separate_alpha_blend;
|
||||
BitField<30, 1, u32> enable;
|
||||
BitField<31, 1, u32> disable_rop3;
|
||||
};
|
||||
|
||||
union ColorControl {
|
||||
|
@ -697,9 +704,11 @@ struct Liverpool {
|
|||
Normal = 1u,
|
||||
EliminateFastClear = 2u,
|
||||
Resolve = 3u,
|
||||
Err = 4u,
|
||||
FmaskDecompress = 5u,
|
||||
};
|
||||
|
||||
BitField<0, 1, u32> disable_dual_quad;
|
||||
BitField<3, 1, u32> degamma_enable;
|
||||
BitField<4, 3, OperationMode> mode;
|
||||
BitField<16, 8, u32> rop3;
|
||||
|
@ -737,7 +746,7 @@ struct Liverpool {
|
|||
BitField<0, 11, u32> slice_start;
|
||||
BitField<13, 11, u32> slice_max;
|
||||
} view;
|
||||
union {
|
||||
union Color0Info {
|
||||
BitField<0, 2, EndianSwap> endian;
|
||||
BitField<2, 5, DataFormat> format;
|
||||
BitField<7, 1, u32> linear_general;
|
||||
|
@ -750,10 +759,17 @@ struct Liverpool {
|
|||
BitField<17, 1, u32> simple_float;
|
||||
BitField<18, 1, RoundMode> round_mode;
|
||||
BitField<19, 1, u32> cmask_is_linear;
|
||||
BitField<20, 3, u32> blend_opt_dont_rd_dst;
|
||||
BitField<23, 3, u32> blend_opt_discard_pixel;
|
||||
BitField<26, 1, u32> fmask_compression_disable_ci;
|
||||
BitField<27, 1, u32> fmask_compress_1frag_only;
|
||||
BitField<28, 1, u32> dcc_enable;
|
||||
BitField<29, 1, u32> cmask_addr_type;
|
||||
} info;
|
||||
union {
|
||||
union Color0Attrib {
|
||||
BitField<0, 5, TilingMode> tile_mode_index;
|
||||
BitField<5, 5, u32> fmask_tile_mode_index;
|
||||
BitField<10, 2, u32> fmask_bank_height;
|
||||
BitField<12, 3, u32> num_samples_log2;
|
||||
BitField<15, 2, u32> num_fragments_log2;
|
||||
BitField<17, 1, u32> force_dst_alpha_1;
|
||||
|
@ -886,8 +902,14 @@ struct Liverpool {
|
|||
u32 raw;
|
||||
BitField<0, 1, u32> depth_clear_enable;
|
||||
BitField<1, 1, u32> stencil_clear_enable;
|
||||
BitField<2, 1, u32> depth_copy;
|
||||
BitField<3, 1, u32> stencil_copy;
|
||||
BitField<4, 1, u32> resummarize_enable;
|
||||
BitField<5, 1, u32> stencil_compress_disable;
|
||||
BitField<6, 1, u32> depth_compress_disable;
|
||||
BitField<7, 1, u32> copy_centroid;
|
||||
BitField<8, 1, u32> copy_sample;
|
||||
BitField<9, 1, u32> decompress_enable;
|
||||
};
|
||||
|
||||
union DepthView {
|
||||
|
@ -940,6 +962,22 @@ struct Liverpool {
|
|||
}
|
||||
};
|
||||
|
||||
union Eqaa {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> max_anchor_samples;
|
||||
BitField<4, 3, u32> ps_iter_samples;
|
||||
BitField<8, 3, u32> mask_export_num_samples;
|
||||
BitField<12, 3, u32> alpha_to_mask_num_samples;
|
||||
BitField<16, 1, u32> high_quality_intersections;
|
||||
BitField<17, 1, u32> incoherent_eqaa_reads;
|
||||
BitField<18, 1, u32> interpolate_comp_z;
|
||||
BitField<19, 1, u32> interpolate_src_z;
|
||||
BitField<20, 1, u32> static_anchor_associations;
|
||||
BitField<21, 1, u32> alpha_to_mask_eqaa_disable;
|
||||
BitField<24, 3, u32> overrasterization_amount;
|
||||
BitField<27, 1, u32> enable_postz_overrasterization;
|
||||
};
|
||||
|
||||
union Regs {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0x2C08);
|
||||
|
|
|
@ -36,6 +36,8 @@ union PM4Type0Header {
|
|||
};
|
||||
|
||||
union PM4Type3Header {
|
||||
static constexpr u32 TYPE = 3;
|
||||
|
||||
constexpr PM4Type3Header(PM4ItOpcode code, u32 num_words_min_one,
|
||||
PM4ShaderType stype = PM4ShaderType::ShaderGraphics,
|
||||
PM4Predicate pred = PM4Predicate::PredDisable) {
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/layer.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format format) {
|
||||
|
@ -96,7 +99,7 @@ RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool*
|
|||
draw_scheduler{instance}, present_scheduler{instance}, flip_scheduler{instance},
|
||||
swapchain{instance, window},
|
||||
rasterizer{std::make_unique<Rasterizer>(instance, draw_scheduler, liverpool)},
|
||||
texture_cache{rasterizer->GetTextureCache()}, video_info_ui{this} {
|
||||
texture_cache{rasterizer->GetTextureCache()} {
|
||||
const u32 num_images = swapchain.GetImageCount();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
|
||||
|
@ -114,11 +117,11 @@ RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool*
|
|||
|
||||
// Setup ImGui
|
||||
ImGui::Core::Initialize(instance, window, num_images, swapchain.GetSurfaceFormat().format);
|
||||
ImGui::Layer::AddLayer(&video_info_ui);
|
||||
ImGui::Layer::AddLayer(Common::Singleton<Core::Devtools::Layer>::Instance());
|
||||
}
|
||||
|
||||
RendererVulkan::~RendererVulkan() {
|
||||
ImGui::Layer::RemoveLayer(&video_info_ui);
|
||||
ImGui::Layer::RemoveLayer(Common::Singleton<Core::Devtools::Layer>::Instance());
|
||||
draw_scheduler.Finish();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
for (auto& frame : present_frames) {
|
||||
|
@ -416,6 +419,8 @@ void RendererVulkan::Present(Frame* frame) {
|
|||
std::scoped_lock fl{free_mutex};
|
||||
free_queue.push(frame);
|
||||
free_cv.notify_one();
|
||||
|
||||
DebugState.IncFlipFrameNum();
|
||||
}
|
||||
|
||||
Frame* RendererVulkan::GetRenderFrame() {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include <condition_variable>
|
||||
|
||||
#include "imgui/layer/video_info.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
|
@ -105,8 +104,6 @@ private:
|
|||
std::condition_variable_any frame_cv;
|
||||
std::optional<VideoCore::Image> splash_img;
|
||||
std::vector<VAddr> vo_buffers_addr;
|
||||
|
||||
ImGui::Layers::VideoInfo video_info_ui;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
Loading…
Reference in a new issue