2022-04-23 10:59:50 +02:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2020-03-30 10:21:59 +02:00
|
|
|
|
|
|
|
#ifdef HAS_NSIGHT_AFTERMATH
|
|
|
|
|
|
|
|
#include <mutex>
|
2021-05-26 01:32:56 +02:00
|
|
|
#include <span>
|
2020-03-30 10:21:59 +02:00
|
|
|
#include <string>
|
|
|
|
#include <string_view>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
|
|
|
|
#include "common/common_types.h"
|
2021-05-26 01:32:56 +02:00
|
|
|
#include "common/fs/file.h"
|
|
|
|
#include "common/fs/fs.h"
|
|
|
|
#include "common/fs/path_util.h"
|
2020-03-30 10:21:59 +02:00
|
|
|
#include "common/logging/log.h"
|
|
|
|
#include "common/scope_exit.h"
|
2021-01-23 08:13:37 +01:00
|
|
|
#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
|
2020-03-30 10:21:59 +02:00
|
|
|
|
|
|
|
namespace Vulkan {
|
|
|
|
|
|
|
|
static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll";
|
|
|
|
|
2020-12-26 05:26:52 +01:00
|
|
|
NsightAftermathTracker::NsightAftermathTracker() {
|
2020-03-30 10:21:59 +02:00
|
|
|
if (!dl.Open(AFTERMATH_LIB_NAME)) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL");
|
2020-12-26 05:26:52 +01:00
|
|
|
return;
|
2020-03-30 10:21:59 +02:00
|
|
|
}
|
|
|
|
if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps",
|
|
|
|
&GFSDK_Aftermath_DisableGpuCrashDumps) ||
|
|
|
|
!dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps",
|
|
|
|
&GFSDK_Aftermath_EnableGpuCrashDumps) ||
|
|
|
|
!dl.GetSymbol("GFSDK_Aftermath_GetShaderDebugInfoIdentifier",
|
|
|
|
&GFSDK_Aftermath_GetShaderDebugInfoIdentifier) ||
|
|
|
|
!dl.GetSymbol("GFSDK_Aftermath_GetShaderHashSpirv", &GFSDK_Aftermath_GetShaderHashSpirv) ||
|
|
|
|
!dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_CreateDecoder",
|
|
|
|
&GFSDK_Aftermath_GpuCrashDump_CreateDecoder) ||
|
|
|
|
!dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_DestroyDecoder",
|
|
|
|
&GFSDK_Aftermath_GpuCrashDump_DestroyDecoder) ||
|
|
|
|
!dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GenerateJSON",
|
|
|
|
&GFSDK_Aftermath_GpuCrashDump_GenerateJSON) ||
|
|
|
|
!dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON",
|
|
|
|
&GFSDK_Aftermath_GpuCrashDump_GetJSON)) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers");
|
2021-01-23 08:13:37 +01:00
|
|
|
return;
|
2020-03-30 10:21:59 +02:00
|
|
|
}
|
2021-05-26 01:32:56 +02:00
|
|
|
dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LogDir) / "gpucrash";
|
2020-03-30 10:21:59 +02:00
|
|
|
|
2021-06-19 09:43:16 +02:00
|
|
|
Common::FS::RemoveDirRecursively(dump_dir);
|
2020-08-15 14:33:16 +02:00
|
|
|
if (!Common::FS::CreateDir(dump_dir)) {
|
2020-03-30 10:21:59 +02:00
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
|
2020-12-26 05:26:52 +01:00
|
|
|
return;
|
2020-03-30 10:21:59 +02:00
|
|
|
}
|
|
|
|
if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
|
|
|
|
GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
|
|
|
|
GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
|
|
|
|
ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
|
2020-12-26 05:26:52 +01:00
|
|
|
return;
|
2020-03-30 10:21:59 +02:00
|
|
|
}
|
2021-05-26 01:32:56 +02:00
|
|
|
LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"",
|
|
|
|
Common::FS::PathToUTF8String(dump_dir));
|
2020-03-30 10:21:59 +02:00
|
|
|
initialized = true;
|
2020-12-26 05:26:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
NsightAftermathTracker::~NsightAftermathTracker() {
|
|
|
|
if (initialized) {
|
|
|
|
(void)GFSDK_Aftermath_DisableGpuCrashDumps();
|
|
|
|
}
|
2020-03-30 10:21:59 +02:00
|
|
|
}
|
|
|
|
|
2021-04-11 07:50:30 +02:00
|
|
|
void NsightAftermathTracker::SaveShader(std::span<const u32> spirv) const {
|
2020-03-30 10:21:59 +02:00
|
|
|
if (!initialized) {
|
|
|
|
return;
|
|
|
|
}
|
2021-04-11 07:50:30 +02:00
|
|
|
std::vector<u32> spirv_copy(spirv.begin(), spirv.end());
|
2020-03-30 10:21:59 +02:00
|
|
|
GFSDK_Aftermath_SpirvCode shader;
|
|
|
|
shader.pData = spirv_copy.data();
|
|
|
|
shader.size = static_cast<u32>(spirv_copy.size() * 4);
|
|
|
|
|
|
|
|
std::scoped_lock lock{mutex};
|
|
|
|
|
|
|
|
GFSDK_Aftermath_ShaderHash hash;
|
|
|
|
if (!GFSDK_Aftermath_SUCCEED(
|
|
|
|
GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-26 01:32:56 +02:00
|
|
|
const auto shader_file = dump_dir / fmt::format("source_{:016x}.spv", hash.hash);
|
|
|
|
|
|
|
|
Common::FS::IOFile file{shader_file, Common::FS::FileAccessMode::Write,
|
|
|
|
Common::FS::FileType::BinaryFile};
|
2020-03-30 10:21:59 +02:00
|
|
|
if (!file.IsOpen()) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
|
|
|
|
return;
|
|
|
|
}
|
2021-06-02 09:28:59 +02:00
|
|
|
if (file.WriteSpan(spirv) != spirv.size()) {
|
2020-03-30 10:21:59 +02:00
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
|
|
|
|
u32 gpu_crash_dump_size) {
|
|
|
|
std::scoped_lock lock{mutex};
|
|
|
|
|
|
|
|
LOG_CRITICAL(Render_Vulkan, "called");
|
|
|
|
|
|
|
|
GFSDK_Aftermath_GpuCrashDump_Decoder decoder;
|
|
|
|
if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_CreateDecoder(
|
|
|
|
GFSDK_Aftermath_Version_API, gpu_crash_dump, gpu_crash_dump_size, &decoder))) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to create decoder");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); });
|
|
|
|
|
|
|
|
u32 json_size = 0;
|
|
|
|
if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
|
|
|
|
decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
|
|
|
|
GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr,
|
|
|
|
this, &json_size))) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::vector<char> json(json_size);
|
|
|
|
if (!GFSDK_Aftermath_SUCCEED(
|
|
|
|
GFSDK_Aftermath_GpuCrashDump_GetJSON(decoder, json_size, json.data()))) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to query JSON");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-26 01:32:56 +02:00
|
|
|
std::filesystem::path base_name = [this] {
|
2020-03-30 10:21:59 +02:00
|
|
|
const int id = dump_id++;
|
|
|
|
if (id == 0) {
|
2021-05-26 01:32:56 +02:00
|
|
|
return dump_dir / "crash.nv-gpudmp";
|
2020-03-30 10:21:59 +02:00
|
|
|
} else {
|
2021-05-26 01:32:56 +02:00
|
|
|
return dump_dir / fmt::format("crash_{}.nv-gpudmp", id);
|
2020-03-30 10:21:59 +02:00
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
|
2021-05-26 01:32:56 +02:00
|
|
|
if (Common::FS::WriteStringToFile(base_name, Common::FS::FileType::BinaryFile, dump_view) !=
|
|
|
|
gpu_crash_dump_size) {
|
2020-03-30 10:21:59 +02:00
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to write dump file");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const std::string_view json_view(json.data(), json.size());
|
2021-05-26 01:32:56 +02:00
|
|
|
if (Common::FS::WriteStringToFile(base_name.concat(".json"), Common::FS::FileType::TextFile,
|
|
|
|
json_view) != json.size()) {
|
2020-03-30 10:21:59 +02:00
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to write JSON");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_info,
|
|
|
|
u32 shader_debug_info_size) {
|
|
|
|
std::scoped_lock lock{mutex};
|
|
|
|
|
|
|
|
GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier;
|
|
|
|
if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GetShaderDebugInfoIdentifier(
|
|
|
|
GFSDK_Aftermath_Version_API, shader_debug_info, shader_debug_info_size, &identifier))) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_GetShaderDebugInfoIdentifier failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-26 01:32:56 +02:00
|
|
|
const auto path =
|
|
|
|
dump_dir / fmt::format("shader_{:016x}{:016x}.nvdbg", identifier.id[0], identifier.id[1]);
|
|
|
|
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Write,
|
|
|
|
Common::FS::FileType::BinaryFile};
|
2020-03-30 10:21:59 +02:00
|
|
|
if (!file.IsOpen()) {
|
2021-05-26 01:32:56 +02:00
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to create file {}", Common::FS::PathToUTF8String(path));
|
2020-03-30 10:21:59 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-05-26 01:32:56 +02:00
|
|
|
if (file.WriteSpan(std::span(static_cast<const u8*>(shader_debug_info),
|
|
|
|
shader_debug_info_size)) != shader_debug_info_size) {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Failed to write file {}", Common::FS::PathToUTF8String(path));
|
2020-03-30 10:21:59 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NsightAftermathTracker::OnCrashDumpDescriptionCallback(
|
|
|
|
PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description) {
|
|
|
|
add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "yuzu");
|
|
|
|
}
|
|
|
|
|
|
|
|
void NsightAftermathTracker::GpuCrashDumpCallback(const void* gpu_crash_dump,
|
|
|
|
u32 gpu_crash_dump_size, void* user_data) {
|
|
|
|
static_cast<NsightAftermathTracker*>(user_data)->OnGpuCrashDumpCallback(gpu_crash_dump,
|
|
|
|
gpu_crash_dump_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NsightAftermathTracker::ShaderDebugInfoCallback(const void* shader_debug_info,
|
|
|
|
u32 shader_debug_info_size, void* user_data) {
|
|
|
|
static_cast<NsightAftermathTracker*>(user_data)->OnShaderDebugInfoCallback(
|
|
|
|
shader_debug_info, shader_debug_info_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NsightAftermathTracker::CrashDumpDescriptionCallback(
|
|
|
|
PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data) {
|
|
|
|
static_cast<NsightAftermathTracker*>(user_data)->OnCrashDumpDescriptionCallback(
|
|
|
|
add_description);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Vulkan
|
|
|
|
|
|
|
|
#endif // HAS_NSIGHT_AFTERMATH
|