summaryrefslogtreecommitdiff
path: root/src/video_core/vulkan_common
diff options
context:
space:
mode:
authorGravatar bunnei2021-01-05 17:44:56 -0800
committerGravatar GitHub2021-01-05 17:44:56 -0800
commit275b96a0e22e40a0b1d9f49fa8ba3ec07e8dcc65 (patch)
tree75f9aa4727d41015f841ba83f4af36335f83771f /src/video_core/vulkan_common
parentMerge pull request #5292 from ReinUsesLisp/empty-set (diff)
parentvulkan_device: Allow creating a device without surface (diff)
downloadyuzu-275b96a0e22e40a0b1d9f49fa8ba3ec07e8dcc65.tar.gz
yuzu-275b96a0e22e40a0b1d9f49fa8ba3ec07e8dcc65.tar.xz
yuzu-275b96a0e22e40a0b1d9f49fa8ba3ec07e8dcc65.zip
Merge pull request #5289 from ReinUsesLisp/vulkan-device
vulkan_common: Move device abstraction to the common directory and allow surfaceless devices
Diffstat (limited to 'src/video_core/vulkan_common')
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp212
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.h82
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp886
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h307
4 files changed, 1487 insertions, 0 deletions
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
new file mode 100644
index 000000000..8d10ac29e
--- /dev/null
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -0,0 +1,212 @@
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() {
36 if (!dl.Open(AFTERMATH_LIB_NAME)) {
37 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL");
38 return;
39 }
40 if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps",
41 &GFSDK_Aftermath_DisableGpuCrashDumps) ||
42 !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps",
43 &GFSDK_Aftermath_EnableGpuCrashDumps) ||
44 !dl.GetSymbol("GFSDK_Aftermath_GetShaderDebugInfoIdentifier",
45 &GFSDK_Aftermath_GetShaderDebugInfoIdentifier) ||
46 !dl.GetSymbol("GFSDK_Aftermath_GetShaderHashSpirv", &GFSDK_Aftermath_GetShaderHashSpirv) ||
47 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_CreateDecoder",
48 &GFSDK_Aftermath_GpuCrashDump_CreateDecoder) ||
49 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_DestroyDecoder",
50 &GFSDK_Aftermath_GpuCrashDump_DestroyDecoder) ||
51 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GenerateJSON",
52 &GFSDK_Aftermath_GpuCrashDump_GenerateJSON) ||
53 !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON",
54 &GFSDK_Aftermath_GpuCrashDump_GetJSON)) {
55 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers");
56 return false;
57 }
58 dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash";
59
60 void(Common::FS::DeleteDirRecursively(dump_dir));
61 if (!Common::FS::CreateDir(dump_dir)) {
62 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
63 return;
64 }
65 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
66 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
67 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
68 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) {
69 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
70 return;
71 }
72 LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir);
73 initialized = true;
74}
75
76NsightAftermathTracker::~NsightAftermathTracker() {
77 if (initialized) {
78 (void)GFSDK_Aftermath_DisableGpuCrashDumps();
79 }
80}
81
82void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
83 if (!initialized) {
84 return;
85 }
86
87 std::vector<u32> spirv_copy = spirv;
88 GFSDK_Aftermath_SpirvCode shader;
89 shader.pData = spirv_copy.data();
90 shader.size = static_cast<u32>(spirv_copy.size() * 4);
91
92 std::scoped_lock lock{mutex};
93
94 GFSDK_Aftermath_ShaderHash hash;
95 if (!GFSDK_Aftermath_SUCCEED(
96 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
97 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
98 return;
99 }
100
101 Common::FS::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
102 if (!file.IsOpen()) {
103 LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
104 return;
105 }
106 if (file.WriteArray(spirv.data(), spirv.size()) != spirv.size()) {
107 LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash);
108 return;
109 }
110}
111
112void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
113 u32 gpu_crash_dump_size) {
114 std::scoped_lock lock{mutex};
115
116 LOG_CRITICAL(Render_Vulkan, "called");
117
118 GFSDK_Aftermath_GpuCrashDump_Decoder decoder;
119 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_CreateDecoder(
120 GFSDK_Aftermath_Version_API, gpu_crash_dump, gpu_crash_dump_size, &decoder))) {
121 LOG_ERROR(Render_Vulkan, "Failed to create decoder");
122 return;
123 }
124 SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); });
125
126 u32 json_size = 0;
127 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
128 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
129 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr,
130 this, &json_size))) {
131 LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
132 return;
133 }
134 std::vector<char> json(json_size);
135 if (!GFSDK_Aftermath_SUCCEED(
136 GFSDK_Aftermath_GpuCrashDump_GetJSON(decoder, json_size, json.data()))) {
137 LOG_ERROR(Render_Vulkan, "Failed to query JSON");
138 return;
139 }
140
141 const std::string base_name = [this] {
142 const int id = dump_id++;
143 if (id == 0) {
144 return fmt::format("{}/crash.nv-gpudmp", dump_dir);
145 } else {
146 return fmt::format("{}/crash_{}.nv-gpudmp", dump_dir, id);
147 }
148 }();
149
150 std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
151 if (Common::FS::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
152 LOG_ERROR(Render_Vulkan, "Failed to write dump file");
153 return;
154 }
155 const std::string_view json_view(json.data(), json.size());
156 if (Common::FS::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
157 LOG_ERROR(Render_Vulkan, "Failed to write JSON");
158 return;
159 }
160}
161
162void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_info,
163 u32 shader_debug_info_size) {
164 std::scoped_lock lock{mutex};
165
166 GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier;
167 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GetShaderDebugInfoIdentifier(
168 GFSDK_Aftermath_Version_API, shader_debug_info, shader_debug_info_size, &identifier))) {
169 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_GetShaderDebugInfoIdentifier failed");
170 return;
171 }
172
173 const std::string path =
174 fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
175 Common::FS::IOFile file(path, "wb");
176 if (!file.IsOpen()) {
177 LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
178 return;
179 }
180 if (file.WriteBytes(static_cast<const u8*>(shader_debug_info), shader_debug_info_size) !=
181 shader_debug_info_size) {
182 LOG_ERROR(Render_Vulkan, "Failed to write file {}", path);
183 return;
184 }
185}
186
187void NsightAftermathTracker::OnCrashDumpDescriptionCallback(
188 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description) {
189 add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "yuzu");
190}
191
192void NsightAftermathTracker::GpuCrashDumpCallback(const void* gpu_crash_dump,
193 u32 gpu_crash_dump_size, void* user_data) {
194 static_cast<NsightAftermathTracker*>(user_data)->OnGpuCrashDumpCallback(gpu_crash_dump,
195 gpu_crash_dump_size);
196}
197
198void NsightAftermathTracker::ShaderDebugInfoCallback(const void* shader_debug_info,
199 u32 shader_debug_info_size, void* user_data) {
200 static_cast<NsightAftermathTracker*>(user_data)->OnShaderDebugInfoCallback(
201 shader_debug_info, shader_debug_info_size);
202}
203
204void NsightAftermathTracker::CrashDumpDescriptionCallback(
205 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data) {
206 static_cast<NsightAftermathTracker*>(user_data)->OnCrashDumpDescriptionCallback(
207 add_description);
208}
209
210} // namespace Vulkan
211
212#endif // HAS_NSIGHT_AFTERMATH
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.h b/src/video_core/vulkan_common/nsight_aftermath_tracker.h
new file mode 100644
index 000000000..cee3847fb
--- /dev/null
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.h
@@ -0,0 +1,82 @@
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 void SaveShader(const std::vector<u32>& spirv) const;
38
39private:
40#ifdef HAS_NSIGHT_AFTERMATH
41 static void GpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size,
42 void* user_data);
43
44 static void ShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size,
45 void* user_data);
46
47 static void CrashDumpDescriptionCallback(
48 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data);
49
50 void OnGpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size);
51
52 void OnShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size);
53
54 void OnCrashDumpDescriptionCallback(
55 PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description);
56
57 mutable std::mutex mutex;
58
59 std::string dump_dir;
60 int dump_id = 0;
61
62 bool initialized = false;
63
64 Common::DynamicLibrary dl;
65 PFN_GFSDK_Aftermath_DisableGpuCrashDumps GFSDK_Aftermath_DisableGpuCrashDumps;
66 PFN_GFSDK_Aftermath_EnableGpuCrashDumps GFSDK_Aftermath_EnableGpuCrashDumps;
67 PFN_GFSDK_Aftermath_GetShaderDebugInfoIdentifier GFSDK_Aftermath_GetShaderDebugInfoIdentifier;
68 PFN_GFSDK_Aftermath_GetShaderHashSpirv GFSDK_Aftermath_GetShaderHashSpirv;
69 PFN_GFSDK_Aftermath_GpuCrashDump_CreateDecoder GFSDK_Aftermath_GpuCrashDump_CreateDecoder;
70 PFN_GFSDK_Aftermath_GpuCrashDump_DestroyDecoder GFSDK_Aftermath_GpuCrashDump_DestroyDecoder;
71 PFN_GFSDK_Aftermath_GpuCrashDump_GenerateJSON GFSDK_Aftermath_GpuCrashDump_GenerateJSON;
72 PFN_GFSDK_Aftermath_GpuCrashDump_GetJSON GFSDK_Aftermath_GpuCrashDump_GetJSON;
73#endif
74};
75
76#ifndef HAS_NSIGHT_AFTERMATH
77inline NsightAftermathTracker::NsightAftermathTracker() = default;
78inline NsightAftermathTracker::~NsightAftermathTracker() = default;
79inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {}
80#endif
81
82} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
new file mode 100644
index 000000000..75173324e
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -0,0 +1,886 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <bitset>
6#include <chrono>
7#include <optional>
8#include <string_view>
9#include <thread>
10#include <unordered_set>
11#include <utility>
12#include <vector>
13
14#include "common/assert.h"
15#include "core/settings.h"
16#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
17#include "video_core/vulkan_common/vulkan_device.h"
18#include "video_core/vulkan_common/vulkan_wrapper.h"
19
20namespace Vulkan {
21
22namespace {
23
24namespace Alternatives {
25
26constexpr std::array Depth24UnormS8_UINT{
27 VK_FORMAT_D32_SFLOAT_S8_UINT,
28 VK_FORMAT_D16_UNORM_S8_UINT,
29 VkFormat{},
30};
31
32constexpr std::array Depth16UnormS8_UINT{
33 VK_FORMAT_D24_UNORM_S8_UINT,
34 VK_FORMAT_D32_SFLOAT_S8_UINT,
35 VkFormat{},
36};
37
38} // namespace Alternatives
39
40constexpr std::array REQUIRED_EXTENSIONS{
41 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
42 VK_KHR_MAINTENANCE1_EXTENSION_NAME,
43 VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME,
44 VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME,
45 VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
46 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
47 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
48 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
49 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
50 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
51 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
52 VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
53 VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
54 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
55};
56
57template <typename T>
58void SetNext(void**& next, T& data) {
59 *next = &data;
60 next = &data.pNext;
61}
62
63constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
64 switch (format) {
65 case VK_FORMAT_D24_UNORM_S8_UINT:
66 return Alternatives::Depth24UnormS8_UINT.data();
67 case VK_FORMAT_D16_UNORM_S8_UINT:
68 return Alternatives::Depth16UnormS8_UINT.data();
69 default:
70 return nullptr;
71 }
72}
73
74VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType format_type) {
75 switch (format_type) {
76 case FormatType::Linear:
77 return properties.linearTilingFeatures;
78 case FormatType::Optimal:
79 return properties.optimalTilingFeatures;
80 case FormatType::Buffer:
81 return properties.bufferFeatures;
82 default:
83 return {};
84 }
85}
86
87[[nodiscard]] bool IsRDNA(std::string_view device_name, VkDriverIdKHR driver_id) {
88 static constexpr std::array RDNA_DEVICES{
89 "5700",
90 "5600",
91 "5500",
92 "5300",
93 };
94 if (driver_id != VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
95 return false;
96 }
97 return std::any_of(RDNA_DEVICES.begin(), RDNA_DEVICES.end(), [device_name](const char* name) {
98 return device_name.find(name) != std::string_view::npos;
99 });
100}
101
102std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
103 vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) {
104 static constexpr std::array formats{
105 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
106 VK_FORMAT_A8B8G8R8_UINT_PACK32,
107 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
108 VK_FORMAT_A8B8G8R8_SINT_PACK32,
109 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
110 VK_FORMAT_B5G6R5_UNORM_PACK16,
111 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
112 VK_FORMAT_A2B10G10R10_UINT_PACK32,
113 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
114 VK_FORMAT_R32G32B32A32_SFLOAT,
115 VK_FORMAT_R32G32B32A32_SINT,
116 VK_FORMAT_R32G32B32A32_UINT,
117 VK_FORMAT_R32G32_SFLOAT,
118 VK_FORMAT_R32G32_SINT,
119 VK_FORMAT_R32G32_UINT,
120 VK_FORMAT_R16G16B16A16_SINT,
121 VK_FORMAT_R16G16B16A16_UINT,
122 VK_FORMAT_R16G16B16A16_SNORM,
123 VK_FORMAT_R16G16B16A16_UNORM,
124 VK_FORMAT_R16G16_UNORM,
125 VK_FORMAT_R16G16_SNORM,
126 VK_FORMAT_R16G16_SFLOAT,
127 VK_FORMAT_R16G16_SINT,
128 VK_FORMAT_R16_UNORM,
129 VK_FORMAT_R16_UINT,
130 VK_FORMAT_R8G8B8A8_SRGB,
131 VK_FORMAT_R8G8_UNORM,
132 VK_FORMAT_R8G8_SNORM,
133 VK_FORMAT_R8G8_SINT,
134 VK_FORMAT_R8G8_UINT,
135 VK_FORMAT_R8_UNORM,
136 VK_FORMAT_R8_SNORM,
137 VK_FORMAT_R8_SINT,
138 VK_FORMAT_R8_UINT,
139 VK_FORMAT_B10G11R11_UFLOAT_PACK32,
140 VK_FORMAT_R32_SFLOAT,
141 VK_FORMAT_R32_UINT,
142 VK_FORMAT_R32_SINT,
143 VK_FORMAT_R16_SFLOAT,
144 VK_FORMAT_R16G16B16A16_SFLOAT,
145 VK_FORMAT_B8G8R8A8_UNORM,
146 VK_FORMAT_B8G8R8A8_SRGB,
147 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
148 VK_FORMAT_D32_SFLOAT,
149 VK_FORMAT_D16_UNORM,
150 VK_FORMAT_D16_UNORM_S8_UINT,
151 VK_FORMAT_D24_UNORM_S8_UINT,
152 VK_FORMAT_D32_SFLOAT_S8_UINT,
153 VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
154 VK_FORMAT_BC2_UNORM_BLOCK,
155 VK_FORMAT_BC3_UNORM_BLOCK,
156 VK_FORMAT_BC4_UNORM_BLOCK,
157 VK_FORMAT_BC4_SNORM_BLOCK,
158 VK_FORMAT_BC5_UNORM_BLOCK,
159 VK_FORMAT_BC5_SNORM_BLOCK,
160 VK_FORMAT_BC7_UNORM_BLOCK,
161 VK_FORMAT_BC6H_UFLOAT_BLOCK,
162 VK_FORMAT_BC6H_SFLOAT_BLOCK,
163 VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
164 VK_FORMAT_BC2_SRGB_BLOCK,
165 VK_FORMAT_BC3_SRGB_BLOCK,
166 VK_FORMAT_BC7_SRGB_BLOCK,
167 VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
168 VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
169 VK_FORMAT_ASTC_5x4_UNORM_BLOCK,
170 VK_FORMAT_ASTC_5x4_SRGB_BLOCK,
171 VK_FORMAT_ASTC_5x5_UNORM_BLOCK,
172 VK_FORMAT_ASTC_5x5_SRGB_BLOCK,
173 VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
174 VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
175 VK_FORMAT_ASTC_6x6_UNORM_BLOCK,
176 VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
177 VK_FORMAT_ASTC_8x5_UNORM_BLOCK,
178 VK_FORMAT_ASTC_8x5_SRGB_BLOCK,
179 VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
180 VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
181 VK_FORMAT_ASTC_8x8_UNORM_BLOCK,
182 VK_FORMAT_ASTC_8x8_SRGB_BLOCK,
183 VK_FORMAT_ASTC_10x5_UNORM_BLOCK,
184 VK_FORMAT_ASTC_10x5_SRGB_BLOCK,
185 VK_FORMAT_ASTC_10x6_UNORM_BLOCK,
186 VK_FORMAT_ASTC_10x6_SRGB_BLOCK,
187 VK_FORMAT_ASTC_10x8_UNORM_BLOCK,
188 VK_FORMAT_ASTC_10x8_SRGB_BLOCK,
189 VK_FORMAT_ASTC_10x10_UNORM_BLOCK,
190 VK_FORMAT_ASTC_10x10_SRGB_BLOCK,
191 VK_FORMAT_ASTC_12x10_UNORM_BLOCK,
192 VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
193 VK_FORMAT_ASTC_12x12_UNORM_BLOCK,
194 VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
195 VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
196 VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
197 VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
198 VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
199 VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
200 };
201 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
202 for (const auto format : formats) {
203 format_properties.emplace(format, physical.GetFormatProperties(format));
204 }
205 return format_properties;
206}
207
208} // Anonymous namespace
209
210Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
211 const vk::InstanceDispatch& dld_)
212 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
213 format_properties{GetFormatProperties(physical, dld)} {
214 CheckSuitability();
215 SetupFamilies(surface);
216 SetupFeatures();
217
218 const auto queue_cis = GetDeviceQueueCreateInfos();
219 const std::vector extensions = LoadExtensions();
220
221 VkPhysicalDeviceFeatures2 features2{
222 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
223 .pNext = nullptr,
224 };
225 const void* first_next = &features2;
226 void** next = &features2.pNext;
227
228 features2.features = {
229 .robustBufferAccess = false,
230 .fullDrawIndexUint32 = false,
231 .imageCubeArray = true,
232 .independentBlend = true,
233 .geometryShader = true,
234 .tessellationShader = true,
235 .sampleRateShading = false,
236 .dualSrcBlend = false,
237 .logicOp = false,
238 .multiDrawIndirect = false,
239 .drawIndirectFirstInstance = false,
240 .depthClamp = true,
241 .depthBiasClamp = true,
242 .fillModeNonSolid = false,
243 .depthBounds = false,
244 .wideLines = false,
245 .largePoints = true,
246 .alphaToOne = false,
247 .multiViewport = true,
248 .samplerAnisotropy = true,
249 .textureCompressionETC2 = false,
250 .textureCompressionASTC_LDR = is_optimal_astc_supported,
251 .textureCompressionBC = false,
252 .occlusionQueryPrecise = true,
253 .pipelineStatisticsQuery = false,
254 .vertexPipelineStoresAndAtomics = true,
255 .fragmentStoresAndAtomics = true,
256 .shaderTessellationAndGeometryPointSize = false,
257 .shaderImageGatherExtended = true,
258 .shaderStorageImageExtendedFormats = false,
259 .shaderStorageImageMultisample = true,
260 .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
261 .shaderStorageImageWriteWithoutFormat = true,
262 .shaderUniformBufferArrayDynamicIndexing = false,
263 .shaderSampledImageArrayDynamicIndexing = false,
264 .shaderStorageBufferArrayDynamicIndexing = false,
265 .shaderStorageImageArrayDynamicIndexing = false,
266 .shaderClipDistance = false,
267 .shaderCullDistance = false,
268 .shaderFloat64 = false,
269 .shaderInt64 = false,
270 .shaderInt16 = false,
271 .shaderResourceResidency = false,
272 .shaderResourceMinLod = false,
273 .sparseBinding = false,
274 .sparseResidencyBuffer = false,
275 .sparseResidencyImage2D = false,
276 .sparseResidencyImage3D = false,
277 .sparseResidency2Samples = false,
278 .sparseResidency4Samples = false,
279 .sparseResidency8Samples = false,
280 .sparseResidency16Samples = false,
281 .sparseResidencyAliased = false,
282 .variableMultisampleRate = false,
283 .inheritedQueries = false,
284 };
285 VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{
286 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR,
287 .pNext = nullptr,
288 .timelineSemaphore = true,
289 };
290 SetNext(next, timeline_semaphore);
291
292 VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{
293 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
294 .pNext = nullptr,
295 .storageBuffer16BitAccess = false,
296 .uniformAndStorageBuffer16BitAccess = true,
297 .storagePushConstant16 = false,
298 .storageInputOutput16 = false,
299 };
300 SetNext(next, bit16_storage);
301
302 VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage{
303 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR,
304 .pNext = nullptr,
305 .storageBuffer8BitAccess = false,
306 .uniformAndStorageBuffer8BitAccess = true,
307 .storagePushConstant8 = false,
308 };
309 SetNext(next, bit8_storage);
310
311 VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{
312 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT,
313 .hostQueryReset = true,
314 };
315 SetNext(next, host_query_reset);
316
317 VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
318 if (is_float16_supported) {
319 float16_int8 = {
320 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
321 .pNext = nullptr,
322 .shaderFloat16 = true,
323 .shaderInt8 = false,
324 };
325 SetNext(next, float16_int8);
326 } else {
327 LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
328 }
329
330 if (!nv_viewport_swizzle) {
331 LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles");
332 }
333
334 VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR std430_layout;
335 if (khr_uniform_buffer_standard_layout) {
336 std430_layout = {
337 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR,
338 .pNext = nullptr,
339 .uniformBufferStandardLayout = true,
340 };
341 SetNext(next, std430_layout);
342 } else {
343 LOG_INFO(Render_Vulkan, "Device doesn't support packed UBOs");
344 }
345
346 VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8;
347 if (ext_index_type_uint8) {
348 index_type_uint8 = {
349 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT,
350 .pNext = nullptr,
351 .indexTypeUint8 = true,
352 };
353 SetNext(next, index_type_uint8);
354 } else {
355 LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
356 }
357
358 VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
359 if (ext_transform_feedback) {
360 transform_feedback = {
361 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT,
362 .pNext = nullptr,
363 .transformFeedback = true,
364 .geometryStreams = true,
365 };
366 SetNext(next, transform_feedback);
367 } else {
368 LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks");
369 }
370
371 VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border;
372 if (ext_custom_border_color) {
373 custom_border = {
374 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT,
375 .pNext = nullptr,
376 .customBorderColors = VK_TRUE,
377 .customBorderColorWithoutFormat = VK_TRUE,
378 };
379 SetNext(next, custom_border);
380 } else {
381 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
382 }
383
384 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
385 if (ext_extended_dynamic_state) {
386 dynamic_state = {
387 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT,
388 .pNext = nullptr,
389 .extendedDynamicState = VK_TRUE,
390 };
391 SetNext(next, dynamic_state);
392 } else {
393 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
394 }
395
396 VkPhysicalDeviceRobustness2FeaturesEXT robustness2;
397 if (ext_robustness2) {
398 robustness2 = {
399 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
400 .pNext = nullptr,
401 .robustBufferAccess2 = false,
402 .robustImageAccess2 = true,
403 .nullDescriptor = true,
404 };
405 SetNext(next, robustness2);
406 } else {
407 LOG_INFO(Render_Vulkan, "Device doesn't support robustness2");
408 }
409
410 if (!ext_depth_range_unrestricted) {
411 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
412 }
413
414 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
415 if (nv_device_diagnostics_config) {
416 nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
417
418 diagnostics_nv = {
419 .sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV,
420 .pNext = &features2,
421 .flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
422 VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
423 VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV,
424 };
425 first_next = &diagnostics_nv;
426 }
427 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
428
429 CollectTelemetryParameters();
430 CollectToolingInfo();
431
432 if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_MESA_RADV) {
433 LOG_WARNING(
434 Render_Vulkan,
435 "Blacklisting RADV for VK_EXT_extended_dynamic state, likely due to a bug in yuzu");
436 ext_extended_dynamic_state = false;
437 }
438 if (ext_extended_dynamic_state && IsRDNA(properties.deviceName, driver_id)) {
439 // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but on RDNA devices it
440 // seems to cause stability issues
441 LOG_WARNING(
442 Render_Vulkan,
443 "Blacklisting AMD proprietary on RDNA devices from VK_EXT_extended_dynamic_state");
444 ext_extended_dynamic_state = false;
445 }
446
447 graphics_queue = logical.GetQueue(graphics_family);
448 present_queue = logical.GetQueue(present_family);
449
450 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
451}
452
453Device::~Device() = default;
454
455VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
456 FormatType format_type) const {
457 if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
458 return wanted_format;
459 }
460 // The wanted format is not supported by hardware, search for alternatives
461 const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
462 if (alternatives == nullptr) {
463 UNREACHABLE_MSG("Format={} with usage={} and type={} has no defined alternatives and host "
464 "hardware does not support it",
465 wanted_format, wanted_usage, format_type);
466 return wanted_format;
467 }
468
469 std::size_t i = 0;
470 for (VkFormat alternative = *alternatives; alternative; alternative = alternatives[++i]) {
471 if (!IsFormatSupported(alternative, wanted_usage, format_type)) {
472 continue;
473 }
474 LOG_WARNING(Render_Vulkan,
475 "Emulating format={} with alternative format={} with usage={} and type={}",
476 wanted_format, alternative, wanted_usage, format_type);
477 return alternative;
478 }
479
480 // No alternatives found, panic
481 UNREACHABLE_MSG("Format={} with usage={} and type={} is not supported by the host hardware and "
482 "doesn't support any of the alternatives",
483 wanted_format, wanted_usage, format_type);
484 return wanted_format;
485}
486
487void Device::ReportLoss() const {
488 LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
489
490 // Wait for the log to flush and for Nsight Aftermath to dump the results
491 std::this_thread::sleep_for(std::chrono::seconds{15});
492}
493
494void Device::SaveShader(const std::vector<u32>& spirv) const {
495 if (nsight_aftermath_tracker) {
496 nsight_aftermath_tracker->SaveShader(spirv);
497 }
498}
499
500bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const {
501 // Disable for now to avoid converting ASTC twice.
502 static constexpr std::array astc_formats = {
503 VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
504 VK_FORMAT_ASTC_5x4_UNORM_BLOCK, VK_FORMAT_ASTC_5x4_SRGB_BLOCK,
505 VK_FORMAT_ASTC_5x5_UNORM_BLOCK, VK_FORMAT_ASTC_5x5_SRGB_BLOCK,
506 VK_FORMAT_ASTC_6x5_UNORM_BLOCK, VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
507 VK_FORMAT_ASTC_6x6_UNORM_BLOCK, VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
508 VK_FORMAT_ASTC_8x5_UNORM_BLOCK, VK_FORMAT_ASTC_8x5_SRGB_BLOCK,
509 VK_FORMAT_ASTC_8x6_UNORM_BLOCK, VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
510 VK_FORMAT_ASTC_8x8_UNORM_BLOCK, VK_FORMAT_ASTC_8x8_SRGB_BLOCK,
511 VK_FORMAT_ASTC_10x5_UNORM_BLOCK, VK_FORMAT_ASTC_10x5_SRGB_BLOCK,
512 VK_FORMAT_ASTC_10x6_UNORM_BLOCK, VK_FORMAT_ASTC_10x6_SRGB_BLOCK,
513 VK_FORMAT_ASTC_10x8_UNORM_BLOCK, VK_FORMAT_ASTC_10x8_SRGB_BLOCK,
514 VK_FORMAT_ASTC_10x10_UNORM_BLOCK, VK_FORMAT_ASTC_10x10_SRGB_BLOCK,
515 VK_FORMAT_ASTC_12x10_UNORM_BLOCK, VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
516 VK_FORMAT_ASTC_12x12_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
517 };
518 if (!features.textureCompressionASTC_LDR) {
519 return false;
520 }
521 const auto format_feature_usage{
522 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT |
523 VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
524 VK_FORMAT_FEATURE_TRANSFER_DST_BIT};
525 for (const auto format : astc_formats) {
526 const auto physical_format_properties{physical.GetFormatProperties(format)};
527 if ((physical_format_properties.optimalTilingFeatures & format_feature_usage) == 0) {
528 return false;
529 }
530 }
531 return true;
532}
533
534bool Device::TestDepthStencilBlits() const {
535 static constexpr VkFormatFeatureFlags required_features =
536 VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
537 const auto test_features = [](VkFormatProperties props) {
538 return (props.optimalTilingFeatures & required_features) == required_features;
539 };
540 return test_features(format_properties.at(VK_FORMAT_D32_SFLOAT_S8_UINT)) &&
541 test_features(format_properties.at(VK_FORMAT_D24_UNORM_S8_UINT));
542}
543
544bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
545 FormatType format_type) const {
546 const auto it = format_properties.find(wanted_format);
547 if (it == format_properties.end()) {
548 UNIMPLEMENTED_MSG("Unimplemented format query={}", wanted_format);
549 return true;
550 }
551 const auto supported_usage = GetFormatFeatures(it->second, format_type);
552 return (supported_usage & wanted_usage) == wanted_usage;
553}
554
555void Device::CheckSuitability() const {
556 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions;
557 for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) {
558 for (std::size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) {
559 if (available_extensions[i]) {
560 continue;
561 }
562 const std::string_view name{property.extensionName};
563 available_extensions[i] = name == REQUIRED_EXTENSIONS[i];
564 }
565 }
566 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) {
567 if (available_extensions[i]) {
568 continue;
569 }
570 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]);
571 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
572 }
573 struct LimitTuple {
574 u32 minimum;
575 u32 value;
576 const char* name;
577 };
578 const VkPhysicalDeviceLimits& limits{properties.limits};
579 const std::array limits_report{
580 LimitTuple{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"},
581 LimitTuple{16, limits.maxViewports, "maxViewports"},
582 LimitTuple{8, limits.maxColorAttachments, "maxColorAttachments"},
583 LimitTuple{8, limits.maxClipDistances, "maxClipDistances"},
584 };
585 for (const auto& tuple : limits_report) {
586 if (tuple.value < tuple.minimum) {
587 LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", tuple.name,
588 tuple.minimum, tuple.value);
589 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
590 }
591 }
592 const VkPhysicalDeviceFeatures features{physical.GetFeatures()};
593 const std::array feature_report{
594 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
595 std::make_pair(features.imageCubeArray, "imageCubeArray"),
596 std::make_pair(features.independentBlend, "independentBlend"),
597 std::make_pair(features.depthClamp, "depthClamp"),
598 std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"),
599 std::make_pair(features.largePoints, "largePoints"),
600 std::make_pair(features.multiViewport, "multiViewport"),
601 std::make_pair(features.depthBiasClamp, "depthBiasClamp"),
602 std::make_pair(features.geometryShader, "geometryShader"),
603 std::make_pair(features.tessellationShader, "tessellationShader"),
604 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
605 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"),
606 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"),
607 std::make_pair(features.shaderStorageImageMultisample, "shaderStorageImageMultisample"),
608 std::make_pair(features.shaderStorageImageWriteWithoutFormat,
609 "shaderStorageImageWriteWithoutFormat"),
610 };
611 for (const auto& [is_supported, name] : feature_report) {
612 if (is_supported) {
613 continue;
614 }
615 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name);
616 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
617 }
618}
619
620std::vector<const char*> Device::LoadExtensions() {
621 std::vector<const char*> extensions;
622 extensions.reserve(7 + REQUIRED_EXTENSIONS.size());
623 extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end());
624
625 bool has_khr_shader_float16_int8{};
626 bool has_ext_subgroup_size_control{};
627 bool has_ext_transform_feedback{};
628 bool has_ext_custom_border_color{};
629 bool has_ext_extended_dynamic_state{};
630 bool has_ext_robustness2{};
631 for (const VkExtensionProperties& extension : physical.EnumerateDeviceExtensionProperties()) {
632 const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
633 bool push) {
634 if (extension.extensionName != std::string_view(name)) {
635 return;
636 }
637 if (push) {
638 extensions.push_back(name);
639 }
640 if (status) {
641 status->get() = true;
642 }
643 };
644 test(nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
645 test(khr_uniform_buffer_standard_layout,
646 VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
647 test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
648 test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
649 test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
650 test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true);
651 test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,
652 true);
653 test(ext_tooling_info, VK_EXT_TOOLING_INFO_EXTENSION_NAME, true);
654 test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true);
655 test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
656 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
657 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
658 test(has_ext_robustness2, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, false);
659 test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false);
660 if (Settings::values.renderer_debug) {
661 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
662 true);
663 }
664 }
665
666 VkPhysicalDeviceFeatures2KHR features;
667 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
668
669 VkPhysicalDeviceProperties2KHR physical_properties;
670 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
671
672 if (has_khr_shader_float16_int8) {
673 VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features;
674 float16_int8_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
675 float16_int8_features.pNext = nullptr;
676 features.pNext = &float16_int8_features;
677
678 physical.GetFeatures2KHR(features);
679 is_float16_supported = float16_int8_features.shaderFloat16;
680 extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
681 }
682
683 if (has_ext_subgroup_size_control) {
684 VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroup_features;
685 subgroup_features.sType =
686 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
687 subgroup_features.pNext = nullptr;
688 features.pNext = &subgroup_features;
689 physical.GetFeatures2KHR(features);
690
691 VkPhysicalDeviceSubgroupSizeControlPropertiesEXT subgroup_properties;
692 subgroup_properties.sType =
693 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT;
694 subgroup_properties.pNext = nullptr;
695 physical_properties.pNext = &subgroup_properties;
696 physical.GetProperties2KHR(physical_properties);
697
698 is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize;
699
700 if (subgroup_features.subgroupSizeControl &&
701 subgroup_properties.minSubgroupSize <= GuestWarpSize &&
702 subgroup_properties.maxSubgroupSize >= GuestWarpSize) {
703 extensions.push_back(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
704 guest_warp_stages = subgroup_properties.requiredSubgroupSizeStages;
705 }
706 } else {
707 is_warp_potentially_bigger = true;
708 }
709
710 if (has_ext_transform_feedback) {
711 VkPhysicalDeviceTransformFeedbackFeaturesEXT tfb_features;
712 tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
713 tfb_features.pNext = nullptr;
714 features.pNext = &tfb_features;
715 physical.GetFeatures2KHR(features);
716
717 VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties;
718 tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
719 tfb_properties.pNext = nullptr;
720 physical_properties.pNext = &tfb_properties;
721 physical.GetProperties2KHR(physical_properties);
722
723 if (tfb_features.transformFeedback && tfb_features.geometryStreams &&
724 tfb_properties.maxTransformFeedbackStreams >= 4 &&
725 tfb_properties.maxTransformFeedbackBuffers && tfb_properties.transformFeedbackQueries &&
726 tfb_properties.transformFeedbackDraw) {
727 extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
728 ext_transform_feedback = true;
729 }
730 }
731
732 if (has_ext_custom_border_color) {
733 VkPhysicalDeviceCustomBorderColorFeaturesEXT border_features;
734 border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
735 border_features.pNext = nullptr;
736 features.pNext = &border_features;
737 physical.GetFeatures2KHR(features);
738
739 if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) {
740 extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
741 ext_custom_border_color = true;
742 }
743 }
744
745 if (has_ext_extended_dynamic_state) {
746 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
747 dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
748 dynamic_state.pNext = nullptr;
749 features.pNext = &dynamic_state;
750 physical.GetFeatures2KHR(features);
751
752 if (dynamic_state.extendedDynamicState) {
753 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
754 ext_extended_dynamic_state = true;
755 }
756 }
757
758 if (has_ext_robustness2) {
759 VkPhysicalDeviceRobustness2FeaturesEXT robustness2;
760 robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
761 robustness2.pNext = nullptr;
762 features.pNext = &robustness2;
763 physical.GetFeatures2KHR(features);
764 if (robustness2.nullDescriptor && robustness2.robustImageAccess2) {
765 extensions.push_back(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
766 ext_robustness2 = true;
767 }
768 }
769
770 return extensions;
771}
772
773void Device::SetupFamilies(VkSurfaceKHR surface) {
774 const std::vector queue_family_properties = physical.GetQueueFamilyProperties();
775 std::optional<u32> graphics;
776 std::optional<u32> present;
777 for (u32 index = 0; index < static_cast<u32>(queue_family_properties.size()); ++index) {
778 if (graphics && (present || !surface)) {
779 break;
780 }
781 const VkQueueFamilyProperties& queue_family = queue_family_properties[index];
782 if (queue_family.queueCount == 0) {
783 continue;
784 }
785 if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
786 graphics = index;
787 }
788 if (surface && physical.GetSurfaceSupportKHR(index, surface)) {
789 present = index;
790 }
791 }
792 if (!graphics) {
793 LOG_ERROR(Render_Vulkan, "Device lacks a graphics queue");
794 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
795 }
796 if (surface && !present) {
797 LOG_ERROR(Render_Vulkan, "Device lacks a present queue");
798 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
799 }
800 graphics_family = *graphics;
801 present_family = *present;
802}
803
804void Device::SetupFeatures() {
805 const auto supported_features{physical.GetFeatures()};
806 is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat;
807 is_blit_depth_stencil_supported = TestDepthStencilBlits();
808 is_optimal_astc_supported = IsOptimalAstcSupported(supported_features);
809}
810
811void Device::CollectTelemetryParameters() {
812 VkPhysicalDeviceDriverPropertiesKHR driver{
813 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
814 .pNext = nullptr,
815 .driverID = {},
816 .driverName = {},
817 .driverInfo = {},
818 .conformanceVersion = {},
819 };
820
821 VkPhysicalDeviceProperties2KHR device_properties{
822 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
823 .pNext = &driver,
824 .properties = {},
825 };
826 physical.GetProperties2KHR(device_properties);
827
828 driver_id = driver.driverID;
829 vendor_name = driver.driverName;
830
831 const std::vector extensions = physical.EnumerateDeviceExtensionProperties();
832 reported_extensions.reserve(std::size(extensions));
833 for (const auto& extension : extensions) {
834 reported_extensions.emplace_back(extension.extensionName);
835 }
836}
837
838void Device::CollectToolingInfo() {
839 if (!ext_tooling_info) {
840 return;
841 }
842 const auto vkGetPhysicalDeviceToolPropertiesEXT =
843 reinterpret_cast<PFN_vkGetPhysicalDeviceToolPropertiesEXT>(
844 dld.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceToolPropertiesEXT"));
845 if (!vkGetPhysicalDeviceToolPropertiesEXT) {
846 return;
847 }
848 u32 tool_count = 0;
849 if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, nullptr) != VK_SUCCESS) {
850 return;
851 }
852 std::vector<VkPhysicalDeviceToolPropertiesEXT> tools(tool_count);
853 if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, tools.data()) != VK_SUCCESS) {
854 return;
855 }
856 for (const VkPhysicalDeviceToolPropertiesEXT& tool : tools) {
857 const std::string_view name = tool.name;
858 LOG_INFO(Render_Vulkan, "{}", name);
859 has_renderdoc = has_renderdoc || name == "RenderDoc";
860 has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics";
861 }
862}
863
864std::vector<VkDeviceQueueCreateInfo> Device::GetDeviceQueueCreateInfos() const {
865 static constexpr float QUEUE_PRIORITY = 1.0f;
866
867 std::unordered_set<u32> unique_queue_families{graphics_family, present_family};
868 std::vector<VkDeviceQueueCreateInfo> queue_cis;
869 queue_cis.reserve(unique_queue_families.size());
870
871 for (const u32 queue_family : unique_queue_families) {
872 auto& ci = queue_cis.emplace_back(VkDeviceQueueCreateInfo{
873 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
874 .pNext = nullptr,
875 .flags = 0,
876 .queueFamilyIndex = queue_family,
877 .queueCount = 1,
878 .pQueuePriorities = nullptr,
879 });
880 ci.pQueuePriorities = &QUEUE_PRIORITY;
881 }
882
883 return queue_cis;
884}
885
886} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
new file mode 100644
index 000000000..a973c3ce4
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -0,0 +1,307 @@
1// Copyright 2018 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 <string>
8#include <string_view>
9#include <unordered_map>
10#include <vector>
11
12#include "common/common_types.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h"
14
15namespace Vulkan {
16
17class NsightAftermathTracker;
18
19/// Format usage descriptor.
20enum class FormatType { Linear, Optimal, Buffer };
21
22/// Subgroup size of the guest emulated hardware (Nvidia has 32 threads per subgroup).
23const u32 GuestWarpSize = 32;
24
25/// Handles data specific to a physical device.
26class Device final {
27public:
28 explicit Device(VkInstance instance, vk::PhysicalDevice physical, VkSurfaceKHR surface,
29 const vk::InstanceDispatch& dld);
30 ~Device();
31
32 /**
33 * Returns a format supported by the device for the passed requeriments.
34 * @param wanted_format The ideal format to be returned. It may not be the returned format.
35 * @param wanted_usage The usage that must be fulfilled even if the format is not supported.
36 * @param format_type Format type usage.
37 * @returns A format supported by the device.
38 */
39 VkFormat GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
40 FormatType format_type) const;
41
42 /// Reports a device loss.
43 void ReportLoss() const;
44
45 /// Reports a shader to Nsight Aftermath.
46 void SaveShader(const std::vector<u32>& spirv) const;
47
48 /// Returns the dispatch loader with direct function pointers of the device.
49 const vk::DeviceDispatch& GetDispatchLoader() const {
50 return dld;
51 }
52
53 /// Returns the logical device.
54 const vk::Device& GetLogical() const {
55 return logical;
56 }
57
58 /// Returns the physical device.
59 vk::PhysicalDevice GetPhysical() const {
60 return physical;
61 }
62
63 /// Returns the main graphics queue.
64 vk::Queue GetGraphicsQueue() const {
65 return graphics_queue;
66 }
67
68 /// Returns the main present queue.
69 vk::Queue GetPresentQueue() const {
70 return present_queue;
71 }
72
73 /// Returns main graphics queue family index.
74 u32 GetGraphicsFamily() const {
75 return graphics_family;
76 }
77
78 /// Returns main present queue family index.
79 u32 GetPresentFamily() const {
80 return present_family;
81 }
82
83 /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers.
84 u32 ApiVersion() const {
85 return properties.apiVersion;
86 }
87
88 /// Returns the current driver version provided in Vulkan-formatted version numbers.
89 u32 GetDriverVersion() const {
90 return properties.driverVersion;
91 }
92
93 /// Returns the device name.
94 std::string_view GetModelName() const {
95 return properties.deviceName;
96 }
97
98 /// Returns the driver ID.
99 VkDriverIdKHR GetDriverID() const {
100 return driver_id;
101 }
102
103 /// Returns uniform buffer alignment requeriment.
104 VkDeviceSize GetUniformBufferAlignment() const {
105 return properties.limits.minUniformBufferOffsetAlignment;
106 }
107
108 /// Returns storage alignment requeriment.
109 VkDeviceSize GetStorageBufferAlignment() const {
110 return properties.limits.minStorageBufferOffsetAlignment;
111 }
112
113 /// Returns the maximum range for storage buffers.
114 VkDeviceSize GetMaxStorageBufferRange() const {
115 return properties.limits.maxStorageBufferRange;
116 }
117
118 /// Returns the maximum size for push constants.
119 VkDeviceSize GetMaxPushConstantsSize() const {
120 return properties.limits.maxPushConstantsSize;
121 }
122
123 /// Returns the maximum size for shared memory.
124 u32 GetMaxComputeSharedMemorySize() const {
125 return properties.limits.maxComputeSharedMemorySize;
126 }
127
128 /// Returns true if ASTC is natively supported.
129 bool IsOptimalAstcSupported() const {
130 return is_optimal_astc_supported;
131 }
132
133 /// Returns true if the device supports float16 natively
134 bool IsFloat16Supported() const {
135 return is_float16_supported;
136 }
137
138 /// Returns true if the device warp size can potentially be bigger than guest's warp size.
139 bool IsWarpSizePotentiallyBiggerThanGuest() const {
140 return is_warp_potentially_bigger;
141 }
142
143 /// Returns true if the device can be forced to use the guest warp size.
144 bool IsGuestWarpSizeSupported(VkShaderStageFlagBits stage) const {
145 return guest_warp_stages & stage;
146 }
147
148 /// Returns true if formatless image load is supported.
149 bool IsFormatlessImageLoadSupported() const {
150 return is_formatless_image_load_supported;
151 }
152
153 /// Returns true when blitting from and to depth stencil images is supported.
154 bool IsBlitDepthStencilSupported() const {
155 return is_blit_depth_stencil_supported;
156 }
157
158 /// Returns true if the device supports VK_NV_viewport_swizzle.
159 bool IsNvViewportSwizzleSupported() const {
160 return nv_viewport_swizzle;
161 }
162
163 /// Returns true if the device supports VK_EXT_scalar_block_layout.
164 bool IsKhrUniformBufferStandardLayoutSupported() const {
165 return khr_uniform_buffer_standard_layout;
166 }
167
168 /// Returns true if the device supports VK_EXT_index_type_uint8.
169 bool IsExtIndexTypeUint8Supported() const {
170 return ext_index_type_uint8;
171 }
172
173 /// Returns true if the device supports VK_EXT_sampler_filter_minmax.
174 bool IsExtSamplerFilterMinmaxSupported() const {
175 return ext_sampler_filter_minmax;
176 }
177
178 /// Returns true if the device supports VK_EXT_depth_range_unrestricted.
179 bool IsExtDepthRangeUnrestrictedSupported() const {
180 return ext_depth_range_unrestricted;
181 }
182
183 /// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
184 bool IsExtShaderViewportIndexLayerSupported() const {
185 return ext_shader_viewport_index_layer;
186 }
187
188 /// Returns true if the device supports VK_EXT_transform_feedback.
189 bool IsExtTransformFeedbackSupported() const {
190 return ext_transform_feedback;
191 }
192
193 /// Returns true if the device supports VK_EXT_custom_border_color.
194 bool IsExtCustomBorderColorSupported() const {
195 return ext_custom_border_color;
196 }
197
198 /// Returns true if the device supports VK_EXT_extended_dynamic_state.
199 bool IsExtExtendedDynamicStateSupported() const {
200 return ext_extended_dynamic_state;
201 }
202
203 /// Returns true if the device supports VK_EXT_shader_stencil_export.
204 bool IsExtShaderStencilExportSupported() const {
205 return ext_shader_stencil_export;
206 }
207
208 /// Returns true when a known debugging tool is attached.
209 bool HasDebuggingToolAttached() const {
210 return has_renderdoc || has_nsight_graphics;
211 }
212
213 /// Returns the vendor name reported from Vulkan.
214 std::string_view GetVendorName() const {
215 return vendor_name;
216 }
217
218 /// Returns the list of available extensions.
219 const std::vector<std::string>& GetAvailableExtensions() const {
220 return reported_extensions;
221 }
222
223 /// Returns true if the setting for async shader compilation is enabled.
224 bool UseAsynchronousShaders() const {
225 return use_asynchronous_shaders;
226 }
227
228private:
229 /// Checks if the physical device is suitable.
230 void CheckSuitability() const;
231
232 /// Loads extensions into a vector and stores available ones in this object.
233 std::vector<const char*> LoadExtensions();
234
235 /// Sets up queue families.
236 void SetupFamilies(VkSurfaceKHR surface);
237
238 /// Sets up device features.
239 void SetupFeatures();
240
241 /// Collects telemetry information from the device.
242 void CollectTelemetryParameters();
243
244 /// Collects information about attached tools.
245 void CollectToolingInfo();
246
247 /// Returns a list of queue initialization descriptors.
248 std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
249
250 /// Returns true if ASTC textures are natively supported.
251 bool IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const;
252
253 /// Returns true if the device natively supports blitting depth stencil images.
254 bool TestDepthStencilBlits() const;
255
256 /// Returns true if a format is supported.
257 bool IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
258 FormatType format_type) const;
259
260 VkInstance instance; ///< Vulkan instance.
261 vk::DeviceDispatch dld; ///< Device function pointers.
262 vk::PhysicalDevice physical; ///< Physical device.
263 VkPhysicalDeviceProperties properties; ///< Device properties.
264 vk::Device logical; ///< Logical device.
265 vk::Queue graphics_queue; ///< Main graphics queue.
266 vk::Queue present_queue; ///< Main present queue.
267 u32 instance_version{}; ///< Vulkan onstance version.
268 u32 graphics_family{}; ///< Main graphics queue family index.
269 u32 present_family{}; ///< Main present queue family index.
270 VkDriverIdKHR driver_id{}; ///< Driver ID.
271 VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.ed
272 bool is_optimal_astc_supported{}; ///< Support for native ASTC.
273 bool is_float16_supported{}; ///< Support for float16 arithmetics.
274 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
275 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
276 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
277 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
278 bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs.
279 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
280 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
281 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
282 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
283 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
284 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
285 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
286 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
287 bool ext_robustness2{}; ///< Support for VK_EXT_robustness2.
288 bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export.
289 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
290 bool has_renderdoc{}; ///< Has RenderDoc attached
291 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
292
293 // Asynchronous Graphics Pipeline setting
294 bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
295
296 // Telemetry parameters
297 std::string vendor_name; ///< Device's driver name.
298 std::vector<std::string> reported_extensions; ///< Reported Vulkan extensions.
299
300 /// Format properties dictionary.
301 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
302
303 /// Nsight Aftermath GPU crash tracker
304 std::unique_ptr<NsightAftermathTracker> nsight_aftermath_tracker;
305};
306
307} // namespace Vulkan