mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-30 22:14:56 +00:00
rdoc: manual capture trigger
This commit is contained in:
parent
db90922d3d
commit
3b0bbba2ce
@ -25,6 +25,7 @@ static bool shouldDumpPM4 = false;
|
|||||||
static u32 vblankDivider = 1;
|
static u32 vblankDivider = 1;
|
||||||
static bool vkValidation = false;
|
static bool vkValidation = false;
|
||||||
static bool vkValidationSync = false;
|
static bool vkValidationSync = false;
|
||||||
|
static bool rdocEnable = false;
|
||||||
// Gui
|
// Gui
|
||||||
std::string settings_install_dir = "";
|
std::string settings_install_dir = "";
|
||||||
u32 main_window_geometry_x = 400;
|
u32 main_window_geometry_x = 400;
|
||||||
@ -95,6 +96,10 @@ bool dumpPM4() {
|
|||||||
return shouldDumpPM4;
|
return shouldDumpPM4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isRdocEnabled() {
|
||||||
|
return rdocEnable;
|
||||||
|
}
|
||||||
|
|
||||||
u32 vblankDiv() {
|
u32 vblankDiv() {
|
||||||
return vblankDivider;
|
return vblankDivider;
|
||||||
}
|
}
|
||||||
@ -238,7 +243,6 @@ void load(const std::filesystem::path& path) {
|
|||||||
|
|
||||||
screenWidth = toml::find_or<toml::integer>(gpu, "screenWidth", screenWidth);
|
screenWidth = toml::find_or<toml::integer>(gpu, "screenWidth", screenWidth);
|
||||||
screenHeight = toml::find_or<toml::integer>(gpu, "screenHeight", screenHeight);
|
screenHeight = toml::find_or<toml::integer>(gpu, "screenHeight", screenHeight);
|
||||||
gpuId = toml::find_or<toml::integer>(gpu, "gpuId", 0);
|
|
||||||
isNullGpu = toml::find_or<toml::boolean>(gpu, "nullGpu", false);
|
isNullGpu = toml::find_or<toml::boolean>(gpu, "nullGpu", false);
|
||||||
shouldDumpShaders = toml::find_or<toml::boolean>(gpu, "dumpShaders", false);
|
shouldDumpShaders = toml::find_or<toml::boolean>(gpu, "dumpShaders", false);
|
||||||
shouldDumpPM4 = toml::find_or<toml::boolean>(gpu, "dumpPM4", false);
|
shouldDumpPM4 = toml::find_or<toml::boolean>(gpu, "dumpPM4", false);
|
||||||
@ -250,8 +254,10 @@ void load(const std::filesystem::path& path) {
|
|||||||
if (vkResult.is_ok()) {
|
if (vkResult.is_ok()) {
|
||||||
auto vk = vkResult.unwrap();
|
auto vk = vkResult.unwrap();
|
||||||
|
|
||||||
|
gpuId = toml::find_or<toml::integer>(vk, "gpuId", 0);
|
||||||
vkValidation = toml::find_or<toml::boolean>(vk, "validation", true);
|
vkValidation = toml::find_or<toml::boolean>(vk, "validation", true);
|
||||||
vkValidationSync = toml::find_or<toml::boolean>(vk, "validation_sync", true);
|
vkValidationSync = toml::find_or<toml::boolean>(vk, "validation_sync", true);
|
||||||
|
rdocEnable = toml::find_or<toml::boolean>(vk, "rdocEnable", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.contains("Debug")) {
|
if (data.contains("Debug")) {
|
||||||
@ -318,15 +324,16 @@ void save(const std::filesystem::path& path) {
|
|||||||
data["General"]["logFilter"] = logFilter;
|
data["General"]["logFilter"] = logFilter;
|
||||||
data["General"]["logType"] = logType;
|
data["General"]["logType"] = logType;
|
||||||
data["General"]["showSplash"] = isShowSplash;
|
data["General"]["showSplash"] = isShowSplash;
|
||||||
data["GPU"]["gpuId"] = gpuId;
|
|
||||||
data["GPU"]["screenWidth"] = screenWidth;
|
data["GPU"]["screenWidth"] = screenWidth;
|
||||||
data["GPU"]["screenHeight"] = screenHeight;
|
data["GPU"]["screenHeight"] = screenHeight;
|
||||||
data["GPU"]["nullGpu"] = isNullGpu;
|
data["GPU"]["nullGpu"] = isNullGpu;
|
||||||
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
||||||
data["GPU"]["dumpPM4"] = shouldDumpPM4;
|
data["GPU"]["dumpPM4"] = shouldDumpPM4;
|
||||||
data["GPU"]["vblankDivider"] = vblankDivider;
|
data["GPU"]["vblankDivider"] = vblankDivider;
|
||||||
|
data["Vulkan"]["gpuId"] = gpuId;
|
||||||
data["Vulkan"]["validation"] = vkValidation;
|
data["Vulkan"]["validation"] = vkValidation;
|
||||||
data["Vulkan"]["validation_sync"] = vkValidationSync;
|
data["Vulkan"]["validation_sync"] = vkValidationSync;
|
||||||
|
data["Vulkan"]["rdocEnable"] = rdocEnable;
|
||||||
data["Debug"]["DebugDump"] = isDebugDump;
|
data["Debug"]["DebugDump"] = isDebugDump;
|
||||||
data["LLE"]["libc"] = isLibc;
|
data["LLE"]["libc"] = isLibc;
|
||||||
data["GUI"]["theme"] = mw_themes;
|
data["GUI"]["theme"] = mw_themes;
|
||||||
|
@ -26,6 +26,7 @@ bool showSplash();
|
|||||||
bool nullGpu();
|
bool nullGpu();
|
||||||
bool dumpShaders();
|
bool dumpShaders();
|
||||||
bool dumpPM4();
|
bool dumpPM4();
|
||||||
|
bool isRdocEnabled();
|
||||||
u32 vblankDiv();
|
u32 vblankDiv();
|
||||||
|
|
||||||
bool vkValidationEnabled();
|
bool vkValidationEnabled();
|
||||||
|
@ -72,6 +72,7 @@ static auto UserPaths = [] {
|
|||||||
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
||||||
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
||||||
create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR);
|
create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR);
|
||||||
|
create_path(PathType::CapturesDir, user_dir / CAPTURES_DIR);
|
||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
}();
|
}();
|
||||||
|
@ -18,6 +18,7 @@ enum class PathType {
|
|||||||
TempDataDir, // Where game temp data is stored.
|
TempDataDir, // Where game temp data is stored.
|
||||||
GameDataDir, // Where game data is stored.
|
GameDataDir, // Where game data is stored.
|
||||||
SysModuleDir, // Where system modules are stored.
|
SysModuleDir, // Where system modules are stored.
|
||||||
|
CapturesDir, // Where rdoc captures are stored.
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto PORTABLE_DIR = "user";
|
constexpr auto PORTABLE_DIR = "user";
|
||||||
@ -31,6 +32,7 @@ constexpr auto SAVEDATA_DIR = "savedata";
|
|||||||
constexpr auto GAMEDATA_DIR = "data";
|
constexpr auto GAMEDATA_DIR = "data";
|
||||||
constexpr auto TEMPDATA_DIR = "temp";
|
constexpr auto TEMPDATA_DIR = "temp";
|
||||||
constexpr auto SYSMODULES_DIR = "sys_modules";
|
constexpr auto SYSMODULES_DIR = "sys_modules";
|
||||||
|
constexpr auto CAPTURES_DIR = "captures";
|
||||||
|
|
||||||
// Filenames
|
// Filenames
|
||||||
constexpr auto LOG_FILE = "shad_log.txt";
|
constexpr auto LOG_FILE = "shad_log.txt";
|
||||||
|
@ -105,6 +105,12 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||||||
}
|
}
|
||||||
mnt->Mount(mount_temp_dir, "/temp0"); // called in app_content ==> stat/mkdir
|
mnt->Mount(mount_temp_dir, "/temp0"); // called in app_content ==> stat/mkdir
|
||||||
|
|
||||||
|
const auto& mount_captures_dir = Common::FS::GetUserPath(Common::FS::PathType::CapturesDir);
|
||||||
|
if (!std::filesystem::exists(mount_captures_dir)) {
|
||||||
|
std::filesystem::create_directory(mount_captures_dir);
|
||||||
|
}
|
||||||
|
VideoCore::SetOutputDir(mount_captures_dir.generic_string(), id);
|
||||||
|
|
||||||
// Initialize kernel and library facilities.
|
// Initialize kernel and library facilities.
|
||||||
Libraries::Kernel::init_pthreads();
|
Libraries::Kernel::init_pthreads();
|
||||||
Libraries::InitHLELibs(&linker->GetHLESymbols());
|
Libraries::InitHLELibs(&linker->GetHLESymbols());
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "core/libraries/pad/pad.h"
|
#include "core/libraries/pad/pad.h"
|
||||||
#include "input/controller.h"
|
#include "input/controller.h"
|
||||||
#include "sdl_window.h"
|
#include "sdl_window.h"
|
||||||
|
#include "video_core/renderdoc.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include <SDL3/SDL_metal.h>
|
#include <SDL3/SDL_metal.h>
|
||||||
@ -179,6 +180,11 @@ void WindowSDL::onKeyPress(const SDL_Event* event) {
|
|||||||
ax = Input::GetAxis(-0x80, 0x80, axisvalue);
|
ax = Input::GetAxis(-0x80, 0x80, axisvalue);
|
||||||
break;
|
break;
|
||||||
case SDLK_S:
|
case SDLK_S:
|
||||||
|
if (event->key.mod == SDL_KMOD_LCTRL) {
|
||||||
|
// Trigger rdoc capture
|
||||||
|
VideoCore::TriggerCapture();
|
||||||
|
break;
|
||||||
|
}
|
||||||
axis = Input::Axis::LeftY;
|
axis = Input::Axis::LeftY;
|
||||||
if (event->type == SDL_EVENT_KEY_DOWN) {
|
if (event->type == SDL_EVENT_KEY_DOWN) {
|
||||||
axisvalue += 127;
|
axisvalue += 127;
|
||||||
|
@ -41,6 +41,8 @@ void Liverpool::Process(std::stop_token stoken) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VideoCore::StartCapture();
|
||||||
|
|
||||||
int qid = -1;
|
int qid = -1;
|
||||||
|
|
||||||
while (num_submits) {
|
while (num_submits) {
|
||||||
@ -71,6 +73,8 @@ void Liverpool::Process(std::stop_token stoken) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (submit_done) {
|
if (submit_done) {
|
||||||
|
VideoCore::EndCapture();
|
||||||
|
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
rasterizer->Flush();
|
rasterizer->Flush();
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,59 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <renderdoc_app.h>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/config.h"
|
||||||
#include "video_core/renderdoc.h"
|
#include "video_core/renderdoc.h"
|
||||||
|
|
||||||
|
#include <renderdoc_app.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#else
|
#else
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
|
enum class CaptureState {
|
||||||
|
Idle,
|
||||||
|
Triggered,
|
||||||
|
InProgress,
|
||||||
|
};
|
||||||
|
static CaptureState capture_state{CaptureState::Idle};
|
||||||
|
|
||||||
RENDERDOC_API_1_6_0* rdoc_api{};
|
RENDERDOC_API_1_6_0* rdoc_api{};
|
||||||
|
|
||||||
void LoadRenderDoc() {
|
void LoadRenderDoc() {
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) {
|
|
||||||
|
// Check if we are running by RDoc GUI
|
||||||
|
HMODULE mod = GetModuleHandleA("renderdoc.dll");
|
||||||
|
if (!mod && Config::isRdocEnabled()) {
|
||||||
|
// If enabled in config, try to load RDoc runtime in offline mode
|
||||||
|
HKEY h_reg_key;
|
||||||
|
LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||||||
|
L"SOFTWARE\\Classes\\RenderDoc.RDCCapture.1\\DefaultIcon\\", 0,
|
||||||
|
KEY_READ, &h_reg_key);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::array<wchar_t, MAX_PATH> key_str{};
|
||||||
|
DWORD str_sz_out{key_str.size()};
|
||||||
|
result = RegQueryValueExW(h_reg_key, L"", 0, NULL, (LPBYTE)key_str.data(), &str_sz_out);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path path{key_str.cbegin(), key_str.cend()};
|
||||||
|
path = path.parent_path().append("renderdoc.dll");
|
||||||
|
const auto path_to_lib = path.generic_string();
|
||||||
|
mod = LoadLibraryA(path_to_lib.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mod) {
|
||||||
const auto RENDERDOC_GetAPI =
|
const auto RENDERDOC_GetAPI =
|
||||||
reinterpret_cast<pRENDERDOC_GetAPI>(GetProcAddress(mod, "RENDERDOC_GetAPI"));
|
reinterpret_cast<pRENDERDOC_GetAPI>(GetProcAddress(mod, "RENDERDOC_GetAPI"));
|
||||||
const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api);
|
const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api);
|
||||||
@ -36,18 +72,49 @@ void LoadRenderDoc() {
|
|||||||
ASSERT(ret == 1);
|
ASSERT(ret == 1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (rdoc_api) {
|
||||||
|
// Disable default capture keys as they suppose to trigger present-to-present capturing
|
||||||
|
// and it is not what we want
|
||||||
|
rdoc_api->SetCaptureKeys(nullptr, 0);
|
||||||
|
|
||||||
|
// Also remove rdoc crash handler
|
||||||
|
rdoc_api->UnloadCrashHandler();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartCapture() {
|
void StartCapture() {
|
||||||
if (rdoc_api) {
|
if (!rdoc_api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capture_state == CaptureState::Triggered) {
|
||||||
rdoc_api->StartFrameCapture(nullptr, nullptr);
|
rdoc_api->StartFrameCapture(nullptr, nullptr);
|
||||||
|
capture_state = CaptureState::InProgress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EndCapture() {
|
void EndCapture() {
|
||||||
if (rdoc_api) {
|
if (!rdoc_api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capture_state == CaptureState::InProgress) {
|
||||||
rdoc_api->EndFrameCapture(nullptr, nullptr);
|
rdoc_api->EndFrameCapture(nullptr, nullptr);
|
||||||
|
capture_state = CaptureState::Idle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriggerCapture() {
|
||||||
|
if (capture_state == CaptureState::Idle) {
|
||||||
|
capture_state = CaptureState::Triggered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputDir(const std::string& path, const std::string& prefix) {
|
||||||
|
if (!rdoc_api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rdoc_api->SetCaptureFilePathTemplate((path + '\\' + prefix).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
/// Loads renderdoc dynamic library module.
|
/// Loads renderdoc dynamic library module.
|
||||||
@ -14,4 +16,10 @@ void StartCapture();
|
|||||||
/// Ends current renderdoc capture.
|
/// Ends current renderdoc capture.
|
||||||
void EndCapture();
|
void EndCapture();
|
||||||
|
|
||||||
|
/// Triggers capturing process.
|
||||||
|
void TriggerCapture();
|
||||||
|
|
||||||
|
/// Sets output directory for captures
|
||||||
|
void SetOutputDir(const std::string& path, const std::string& prefix);
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
Loading…
Reference in New Issue
Block a user