Merge pull request #3653 from ReinUsesLisp/nsight-aftermath
renderer_vulkan: Integrate Nvidia Nsight Aftermath on Windows
This commit is contained in:
		
						commit
						afae40a99e
					
				@ -160,6 +160,8 @@ if (ENABLE_VULKAN)
 | 
			
		||||
        renderer_vulkan/fixed_pipeline_state.h
 | 
			
		||||
        renderer_vulkan/maxwell_to_vk.cpp
 | 
			
		||||
        renderer_vulkan/maxwell_to_vk.h
 | 
			
		||||
        renderer_vulkan/nsight_aftermath_tracker.cpp
 | 
			
		||||
        renderer_vulkan/nsight_aftermath_tracker.h
 | 
			
		||||
        renderer_vulkan/renderer_vulkan.h
 | 
			
		||||
        renderer_vulkan/renderer_vulkan.cpp
 | 
			
		||||
        renderer_vulkan/vk_blit_screen.cpp
 | 
			
		||||
@ -213,19 +215,30 @@ if (ENABLE_VULKAN)
 | 
			
		||||
        renderer_vulkan/wrapper.cpp
 | 
			
		||||
        renderer_vulkan/wrapper.h
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
 | 
			
		||||
    target_compile_definitions(video_core PRIVATE HAS_VULKAN)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
create_target_directory_groups(video_core)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(video_core PUBLIC common core)
 | 
			
		||||
target_link_libraries(video_core PRIVATE glad)
 | 
			
		||||
 | 
			
		||||
if (ENABLE_VULKAN)
 | 
			
		||||
    target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
 | 
			
		||||
    target_compile_definitions(video_core PRIVATE HAS_VULKAN)
 | 
			
		||||
    target_link_libraries(video_core PRIVATE sirit)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (ENABLE_NSIGHT_AFTERMATH)
 | 
			
		||||
    if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
 | 
			
		||||
        message(ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided")
 | 
			
		||||
    endif()
 | 
			
		||||
    if (NOT WIN32)
 | 
			
		||||
        message(ERROR "Nsight Aftermath doesn't support non-Windows platforms")
 | 
			
		||||
    endif()
 | 
			
		||||
    target_compile_definitions(video_core PRIVATE HAS_NSIGHT_AFTERMATH)
 | 
			
		||||
    target_include_directories(video_core PRIVATE "$ENV{NSIGHT_AFTERMATH_SDK}/include")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    target_compile_options(video_core PRIVATE /we4267)
 | 
			
		||||
else()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										220
									
								
								src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,220 @@
 | 
			
		||||
// Copyright 2020 yuzu Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#ifdef HAS_NSIGHT_AFTERMATH
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#define VK_NO_PROTOTYPES
 | 
			
		||||
#include <vulkan/vulkan.h>
 | 
			
		||||
 | 
			
		||||
#include <GFSDK_Aftermath.h>
 | 
			
		||||
#include <GFSDK_Aftermath_Defines.h>
 | 
			
		||||
#include <GFSDK_Aftermath_GpuCrashDump.h>
 | 
			
		||||
#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
 | 
			
		||||
 | 
			
		||||
#include "common/common_paths.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/scope_exit.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
 | 
			
		||||
 | 
			
		||||
namespace Vulkan {
 | 
			
		||||
 | 
			
		||||
static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll";
 | 
			
		||||
 | 
			
		||||
NsightAftermathTracker::NsightAftermathTracker() = default;
 | 
			
		||||
 | 
			
		||||
NsightAftermathTracker::~NsightAftermathTracker() {
 | 
			
		||||
    if (initialized) {
 | 
			
		||||
        (void)GFSDK_Aftermath_DisableGpuCrashDumps();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NsightAftermathTracker::Initialize() {
 | 
			
		||||
    if (!dl.Open(AFTERMATH_LIB_NAME)) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash";
 | 
			
		||||
 | 
			
		||||
    (void)FileUtil::DeleteDirRecursively(dump_dir);
 | 
			
		||||
    if (!FileUtil::CreateDir(dump_dir)) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir);
 | 
			
		||||
 | 
			
		||||
    initialized = true;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
 | 
			
		||||
    if (!initialized) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<u32> spirv_copy = spirv;
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
 | 
			
		||||
    if (!file.IsOpen()) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (file.WriteArray(spirv.data(), spirv.size()) != spirv.size()) {
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::string base_name = [this] {
 | 
			
		||||
        const int id = dump_id++;
 | 
			
		||||
        if (id == 0) {
 | 
			
		||||
            return fmt::format("{}/crash.nv-gpudmp", dump_dir);
 | 
			
		||||
        } else {
 | 
			
		||||
            return fmt::format("{}/crash_{}.nv-gpudmp", dump_dir, id);
 | 
			
		||||
        }
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
 | 
			
		||||
    if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to write dump file");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const std::string_view json_view(json.data(), json.size());
 | 
			
		||||
    if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::string path =
 | 
			
		||||
        fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
 | 
			
		||||
    FileUtil::IOFile file(path, "wb");
 | 
			
		||||
    if (!file.IsOpen()) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (file.WriteBytes(static_cast<const u8*>(shader_debug_info), shader_debug_info_size) !=
 | 
			
		||||
        shader_debug_info_size) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to write file {}", path);
 | 
			
		||||
        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
 | 
			
		||||
							
								
								
									
										87
									
								
								src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
			
		||||
// Copyright 2020 yuzu Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#define VK_NO_PROTOTYPES
 | 
			
		||||
#include <vulkan/vulkan.h>
 | 
			
		||||
 | 
			
		||||
#ifdef HAS_NSIGHT_AFTERMATH
 | 
			
		||||
#include <GFSDK_Aftermath_Defines.h>
 | 
			
		||||
#include <GFSDK_Aftermath_GpuCrashDump.h>
 | 
			
		||||
#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/dynamic_library.h"
 | 
			
		||||
 | 
			
		||||
namespace Vulkan {
 | 
			
		||||
 | 
			
		||||
class NsightAftermathTracker {
 | 
			
		||||
public:
 | 
			
		||||
    NsightAftermathTracker();
 | 
			
		||||
    ~NsightAftermathTracker();
 | 
			
		||||
 | 
			
		||||
    NsightAftermathTracker(const NsightAftermathTracker&) = delete;
 | 
			
		||||
    NsightAftermathTracker& operator=(const NsightAftermathTracker&) = delete;
 | 
			
		||||
 | 
			
		||||
    // Delete move semantics because Aftermath initialization uses a pointer to this.
 | 
			
		||||
    NsightAftermathTracker(NsightAftermathTracker&&) = delete;
 | 
			
		||||
    NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete;
 | 
			
		||||
 | 
			
		||||
    bool Initialize();
 | 
			
		||||
 | 
			
		||||
    void SaveShader(const std::vector<u32>& spirv) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
#ifdef HAS_NSIGHT_AFTERMATH
 | 
			
		||||
    static void GpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size,
 | 
			
		||||
                                     void* user_data);
 | 
			
		||||
 | 
			
		||||
    static void ShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size,
 | 
			
		||||
                                        void* user_data);
 | 
			
		||||
 | 
			
		||||
    static void CrashDumpDescriptionCallback(
 | 
			
		||||
        PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data);
 | 
			
		||||
 | 
			
		||||
    void OnGpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size);
 | 
			
		||||
 | 
			
		||||
    void OnShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size);
 | 
			
		||||
 | 
			
		||||
    void OnCrashDumpDescriptionCallback(
 | 
			
		||||
        PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description);
 | 
			
		||||
 | 
			
		||||
    mutable std::mutex mutex;
 | 
			
		||||
 | 
			
		||||
    std::string dump_dir;
 | 
			
		||||
    int dump_id = 0;
 | 
			
		||||
 | 
			
		||||
    bool initialized = false;
 | 
			
		||||
 | 
			
		||||
    Common::DynamicLibrary dl;
 | 
			
		||||
    PFN_GFSDK_Aftermath_DisableGpuCrashDumps GFSDK_Aftermath_DisableGpuCrashDumps;
 | 
			
		||||
    PFN_GFSDK_Aftermath_EnableGpuCrashDumps GFSDK_Aftermath_EnableGpuCrashDumps;
 | 
			
		||||
    PFN_GFSDK_Aftermath_GetShaderDebugInfoIdentifier GFSDK_Aftermath_GetShaderDebugInfoIdentifier;
 | 
			
		||||
    PFN_GFSDK_Aftermath_GetShaderHashSpirv GFSDK_Aftermath_GetShaderHashSpirv;
 | 
			
		||||
    PFN_GFSDK_Aftermath_GpuCrashDump_CreateDecoder GFSDK_Aftermath_GpuCrashDump_CreateDecoder;
 | 
			
		||||
    PFN_GFSDK_Aftermath_GpuCrashDump_DestroyDecoder GFSDK_Aftermath_GpuCrashDump_DestroyDecoder;
 | 
			
		||||
    PFN_GFSDK_Aftermath_GpuCrashDump_GenerateJSON GFSDK_Aftermath_GpuCrashDump_GenerateJSON;
 | 
			
		||||
    PFN_GFSDK_Aftermath_GpuCrashDump_GetJSON GFSDK_Aftermath_GpuCrashDump_GetJSON;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef HAS_NSIGHT_AFTERMATH
 | 
			
		||||
inline NsightAftermathTracker::NsightAftermathTracker() = default;
 | 
			
		||||
inline NsightAftermathTracker::~NsightAftermathTracker() = default;
 | 
			
		||||
inline bool NsightAftermathTracker::Initialize() {
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace Vulkan
 | 
			
		||||
@ -105,6 +105,8 @@ vk::DescriptorUpdateTemplateKHR VKComputePipeline::CreateDescriptorUpdateTemplat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const {
 | 
			
		||||
    device.SaveShader(code);
 | 
			
		||||
 | 
			
		||||
    VkShaderModuleCreateInfo ci;
 | 
			
		||||
    ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
 | 
			
		||||
    ci.pNext = nullptr;
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
@ -167,6 +168,7 @@ bool VKDevice::Create() {
 | 
			
		||||
    VkPhysicalDeviceFeatures2 features2;
 | 
			
		||||
    features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
 | 
			
		||||
    features2.pNext = nullptr;
 | 
			
		||||
    const void* first_next = &features2;
 | 
			
		||||
    void** next = &features2.pNext;
 | 
			
		||||
 | 
			
		||||
    auto& features = features2.features;
 | 
			
		||||
@ -296,7 +298,19 @@ bool VKDevice::Create() {
 | 
			
		||||
        LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logical = vk::Device::Create(physical, queue_cis, extensions, features2, dld);
 | 
			
		||||
    VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
 | 
			
		||||
    if (nv_device_diagnostics_config) {
 | 
			
		||||
        nsight_aftermath_tracker.Initialize();
 | 
			
		||||
 | 
			
		||||
        diagnostics_nv.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
 | 
			
		||||
        diagnostics_nv.pNext = &features2;
 | 
			
		||||
        diagnostics_nv.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
 | 
			
		||||
                               VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
 | 
			
		||||
                               VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
 | 
			
		||||
        first_next = &diagnostics_nv;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
 | 
			
		||||
    if (!logical) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to create logical device");
 | 
			
		||||
        return false;
 | 
			
		||||
@ -344,17 +358,12 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla
 | 
			
		||||
void VKDevice::ReportLoss() const {
 | 
			
		||||
    LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
 | 
			
		||||
 | 
			
		||||
    // Wait some time to let the log flush
 | 
			
		||||
    std::this_thread::sleep_for(std::chrono::seconds{1});
 | 
			
		||||
    // Wait for the log to flush and for Nsight Aftermath to dump the results
 | 
			
		||||
    std::this_thread::sleep_for(std::chrono::seconds{3});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    if (!nv_device_diagnostic_checkpoints) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [[maybe_unused]] const std::vector data = graphics_queue.GetCheckpointDataNV(dld);
 | 
			
		||||
    // Catch here in debug builds (or with optimizations disabled) the last graphics pipeline to be
 | 
			
		||||
    // executed. It can be done on a debugger by evaluating the expression:
 | 
			
		||||
    // *(VKGraphicsPipeline*)data[0]
 | 
			
		||||
void VKDevice::SaveShader(const std::vector<u32>& spirv) const {
 | 
			
		||||
    nsight_aftermath_tracker.SaveShader(spirv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const {
 | 
			
		||||
@ -527,8 +536,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
 | 
			
		||||
        Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,
 | 
			
		||||
             false);
 | 
			
		||||
        if (Settings::values.renderer_debug) {
 | 
			
		||||
            Test(extension, nv_device_diagnostic_checkpoints,
 | 
			
		||||
                 VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
 | 
			
		||||
            Test(extension, nv_device_diagnostics_config,
 | 
			
		||||
                 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/wrapper.h"
 | 
			
		||||
 | 
			
		||||
namespace Vulkan {
 | 
			
		||||
@ -43,6 +44,9 @@ public:
 | 
			
		||||
    /// Reports a device loss.
 | 
			
		||||
    void ReportLoss() const;
 | 
			
		||||
 | 
			
		||||
    /// Reports a shader to Nsight Aftermath.
 | 
			
		||||
    void SaveShader(const std::vector<u32>& spirv) const;
 | 
			
		||||
 | 
			
		||||
    /// Returns the dispatch loader with direct function pointers of the device.
 | 
			
		||||
    const vk::DeviceDispatch& GetDispatchLoader() const {
 | 
			
		||||
        return dld;
 | 
			
		||||
@ -173,11 +177,6 @@ public:
 | 
			
		||||
        return ext_transform_feedback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the device supports VK_NV_device_diagnostic_checkpoints.
 | 
			
		||||
    bool IsNvDeviceDiagnosticCheckpoints() const {
 | 
			
		||||
        return nv_device_diagnostic_checkpoints;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the vendor name reported from Vulkan.
 | 
			
		||||
    std::string_view GetVendorName() const {
 | 
			
		||||
        return vendor_name;
 | 
			
		||||
@ -233,7 +232,7 @@ private:
 | 
			
		||||
    bool ext_depth_range_unrestricted{};       ///< Support for VK_EXT_depth_range_unrestricted.
 | 
			
		||||
    bool ext_shader_viewport_index_layer{};    ///< Support for VK_EXT_shader_viewport_index_layer.
 | 
			
		||||
    bool ext_transform_feedback{};             ///< Support for VK_EXT_transform_feedback.
 | 
			
		||||
    bool nv_device_diagnostic_checkpoints{};   ///< Support for VK_NV_device_diagnostic_checkpoints.
 | 
			
		||||
    bool nv_device_diagnostics_config{};       ///< Support for VK_NV_device_diagnostics_config.
 | 
			
		||||
 | 
			
		||||
    // Telemetry parameters
 | 
			
		||||
    std::string vendor_name;                      ///< Device's driver name.
 | 
			
		||||
@ -241,6 +240,9 @@ private:
 | 
			
		||||
 | 
			
		||||
    /// Format properties dictionary.
 | 
			
		||||
    std::unordered_map<VkFormat, VkFormatProperties> format_properties;
 | 
			
		||||
 | 
			
		||||
    /// Nsight Aftermath GPU crash tracker
 | 
			
		||||
    NsightAftermathTracker nsight_aftermath_tracker;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Vulkan
 | 
			
		||||
 | 
			
		||||
@ -148,6 +148,8 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        device.SaveShader(stage->code);
 | 
			
		||||
 | 
			
		||||
        ci.codeSize = stage->code.size() * sizeof(u32);
 | 
			
		||||
        ci.pCode = stage->code.data();
 | 
			
		||||
        modules.push_back(device.GetLogical().CreateShaderModule(ci));
 | 
			
		||||
 | 
			
		||||
@ -113,8 +113,19 @@ u64 HostCounter::BlockingQuery() const {
 | 
			
		||||
    if (ticks >= cache.Scheduler().Ticks()) {
 | 
			
		||||
        cache.Scheduler().Flush();
 | 
			
		||||
    }
 | 
			
		||||
    return cache.Device().GetLogical().GetQueryResult<u64>(
 | 
			
		||||
        query.first, query.second, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
 | 
			
		||||
    u64 data;
 | 
			
		||||
    const VkResult result = cache.Device().GetLogical().GetQueryResults(
 | 
			
		||||
        query.first, query.second, 1, sizeof(data), &data, sizeof(data),
 | 
			
		||||
        VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
 | 
			
		||||
    switch (result) {
 | 
			
		||||
    case VK_SUCCESS:
 | 
			
		||||
        return data;
 | 
			
		||||
    case VK_ERROR_DEVICE_LOST:
 | 
			
		||||
        cache.Device().ReportLoss();
 | 
			
		||||
        [[fallthrough]];
 | 
			
		||||
    default:
 | 
			
		||||
        throw vk::Exception(result);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Vulkan
 | 
			
		||||
 | 
			
		||||
@ -347,11 +347,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
 | 
			
		||||
 | 
			
		||||
    buffer_bindings.Bind(scheduler);
 | 
			
		||||
 | 
			
		||||
    if (device.IsNvDeviceDiagnosticCheckpoints()) {
 | 
			
		||||
        scheduler.Record(
 | 
			
		||||
            [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(&pipeline); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BeginTransformFeedback();
 | 
			
		||||
 | 
			
		||||
    const auto pipeline_layout = pipeline.GetLayout();
 | 
			
		||||
@ -478,11 +473,6 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
 | 
			
		||||
    TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
 | 
			
		||||
                     VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
 | 
			
		||||
 | 
			
		||||
    if (device.IsNvDeviceDiagnosticCheckpoints()) {
 | 
			
		||||
        scheduler.Record(
 | 
			
		||||
            [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(nullptr); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y,
 | 
			
		||||
                      grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(),
 | 
			
		||||
                      layout = pipeline.GetLayout(),
 | 
			
		||||
 | 
			
		||||
@ -166,7 +166,15 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
 | 
			
		||||
    submit_info.pCommandBuffers = current_cmdbuf.address();
 | 
			
		||||
    submit_info.signalSemaphoreCount = semaphore ? 1 : 0;
 | 
			
		||||
    submit_info.pSignalSemaphores = &semaphore;
 | 
			
		||||
    device.GetGraphicsQueue().Submit(submit_info, *current_fence);
 | 
			
		||||
    switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) {
 | 
			
		||||
    case VK_SUCCESS:
 | 
			
		||||
        break;
 | 
			
		||||
    case VK_ERROR_DEVICE_LOST:
 | 
			
		||||
        device.ReportLoss();
 | 
			
		||||
        [[fallthrough]];
 | 
			
		||||
    default:
 | 
			
		||||
        vk::Check(result);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VKScheduler::AllocateNewContext() {
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
 | 
			
		||||
    X(vkCmdPipelineBarrier);
 | 
			
		||||
    X(vkCmdPushConstants);
 | 
			
		||||
    X(vkCmdSetBlendConstants);
 | 
			
		||||
    X(vkCmdSetCheckpointNV);
 | 
			
		||||
    X(vkCmdSetDepthBias);
 | 
			
		||||
    X(vkCmdSetDepthBounds);
 | 
			
		||||
    X(vkCmdSetScissor);
 | 
			
		||||
@ -116,7 +115,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
 | 
			
		||||
    X(vkGetFenceStatus);
 | 
			
		||||
    X(vkGetImageMemoryRequirements);
 | 
			
		||||
    X(vkGetQueryPoolResults);
 | 
			
		||||
    X(vkGetQueueCheckpointDataNV);
 | 
			
		||||
    X(vkMapMemory);
 | 
			
		||||
    X(vkQueueSubmit);
 | 
			
		||||
    X(vkResetFences);
 | 
			
		||||
@ -409,17 +407,6 @@ DebugCallback Instance::TryCreateDebugCallback(
 | 
			
		||||
    return DebugCallback(messenger, handle, *dld);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const {
 | 
			
		||||
    if (!dld.vkGetQueueCheckpointDataNV) {
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
    u32 num;
 | 
			
		||||
    dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr);
 | 
			
		||||
    std::vector<VkCheckpointDataNV> checkpoints(num);
 | 
			
		||||
    dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data());
 | 
			
		||||
    return checkpoints;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
 | 
			
		||||
    Check(dld->vkBindBufferMemory(owner, handle, memory, offset));
 | 
			
		||||
}
 | 
			
		||||
@ -469,12 +456,11 @@ std::vector<VkImage> SwapchainKHR::GetImages() const {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
 | 
			
		||||
                      Span<const char*> enabled_extensions,
 | 
			
		||||
                      const VkPhysicalDeviceFeatures2& enabled_features,
 | 
			
		||||
                      Span<const char*> enabled_extensions, const void* next,
 | 
			
		||||
                      DeviceDispatch& dld) noexcept {
 | 
			
		||||
    VkDeviceCreateInfo ci;
 | 
			
		||||
    ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
 | 
			
		||||
    ci.pNext = &enabled_features;
 | 
			
		||||
    ci.pNext = next;
 | 
			
		||||
    ci.flags = 0;
 | 
			
		||||
    ci.queueCreateInfoCount = queues_ci.size();
 | 
			
		||||
    ci.pQueueCreateInfos = queues_ci.data();
 | 
			
		||||
 | 
			
		||||
@ -197,7 +197,6 @@ struct DeviceDispatch : public InstanceDispatch {
 | 
			
		||||
    PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
 | 
			
		||||
    PFN_vkCmdPushConstants vkCmdPushConstants;
 | 
			
		||||
    PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
 | 
			
		||||
    PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV;
 | 
			
		||||
    PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
 | 
			
		||||
    PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
 | 
			
		||||
    PFN_vkCmdSetScissor vkCmdSetScissor;
 | 
			
		||||
@ -252,7 +251,6 @@ struct DeviceDispatch : public InstanceDispatch {
 | 
			
		||||
    PFN_vkGetFenceStatus vkGetFenceStatus;
 | 
			
		||||
    PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
 | 
			
		||||
    PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
 | 
			
		||||
    PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV;
 | 
			
		||||
    PFN_vkMapMemory vkMapMemory;
 | 
			
		||||
    PFN_vkQueueSubmit vkQueueSubmit;
 | 
			
		||||
    PFN_vkResetFences vkResetFences;
 | 
			
		||||
@ -567,12 +565,8 @@ public:
 | 
			
		||||
    /// Construct a queue handle.
 | 
			
		||||
    constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
 | 
			
		||||
 | 
			
		||||
    /// Returns the checkpoint data.
 | 
			
		||||
    /// @note Returns an empty vector when the function pointer is not present.
 | 
			
		||||
    std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const;
 | 
			
		||||
 | 
			
		||||
    void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const {
 | 
			
		||||
        Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence));
 | 
			
		||||
    VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept {
 | 
			
		||||
        return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
 | 
			
		||||
@ -659,8 +653,7 @@ class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
 | 
			
		||||
                         Span<const char*> enabled_extensions,
 | 
			
		||||
                         const VkPhysicalDeviceFeatures2& enabled_features,
 | 
			
		||||
                         Span<const char*> enabled_extensions, const void* next,
 | 
			
		||||
                         DeviceDispatch& dld) noexcept;
 | 
			
		||||
 | 
			
		||||
    Queue GetQueue(u32 family_index) const noexcept;
 | 
			
		||||
@ -734,18 +727,11 @@ public:
 | 
			
		||||
        dld->vkResetQueryPoolEXT(handle, query_pool, first, count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
 | 
			
		||||
                         void* data, VkDeviceSize stride, VkQueryResultFlags flags) const {
 | 
			
		||||
        Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
 | 
			
		||||
                                         flags));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const {
 | 
			
		||||
        static_assert(std::is_trivially_copyable_v<T>);
 | 
			
		||||
        T value;
 | 
			
		||||
        GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags);
 | 
			
		||||
        return value;
 | 
			
		||||
    VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
 | 
			
		||||
                             void* data, VkDeviceSize stride, VkQueryResultFlags flags) const
 | 
			
		||||
        noexcept {
 | 
			
		||||
        return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
 | 
			
		||||
                                          flags);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -920,10 +906,6 @@ public:
 | 
			
		||||
        dld->vkCmdPushConstants(handle, layout, flags, offset, size, values);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetCheckpointNV(const void* checkpoint_marker) const noexcept {
 | 
			
		||||
        dld->vkCmdSetCheckpointNV(handle, checkpoint_marker);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept {
 | 
			
		||||
        dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user