summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2020-04-22 11:39:01 -0400
committerGravatar GitHub2020-04-22 11:39:01 -0400
commitafae40a99eea45f06896bf35b34fb70f72981005 (patch)
treea920c746c9d5cff673b8a9b3950bd1f68f9c439d /src
parentMerge pull request #3699 from FearlessTobi/port-5185 (diff)
parentrenderer_vulkan: Integrate Nvidia Nsight Aftermath on Windows (diff)
downloadyuzu-afae40a99eea45f06896bf35b34fb70f72981005.tar.gz
yuzu-afae40a99eea45f06896bf35b34fb70f72981005.tar.xz
yuzu-afae40a99eea45f06896bf35b34fb70f72981005.zip
Merge pull request #3653 from ReinUsesLisp/nsight-aftermath
renderer_vulkan: Integrate Nvidia Nsight Aftermath on Windows
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt19
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp220
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.h87
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp35
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp10
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp18
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h34
12 files changed, 389 insertions, 77 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 258d58eba..c0e8f6ab1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -160,6 +160,8 @@ if (ENABLE_VULKAN)
160 renderer_vulkan/fixed_pipeline_state.h 160 renderer_vulkan/fixed_pipeline_state.h
161 renderer_vulkan/maxwell_to_vk.cpp 161 renderer_vulkan/maxwell_to_vk.cpp
162 renderer_vulkan/maxwell_to_vk.h 162 renderer_vulkan/maxwell_to_vk.h
163 renderer_vulkan/nsight_aftermath_tracker.cpp
164 renderer_vulkan/nsight_aftermath_tracker.h
163 renderer_vulkan/renderer_vulkan.h 165 renderer_vulkan/renderer_vulkan.h
164 renderer_vulkan/renderer_vulkan.cpp 166 renderer_vulkan/renderer_vulkan.cpp
165 renderer_vulkan/vk_blit_screen.cpp 167 renderer_vulkan/vk_blit_screen.cpp
@@ -213,19 +215,30 @@ if (ENABLE_VULKAN)
213 renderer_vulkan/wrapper.cpp 215 renderer_vulkan/wrapper.cpp
214 renderer_vulkan/wrapper.h 216 renderer_vulkan/wrapper.h
215 ) 217 )
216
217 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
218 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
219endif() 218endif()
220 219
221create_target_directory_groups(video_core) 220create_target_directory_groups(video_core)
222 221
223target_link_libraries(video_core PUBLIC common core) 222target_link_libraries(video_core PUBLIC common core)
224target_link_libraries(video_core PRIVATE glad) 223target_link_libraries(video_core PRIVATE glad)
224
225if (ENABLE_VULKAN) 225if (ENABLE_VULKAN)
226 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
227 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
226 target_link_libraries(video_core PRIVATE sirit) 228 target_link_libraries(video_core PRIVATE sirit)
227endif() 229endif()
228 230
231if (ENABLE_NSIGHT_AFTERMATH)
232 if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
233 message(ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided")
234 endif()
235 if (NOT WIN32)
236 message(ERROR "Nsight Aftermath doesn't support non-Windows platforms")
237 endif()
238 target_compile_definitions(video_core PRIVATE HAS_NSIGHT_AFTERMATH)
239 target_include_directories(video_core PRIVATE "$ENV{NSIGHT_AFTERMATH_SDK}/include")
240endif()
241
229if (MSVC) 242if (MSVC)
230 target_compile_options(video_core PRIVATE /we4267) 243 target_compile_options(video_core PRIVATE /we4267)
231else() 244else()
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
new file mode 100644
index 000000000..435c8c1b8
--- /dev/null
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
@@ -0,0 +1,220 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef HAS_NSIGHT_AFTERMATH
6
7#include <mutex>
8#include <string>
9#include <string_view>
10#include <utility>
11#include <vector>
12
13#include <fmt/format.h>
14
15#define VK_NO_PROTOTYPES
16#include <vulkan/vulkan.h>
17
18#include <GFSDK_Aftermath.h>
19#include <GFSDK_Aftermath_Defines.h>
20#include <GFSDK_Aftermath_GpuCrashDump.h>
21#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
22
23#include "common/common_paths.h"
24#include "common/common_types.h"
25#include "common/file_util.h"
26#include "common/logging/log.h"
27#include "common/scope_exit.h"
28
29#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
30
31namespace Vulkan {
32
33static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll";
34
35NsightAftermathTracker::NsightAftermathTracker() = default;
36
37NsightAftermathTracker::~NsightAftermathTracker() {
38 if (initialized) {
39 (void)GFSDK_Aftermath_DisableGpuCrashDumps();
40 }
41}
42
43bool NsightAftermathTracker::Initialize() {
44 if (!dl.Open(AFTERMATH_LIB_NAME)) {
45 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL");
46 return false;
47 }
48
49 if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps",
50 &GFSDK_Aftermath_DisableGpuCrashDumps) ||
51 !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps",
52 &GFSDK_Aftermath_EnableGpuCrashDumps) ||
53 !dl.GetSymbol("GFSDK_Aftermath_GetShaderDebugInfoIdentifier",
54 &GFSDK_Aftermath_GetShaderDebugInfoIdentifier) ||
55 !dl.GetSymbol("GFSDK_Aftermath_GetShaderHashSpirv", &GFSDK_Aftermath_GetShaderHashSpirv) ||
56 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_CreateDecoder",
57 &GFSDK_Aftermath_GpuCrashDump_CreateDecoder) ||
58 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_DestroyDecoder",
59 &GFSDK_Aftermath_GpuCrashDump_DestroyDecoder) ||
60 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GenerateJSON",
61 &GFSDK_Aftermath_GpuCrashDump_GenerateJSON) ||
62 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON",
63 &GFSDK_Aftermath_GpuCrashDump_GetJSON)) {
64 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers");
65 return false;
66 }
67
68 dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash";
69
70 (void)FileUtil::DeleteDirRecursively(dump_dir);
71 if (!FileUtil::CreateDir(dump_dir)) {
72 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
73 return false;
74 }
75
76 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
77 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
78 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
79 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) {
80 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
81 return false;
82 }
83
84 LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir);
85
86 initialized = true;
87 return true;
88}
89
90void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
91 if (!initialized) {
92 return;
93 }
94
95 std::vector<u32> spirv_copy = spirv;
96 GFSDK_Aftermath_SpirvCode shader;
97 shader.pData = spirv_copy.data();
98 shader.size = static_cast<u32>(spirv_copy.size() * 4);
99
100 std::scoped_lock lock{mutex};
101
102 GFSDK_Aftermath_ShaderHash hash;
103 if (!GFSDK_Aftermath_SUCCEED(
104 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
105 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
106 return;
107 }
108
109 FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
110 if (!file.IsOpen()) {
111 LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
112 return;
113 }
114 if (file.WriteArray(spirv.data(), spirv.size()) != spirv.size()) {
115 LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash);
116 return;
117 }
118}
119
120void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
121 u32 gpu_crash_dump_size) {
122 std::scoped_lock lock{mutex};
123
124 LOG_CRITICAL(Render_Vulkan, "called");
125
126 GFSDK_Aftermath_GpuCrashDump_Decoder decoder;
127 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_CreateDecoder(
128 GFSDK_Aftermath_Version_API, gpu_crash_dump, gpu_crash_dump_size, &decoder))) {
129 LOG_ERROR(Render_Vulkan, "Failed to create decoder");
130 return;
131 }
132 SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); });
133
134 u32 json_size = 0;
135 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
136 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
137 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr,
138 this, &json_size))) {
139 LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
140 return;
141 }
142 std::vector<char> json(json_size);
143 if (!GFSDK_Aftermath_SUCCEED(
144 GFSDK_Aftermath_GpuCrashDump_GetJSON(decoder, json_size, json.data()))) {
145 LOG_ERROR(Render_Vulkan, "Failed to query JSON");
146 return;
147 }
148
149 const std::string base_name = [this] {
150 const int id = dump_id++;
151 if (id == 0) {
152 return fmt::format("{}/crash.nv-gpudmp", dump_dir);
153 } else {
154 return fmt::format("{}/crash_{}.nv-gpudmp", dump_dir, id);
155 }
156 }();
157
158 std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
159 if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
160 LOG_ERROR(Render_Vulkan, "Failed to write dump file");
161 return;
162 }
163 const std::string_view json_view(json.data(), json.size());
164 if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
165 LOG_ERROR(Render_Vulkan, "Failed to write JSON");
166 return;
167 }
168}
169
170void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_info,
171 u32 shader_debug_info_size) {
172 std::scoped_lock lock{mutex};
173
174 GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier;
175 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GetShaderDebugInfoIdentifier(
176 GFSDK_Aftermath_Version_API, shader_debug_info, shader_debug_info_size, &identifier))) {
177 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_GetShaderDebugInfoIdentifier failed");
178 return;
179 }
180
181 const std::string path =
182 fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
183 FileUtil::IOFile file(path, "wb");
184 if (!file.IsOpen()) {
185 LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
186 return;
187 }
188 if (file.WriteBytes(static_cast<const u8*>(shader_debug_info), shader_debug_info_size) !=
189 shader_debug_info_size) {
190 LOG_ERROR(Render_Vulkan, "Failed to write file {}", path);
191 return;
192 }
193}
194
195void NsightAftermathTracker::OnCrashDumpDescriptionCallback(
196 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description) {
197 add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "yuzu");
198}
199
200void NsightAftermathTracker::GpuCrashDumpCallback(const void* gpu_crash_dump,
201 u32 gpu_crash_dump_size, void* user_data) {
202 static_cast<NsightAftermathTracker*>(user_data)->OnGpuCrashDumpCallback(gpu_crash_dump,
203 gpu_crash_dump_size);
204}
205
206void NsightAftermathTracker::ShaderDebugInfoCallback(const void* shader_debug_info,
207 u32 shader_debug_info_size, void* user_data) {
208 static_cast<NsightAftermathTracker*>(user_data)->OnShaderDebugInfoCallback(
209 shader_debug_info, shader_debug_info_size);
210}
211
212void NsightAftermathTracker::CrashDumpDescriptionCallback(
213 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data) {
214 static_cast<NsightAftermathTracker*>(user_data)->OnCrashDumpDescriptionCallback(
215 add_description);
216}
217
218} // namespace Vulkan
219
220#endif // HAS_NSIGHT_AFTERMATH
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
new file mode 100644
index 000000000..afe7ae99e
--- /dev/null
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
@@ -0,0 +1,87 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8#include <string>
9#include <vector>
10
11#define VK_NO_PROTOTYPES
12#include <vulkan/vulkan.h>
13
14#ifdef HAS_NSIGHT_AFTERMATH
15#include <GFSDK_Aftermath_Defines.h>
16#include <GFSDK_Aftermath_GpuCrashDump.h>
17#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
18#endif
19
20#include "common/common_types.h"
21#include "common/dynamic_library.h"
22
23namespace Vulkan {
24
25class NsightAftermathTracker {
26public:
27 NsightAftermathTracker();
28 ~NsightAftermathTracker();
29
30 NsightAftermathTracker(const NsightAftermathTracker&) = delete;
31 NsightAftermathTracker& operator=(const NsightAftermathTracker&) = delete;
32
33 // Delete move semantics because Aftermath initialization uses a pointer to this.
34 NsightAftermathTracker(NsightAftermathTracker&&) = delete;
35 NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete;
36
37 bool Initialize();
38
39 void SaveShader(const std::vector<u32>& spirv) const;
40
41private:
42#ifdef HAS_NSIGHT_AFTERMATH
43 static void GpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size,
44 void* user_data);
45
46 static void ShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size,
47 void* user_data);
48
49 static void CrashDumpDescriptionCallback(
50 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data);
51
52 void OnGpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size);
53
54 void OnShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size);
55
56 void OnCrashDumpDescriptionCallback(
57 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description);
58
59 mutable std::mutex mutex;
60
61 std::string dump_dir;
62 int dump_id = 0;
63
64 bool initialized = false;
65
66 Common::DynamicLibrary dl;
67 PFN_GFSDK_Aftermath_DisableGpuCrashDumps GFSDK_Aftermath_DisableGpuCrashDumps;
68 PFN_GFSDK_Aftermath_EnableGpuCrashDumps GFSDK_Aftermath_EnableGpuCrashDumps;
69 PFN_GFSDK_Aftermath_GetShaderDebugInfoIdentifier GFSDK_Aftermath_GetShaderDebugInfoIdentifier;
70 PFN_GFSDK_Aftermath_GetShaderHashSpirv GFSDK_Aftermath_GetShaderHashSpirv;
71 PFN_GFSDK_Aftermath_GpuCrashDump_CreateDecoder GFSDK_Aftermath_GpuCrashDump_CreateDecoder;
72 PFN_GFSDK_Aftermath_GpuCrashDump_DestroyDecoder GFSDK_Aftermath_GpuCrashDump_DestroyDecoder;
73 PFN_GFSDK_Aftermath_GpuCrashDump_GenerateJSON GFSDK_Aftermath_GpuCrashDump_GenerateJSON;
74 PFN_GFSDK_Aftermath_GpuCrashDump_GetJSON GFSDK_Aftermath_GpuCrashDump_GetJSON;
75#endif
76};
77
78#ifndef HAS_NSIGHT_AFTERMATH
79inline NsightAftermathTracker::NsightAftermathTracker() = default;
80inline NsightAftermathTracker::~NsightAftermathTracker() = default;
81inline bool NsightAftermathTracker::Initialize() {
82 return false;
83}
84inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {}
85#endif
86
87} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 23beafa4f..52566bb79 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -105,6 +105,8 @@ vk::DescriptorUpdateTemplateKHR VKComputePipeline::CreateDescriptorUpdateTemplat
105} 105}
106 106
107vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const { 107vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const {
108 device.SaveShader(code);
109
108 VkShaderModuleCreateInfo ci; 110 VkShaderModuleCreateInfo ci;
109 ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 111 ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
110 ci.pNext = nullptr; 112 ci.pNext = nullptr;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 52d29e49d..e90c76492 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -9,6 +9,7 @@
9#include <string_view> 9#include <string_view>
10#include <thread> 10#include <thread>
11#include <unordered_set> 11#include <unordered_set>
12#include <utility>
12#include <vector> 13#include <vector>
13 14
14#include "common/assert.h" 15#include "common/assert.h"
@@ -167,6 +168,7 @@ bool VKDevice::Create() {
167 VkPhysicalDeviceFeatures2 features2; 168 VkPhysicalDeviceFeatures2 features2;
168 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; 169 features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
169 features2.pNext = nullptr; 170 features2.pNext = nullptr;
171 const void* first_next = &features2;
170 void** next = &features2.pNext; 172 void** next = &features2.pNext;
171 173
172 auto& features = features2.features; 174 auto& features = features2.features;
@@ -296,7 +298,19 @@ bool VKDevice::Create() {
296 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 298 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
297 } 299 }
298 300
299 logical = vk::Device::Create(physical, queue_cis, extensions, features2, dld); 301 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
302 if (nv_device_diagnostics_config) {
303 nsight_aftermath_tracker.Initialize();
304
305 diagnostics_nv.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
306 diagnostics_nv.pNext = &features2;
307 diagnostics_nv.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
308 VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
309 VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
310 first_next = &diagnostics_nv;
311 }
312
313 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
300 if (!logical) { 314 if (!logical) {
301 LOG_ERROR(Render_Vulkan, "Failed to create logical device"); 315 LOG_ERROR(Render_Vulkan, "Failed to create logical device");
302 return false; 316 return false;
@@ -344,17 +358,12 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla
344void VKDevice::ReportLoss() const { 358void VKDevice::ReportLoss() const {
345 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 359 LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
346 360
347 // Wait some time to let the log flush 361 // Wait for the log to flush and for Nsight Aftermath to dump the results
348 std::this_thread::sleep_for(std::chrono::seconds{1}); 362 std::this_thread::sleep_for(std::chrono::seconds{3});
349 363}
350 if (!nv_device_diagnostic_checkpoints) {
351 return;
352 }
353 364
354 [[maybe_unused]] const std::vector data = graphics_queue.GetCheckpointDataNV(dld); 365void VKDevice::SaveShader(const std::vector<u32>& spirv) const {
355 // Catch here in debug builds (or with optimizations disabled) the last graphics pipeline to be 366 nsight_aftermath_tracker.SaveShader(spirv);
356 // executed. It can be done on a debugger by evaluating the expression:
357 // *(VKGraphicsPipeline*)data[0]
358} 367}
359 368
360bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { 369bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const {
@@ -527,8 +536,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
527 Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, 536 Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,
528 false); 537 false);
529 if (Settings::values.renderer_debug) { 538 if (Settings::values.renderer_debug) {
530 Test(extension, nv_device_diagnostic_checkpoints, 539 Test(extension, nv_device_diagnostics_config,
531 VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); 540 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
532 } 541 }
533 } 542 }
534 543
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 60d64572a..a4d841e26 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
13#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/renderer_vulkan/wrapper.h"
14 15
15namespace Vulkan { 16namespace Vulkan {
@@ -43,6 +44,9 @@ public:
43 /// Reports a device loss. 44 /// Reports a device loss.
44 void ReportLoss() const; 45 void ReportLoss() const;
45 46
47 /// Reports a shader to Nsight Aftermath.
48 void SaveShader(const std::vector<u32>& spirv) const;
49
46 /// Returns the dispatch loader with direct function pointers of the device. 50 /// Returns the dispatch loader with direct function pointers of the device.
47 const vk::DeviceDispatch& GetDispatchLoader() const { 51 const vk::DeviceDispatch& GetDispatchLoader() const {
48 return dld; 52 return dld;
@@ -173,11 +177,6 @@ public:
173 return ext_transform_feedback; 177 return ext_transform_feedback;
174 } 178 }
175 179
176 /// Returns true if the device supports VK_NV_device_diagnostic_checkpoints.
177 bool IsNvDeviceDiagnosticCheckpoints() const {
178 return nv_device_diagnostic_checkpoints;
179 }
180
181 /// Returns the vendor name reported from Vulkan. 180 /// Returns the vendor name reported from Vulkan.
182 std::string_view GetVendorName() const { 181 std::string_view GetVendorName() const {
183 return vendor_name; 182 return vendor_name;
@@ -233,7 +232,7 @@ private:
233 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 232 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
234 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 233 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
235 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 234 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
236 bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints. 235 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
237 236
238 // Telemetry parameters 237 // Telemetry parameters
239 std::string vendor_name; ///< Device's driver name. 238 std::string vendor_name; ///< Device's driver name.
@@ -241,6 +240,9 @@ private:
241 240
242 /// Format properties dictionary. 241 /// Format properties dictionary.
243 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 242 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
243
244 /// Nsight Aftermath GPU crash tracker
245 NsightAftermathTracker nsight_aftermath_tracker;
244}; 246};
245 247
246} // namespace Vulkan 248} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 343999cf5..8332b42aa 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -148,6 +148,8 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
148 continue; 148 continue;
149 } 149 }
150 150
151 device.SaveShader(stage->code);
152
151 ci.codeSize = stage->code.size() * sizeof(u32); 153 ci.codeSize = stage->code.size() * sizeof(u32);
152 ci.pCode = stage->code.data(); 154 ci.pCode = stage->code.data();
153 modules.push_back(device.GetLogical().CreateShaderModule(ci)); 155 modules.push_back(device.GetLogical().CreateShaderModule(ci));
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 0966c7ff7..813f7c162 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -113,8 +113,19 @@ u64 HostCounter::BlockingQuery() const {
113 if (ticks >= cache.Scheduler().Ticks()) { 113 if (ticks >= cache.Scheduler().Ticks()) {
114 cache.Scheduler().Flush(); 114 cache.Scheduler().Flush();
115 } 115 }
116 return cache.Device().GetLogical().GetQueryResult<u64>( 116 u64 data;
117 query.first, query.second, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); 117 const VkResult result = cache.Device().GetLogical().GetQueryResults(
118 query.first, query.second, 1, sizeof(data), &data, sizeof(data),
119 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
120 switch (result) {
121 case VK_SUCCESS:
122 return data;
123 case VK_ERROR_DEVICE_LOST:
124 cache.Device().ReportLoss();
125 [[fallthrough]];
126 default:
127 throw vk::Exception(result);
128 }
118} 129}
119 130
120} // namespace Vulkan 131} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 71007bbe8..b58a88664 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -347,11 +347,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
347 347
348 buffer_bindings.Bind(scheduler); 348 buffer_bindings.Bind(scheduler);
349 349
350 if (device.IsNvDeviceDiagnosticCheckpoints()) {
351 scheduler.Record(
352 [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(&pipeline); });
353 }
354
355 BeginTransformFeedback(); 350 BeginTransformFeedback();
356 351
357 const auto pipeline_layout = pipeline.GetLayout(); 352 const auto pipeline_layout = pipeline.GetLayout();
@@ -478,11 +473,6 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
478 TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 473 TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
479 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); 474 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
480 475
481 if (device.IsNvDeviceDiagnosticCheckpoints()) {
482 scheduler.Record(
483 [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(nullptr); });
484 }
485
486 scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y, 476 scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y,
487 grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(), 477 grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(),
488 layout = pipeline.GetLayout(), 478 layout = pipeline.GetLayout(),
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 900f551b3..ae7ba3eb5 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -166,7 +166,15 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
166 submit_info.pCommandBuffers = current_cmdbuf.address(); 166 submit_info.pCommandBuffers = current_cmdbuf.address();
167 submit_info.signalSemaphoreCount = semaphore ? 1 : 0; 167 submit_info.signalSemaphoreCount = semaphore ? 1 : 0;
168 submit_info.pSignalSemaphores = &semaphore; 168 submit_info.pSignalSemaphores = &semaphore;
169 device.GetGraphicsQueue().Submit(submit_info, *current_fence); 169 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) {
170 case VK_SUCCESS:
171 break;
172 case VK_ERROR_DEVICE_LOST:
173 device.ReportLoss();
174 [[fallthrough]];
175 default:
176 vk::Check(result);
177 }
170} 178}
171 179
172void VKScheduler::AllocateNewContext() { 180void VKScheduler::AllocateNewContext() {
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 9b94dfff1..3a52a3a6f 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -61,7 +61,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
61 X(vkCmdPipelineBarrier); 61 X(vkCmdPipelineBarrier);
62 X(vkCmdPushConstants); 62 X(vkCmdPushConstants);
63 X(vkCmdSetBlendConstants); 63 X(vkCmdSetBlendConstants);
64 X(vkCmdSetCheckpointNV);
65 X(vkCmdSetDepthBias); 64 X(vkCmdSetDepthBias);
66 X(vkCmdSetDepthBounds); 65 X(vkCmdSetDepthBounds);
67 X(vkCmdSetScissor); 66 X(vkCmdSetScissor);
@@ -116,7 +115,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
116 X(vkGetFenceStatus); 115 X(vkGetFenceStatus);
117 X(vkGetImageMemoryRequirements); 116 X(vkGetImageMemoryRequirements);
118 X(vkGetQueryPoolResults); 117 X(vkGetQueryPoolResults);
119 X(vkGetQueueCheckpointDataNV);
120 X(vkMapMemory); 118 X(vkMapMemory);
121 X(vkQueueSubmit); 119 X(vkQueueSubmit);
122 X(vkResetFences); 120 X(vkResetFences);
@@ -409,17 +407,6 @@ DebugCallback Instance::TryCreateDebugCallback(
409 return DebugCallback(messenger, handle, *dld); 407 return DebugCallback(messenger, handle, *dld);
410} 408}
411 409
412std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const {
413 if (!dld.vkGetQueueCheckpointDataNV) {
414 return {};
415 }
416 u32 num;
417 dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr);
418 std::vector<VkCheckpointDataNV> checkpoints(num);
419 dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data());
420 return checkpoints;
421}
422
423void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { 410void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
424 Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); 411 Check(dld->vkBindBufferMemory(owner, handle, memory, offset));
425} 412}
@@ -469,12 +456,11 @@ std::vector<VkImage> SwapchainKHR::GetImages() const {
469} 456}
470 457
471Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, 458Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
472 Span<const char*> enabled_extensions, 459 Span<const char*> enabled_extensions, const void* next,
473 const VkPhysicalDeviceFeatures2& enabled_features,
474 DeviceDispatch& dld) noexcept { 460 DeviceDispatch& dld) noexcept {
475 VkDeviceCreateInfo ci; 461 VkDeviceCreateInfo ci;
476 ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 462 ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
477 ci.pNext = &enabled_features; 463 ci.pNext = next;
478 ci.flags = 0; 464 ci.flags = 0;
479 ci.queueCreateInfoCount = queues_ci.size(); 465 ci.queueCreateInfoCount = queues_ci.size();
480 ci.pQueueCreateInfos = queues_ci.data(); 466 ci.pQueueCreateInfos = queues_ci.data();
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index fb3657819..6fe0294d8 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -197,7 +197,6 @@ struct DeviceDispatch : public InstanceDispatch {
197 PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; 197 PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
198 PFN_vkCmdPushConstants vkCmdPushConstants; 198 PFN_vkCmdPushConstants vkCmdPushConstants;
199 PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; 199 PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
200 PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV;
201 PFN_vkCmdSetDepthBias vkCmdSetDepthBias; 200 PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
202 PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; 201 PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
203 PFN_vkCmdSetScissor vkCmdSetScissor; 202 PFN_vkCmdSetScissor vkCmdSetScissor;
@@ -252,7 +251,6 @@ struct DeviceDispatch : public InstanceDispatch {
252 PFN_vkGetFenceStatus vkGetFenceStatus; 251 PFN_vkGetFenceStatus vkGetFenceStatus;
253 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; 252 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
254 PFN_vkGetQueryPoolResults vkGetQueryPoolResults; 253 PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
255 PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV;
256 PFN_vkMapMemory vkMapMemory; 254 PFN_vkMapMemory vkMapMemory;
257 PFN_vkQueueSubmit vkQueueSubmit; 255 PFN_vkQueueSubmit vkQueueSubmit;
258 PFN_vkResetFences vkResetFences; 256 PFN_vkResetFences vkResetFences;
@@ -567,12 +565,8 @@ public:
567 /// Construct a queue handle. 565 /// Construct a queue handle.
568 constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} 566 constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
569 567
570 /// Returns the checkpoint data. 568 VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept {
571 /// @note Returns an empty vector when the function pointer is not present. 569 return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
572 std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const;
573
574 void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const {
575 Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence));
576 } 570 }
577 571
578 VkResult Present(const VkPresentInfoKHR& present_info) const noexcept { 572 VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
@@ -659,8 +653,7 @@ class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
659 653
660public: 654public:
661 static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, 655 static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
662 Span<const char*> enabled_extensions, 656 Span<const char*> enabled_extensions, const void* next,
663 const VkPhysicalDeviceFeatures2& enabled_features,
664 DeviceDispatch& dld) noexcept; 657 DeviceDispatch& dld) noexcept;
665 658
666 Queue GetQueue(u32 family_index) const noexcept; 659 Queue GetQueue(u32 family_index) const noexcept;
@@ -734,18 +727,11 @@ public:
734 dld->vkResetQueryPoolEXT(handle, query_pool, first, count); 727 dld->vkResetQueryPoolEXT(handle, query_pool, first, count);
735 } 728 }
736 729
737 void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, 730 VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
738 void* data, VkDeviceSize stride, VkQueryResultFlags flags) const { 731 void* data, VkDeviceSize stride, VkQueryResultFlags flags) const
739 Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, 732 noexcept {
740 flags)); 733 return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
741 } 734 flags);
742
743 template <typename T>
744 T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const {
745 static_assert(std::is_trivially_copyable_v<T>);
746 T value;
747 GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags);
748 return value;
749 } 735 }
750}; 736};
751 737
@@ -920,10 +906,6 @@ public:
920 dld->vkCmdPushConstants(handle, layout, flags, offset, size, values); 906 dld->vkCmdPushConstants(handle, layout, flags, offset, size, values);
921 } 907 }
922 908
923 void SetCheckpointNV(const void* checkpoint_marker) const noexcept {
924 dld->vkCmdSetCheckpointNV(handle, checkpoint_marker);
925 }
926
927 void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept { 909 void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept {
928 dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data()); 910 dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data());
929 } 911 }