Просмотр исходного кода

Merge pull request #3653 from ReinUsesLisp/nsight-aftermath

 renderer_vulkan: Integrate Nvidia Nsight Aftermath on Windows
Fernando Sahmkow 6 лет назад
Родитель
Сommit
afae40a99e

+ 16 - 3
src/video_core/CMakeLists.txt

@@ -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 - 0
src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp

@@ -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 - 0
src/video_core/renderer_vulkan/nsight_aftermath_tracker.h

@@ -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

+ 2 - 0
src/video_core/renderer_vulkan/vk_compute_pipeline.cpp

@@ -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;

+ 22 - 13
src/video_core/renderer_vulkan/vk_device.cpp

@@ -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});
-
-    if (!nv_device_diagnostic_checkpoints) {
-        return;
-    }
+    // Wait for the log to flush and for Nsight Aftermath to dump the results
+    std::this_thread::sleep_for(std::chrono::seconds{3});
+}
 
-    [[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);
         }
     }
 

+ 8 - 6
src/video_core/renderer_vulkan/vk_device.h

@@ -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

+ 2 - 0
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp

@@ -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));

+ 13 - 2
src/video_core/renderer_vulkan/vk_query_cache.cpp

@@ -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

+ 0 - 10
src/video_core/renderer_vulkan/vk_rasterizer.cpp

@@ -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(),

+ 9 - 1
src/video_core/renderer_vulkan/vk_scheduler.cpp

@@ -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() {

+ 2 - 16
src/video_core/renderer_vulkan/wrapper.cpp

@@ -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();

+ 8 - 26
src/video_core/renderer_vulkan/wrapper.h

@@ -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());
     }