summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp6
-rw-r--r--src/core/file_sys/content_archive.cpp10
-rw-r--r--src/core/file_sys/ips_layer.cpp6
-rw-r--r--src/core/file_sys/submission_package.cpp10
-rw-r--r--src/core/file_sys/vfs.cpp7
-rw-r--r--src/core/file_sys/vfs_offset.cpp7
-rw-r--r--src/core/file_sys/vfs_static.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp4
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp6
-rw-r--r--src/core/hle/service/sockets/bsd.cpp2
-rw-r--r--src/core/loader/nso.cpp6
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp88
-rw-r--r--src/input_common/gcadapter/gc_adapter.h5
-rw-r--r--src/input_common/main.cpp11
-rw-r--r--src/video_core/CMakeLists.txt8
-rw-r--r--src/video_core/engines/maxwell_3d.cpp2
-rw-r--r--src/video_core/macro/macro.cpp2
-rw-r--r--src/video_core/memory_manager.cpp6
-rw-r--r--src/video_core/query_cache.h6
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.h11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp12
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp33
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp31
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h16
-rw-r--r--src/video_core/renderer_vulkan/vk_command_pool.cpp41
-rw-r--r--src/video_core/renderer_vulkan/vk_command_pool.h35
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp23
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp56
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h70
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp54
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h20
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.cpp311
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.h196
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_pool.cpp63
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_pool.h43
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp77
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h70
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp49
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h15
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp7
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h48
-rw-r--r--src/video_core/shader/ast.h25
-rw-r--r--src/video_core/shader/async_shaders.h11
-rw-r--r--src/video_core/shader/control_flow.cpp27
-rw-r--r--src/video_core/shader/track.cpp4
-rw-r--r--src/video_core/texture_cache/surface_base.cpp10
-rw-r--r--src/yuzu/game_list.cpp80
-rw-r--r--src/yuzu/game_list.h24
-rw-r--r--src/yuzu/install_dialog.h5
-rw-r--r--src/yuzu/main.cpp15
70 files changed, 903 insertions, 950 deletions
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 54556e0f9..caefc09f4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -34,7 +34,7 @@ std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigne
34 CoprocReg CRm, unsigned opc2) { 34 CoprocReg CRm, unsigned opc2) {
35 LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn, 35 LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn,
36 CRm, opc2); 36 CRm, opc2);
37 return {}; 37 return std::nullopt;
38} 38}
39 39
40CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, 40CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
@@ -115,7 +115,7 @@ std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_trans
115 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", 115 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
116 long_transfer ? "l" : "", CRd); 116 long_transfer ? "l" : "", CRd);
117 } 117 }
118 return {}; 118 return std::nullopt;
119} 119}
120 120
121std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, 121std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
@@ -127,7 +127,7 @@ std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_tran
127 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", 127 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
128 long_transfer ? "l" : "", CRd); 128 long_transfer ? "l" : "", CRd);
129 } 129 }
130 return {}; 130 return std::nullopt;
131} 131}
132 132
133} // namespace Core 133} // namespace Core
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 426fb6bb5..76af47ff9 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -323,7 +323,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
323 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); 323 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
324 subsection_buckets.back().entries.push_back({size, {0}, 0}); 324 subsection_buckets.back().entries.push_back({size, {0}, 0});
325 325
326 std::optional<Core::Crypto::Key128> key = {}; 326 std::optional<Core::Crypto::Key128> key;
327 if (encrypted) { 327 if (encrypted) {
328 if (has_rights_id) { 328 if (has_rights_id) {
329 status = Loader::ResultStatus::Success; 329 status = Loader::ResultStatus::Success;
@@ -442,18 +442,18 @@ std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
442 memcpy(rights_id.data(), header.rights_id.data(), 16); 442 memcpy(rights_id.data(), header.rights_id.data(), 16);
443 if (rights_id == u128{}) { 443 if (rights_id == u128{}) {
444 status = Loader::ResultStatus::ErrorInvalidRightsID; 444 status = Loader::ResultStatus::ErrorInvalidRightsID;
445 return {}; 445 return std::nullopt;
446 } 446 }
447 447
448 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); 448 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
449 if (titlekey == Core::Crypto::Key128{}) { 449 if (titlekey == Core::Crypto::Key128{}) {
450 status = Loader::ResultStatus::ErrorMissingTitlekey; 450 status = Loader::ResultStatus::ErrorMissingTitlekey;
451 return {}; 451 return std::nullopt;
452 } 452 }
453 453
454 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { 454 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
455 status = Loader::ResultStatus::ErrorMissingTitlekek; 455 status = Loader::ResultStatus::ErrorMissingTitlekek;
456 return {}; 456 return std::nullopt;
457 } 457 }
458 458
459 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( 459 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -477,7 +477,7 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
477 case NCASectionCryptoType::BKTR: 477 case NCASectionCryptoType::BKTR:
478 LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 478 LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
479 { 479 {
480 std::optional<Core::Crypto::Key128> key = {}; 480 std::optional<Core::Crypto::Key128> key;
481 if (has_rights_id) { 481 if (has_rights_id) {
482 status = Loader::ResultStatus::Success; 482 status = Loader::ResultStatus::Success;
483 key = GetTitlekey(); 483 key = GetTitlekey();
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index a08a70efd..dd779310f 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -245,9 +245,11 @@ void IPSwitchCompiler::Parse() {
245 245
246 // Read rest of patch 246 // Read rest of patch
247 while (true) { 247 while (true) {
248 if (i + 1 >= lines.size()) 248 if (i + 1 >= lines.size()) {
249 break; 249 break;
250 const auto patch_line = lines[++i]; 250 }
251
252 const auto& patch_line = lines[++i];
251 253
252 // Start of new patch 254 // Start of new patch
253 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { 255 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index b9ce93b7c..aab957bf2 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -267,9 +267,9 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
267 } 267 }
268 268
269 const CNMT cnmt(inner_file); 269 const CNMT cnmt(inner_file);
270 auto& ncas_title = ncas[cnmt.GetTitleID()];
271 270
272 ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca; 271 ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca;
272
273 for (const auto& rec : cnmt.GetContentRecords()) { 273 for (const auto& rec : cnmt.GetContentRecords()) {
274 const auto id_string = Common::HexToString(rec.nca_id, false); 274 const auto id_string = Common::HexToString(rec.nca_id, false);
275 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); 275 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -287,12 +287,12 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
287 287
288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); 288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
289 if (next_nca->GetType() == NCAContentType::Program) { 289 if (next_nca->GetType() == NCAContentType::Program) {
290 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 290 program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
291 } 291 }
292 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 292 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
293 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 293 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
294 (cnmt.GetTitleID() & 0x800) != 0)) { 294 (next_nca->GetTitleId() & 0x800) != 0)) {
295 ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca); 295 ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca);
296 } 296 }
297 } 297 }
298 298
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index a4c3f67c4..b2f026b6d 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -169,11 +169,12 @@ VfsDirectory::~VfsDirectory() = default;
169 169
170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { 170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
171 u8 out{}; 171 u8 out{};
172 std::size_t size = Read(&out, 1, offset); 172 const std::size_t size = Read(&out, sizeof(u8), offset);
173 if (size == 1) 173 if (size == 1) {
174 return out; 174 return out;
175 }
175 176
176 return {}; 177 return std::nullopt;
177} 178}
178 179
179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { 180std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index c96f88488..7714d3de5 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -58,10 +58,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
58} 58}
59 59
60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { 60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
61 if (r_offset < size) 61 if (r_offset >= size) {
62 return file->ReadByte(offset + r_offset); 62 return std::nullopt;
63 }
63 64
64 return {}; 65 return file->ReadByte(offset + r_offset);
65} 66}
66 67
67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { 68std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 9f5a90b1b..8b27c30fa 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -54,9 +54,11 @@ public:
54 } 54 }
55 55
56 std::optional<u8> ReadByte(std::size_t offset) const override { 56 std::optional<u8> ReadByte(std::size_t offset) const override {
57 if (offset < size) 57 if (offset >= size) {
58 return value; 58 return std::nullopt;
59 return {}; 59 }
60
61 return value;
60 } 62 }
61 63
62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { 64 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index d4ba88147..39bd2a45b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -265,7 +265,7 @@ std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gp
265 } 265 }
266 } 266 }
267 267
268 return {}; 268 return std::nullopt;
269} 269}
270 270
271void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, 271void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
@@ -286,7 +286,7 @@ std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
286 return size; 286 return size;
287 } 287 }
288 288
289 return {}; 289 return std::nullopt;
290} 290}
291 291
292} // namespace Service::Nvidia::Devices 292} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index d7a1bef91..7706a5590 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -54,7 +54,7 @@ struct EventInterface {
54 } 54 }
55 mask = mask >> 1; 55 mask = mask >> 1;
56 } 56 }
57 return {}; 57 return std::nullopt;
58 } 58 }
59 void SetEventStatus(const u32 event_id, EventState new_status) { 59 void SetEventStatus(const u32 event_id, EventState new_status) {
60 EventState old_status = status[event_id]; 60 EventState old_status = status[event_id];
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index f644a460d..c64673dba 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -114,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
114 [&](const VI::Display& display) { return display.GetName() == name; }); 114 [&](const VI::Display& display) { return display.GetName() == name; });
115 115
116 if (itr == displays.end()) { 116 if (itr == displays.end()) {
117 return {}; 117 return std::nullopt;
118 } 118 }
119 119
120 return itr->GetID(); 120 return itr->GetID();
@@ -124,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
124 auto* const display = FindDisplay(display_id); 124 auto* const display = FindDisplay(display_id);
125 125
126 if (display == nullptr) { 126 if (display == nullptr) {
127 return {}; 127 return std::nullopt;
128 } 128 }
129 129
130 const u64 layer_id = next_layer_id++; 130 const u64 layer_id = next_layer_id++;
@@ -144,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
144 const auto* const layer = FindLayer(display_id, layer_id); 144 const auto* const layer = FindLayer(display_id, layer_id);
145 145
146 if (layer == nullptr) { 146 if (layer == nullptr) {
147 return {}; 147 return std::nullopt;
148 } 148 }
149 149
150 return layer->GetBufferQueue().GetId(); 150 return layer->GetBufferQueue().GetId();
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 7b9dd42d8..a74be9370 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -497,7 +497,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u
497 return {0, Errno::SUCCESS}; 497 return {0, Errno::SUCCESS};
498 } 498 }
499 499
500 std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd]; 500 const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
501 if (!descriptor) { 501 if (!descriptor) {
502 LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd); 502 LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd);
503 pollfd.revents = POLL_NVAL; 503 pollfd.revents = POLL_NVAL;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 60373cc5f..1e70f6e11 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -76,16 +76,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
76 bool should_pass_arguments, bool load_into_process, 76 bool should_pass_arguments, bool load_into_process,
77 std::optional<FileSys::PatchManager> pm) { 77 std::optional<FileSys::PatchManager> pm) {
78 if (file.GetSize() < sizeof(NSOHeader)) { 78 if (file.GetSize() < sizeof(NSOHeader)) {
79 return {}; 79 return std::nullopt;
80 } 80 }
81 81
82 NSOHeader nso_header{}; 82 NSOHeader nso_header{};
83 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { 83 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
84 return {}; 84 return std::nullopt;
85 } 85 }
86 86
87 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { 87 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
88 return {}; 88 return std::nullopt;
89 } 89 }
90 90
91 // Build program image 91 // Build program image
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 86d17c6cb..c3f4829d7 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -567,7 +567,7 @@ struct Memory::Impl {
567 * @param page_table The page table to use to perform the mapping. 567 * @param page_table The page table to use to perform the mapping.
568 * @param base The base address to begin mapping at. 568 * @param base The base address to begin mapping at.
569 * @param size The total size of the range in bytes. 569 * @param size The total size of the range in bytes.
570 * @param memory The memory to map. 570 * @param target The target address to begin mapping from.
571 * @param type The page type to map the memory as. 571 * @param type The page type to map the memory as.
572 */ 572 */
573 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, 573 void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index c507c9891..89c148aba 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -15,7 +15,9 @@
15#endif 15#endif
16 16
17#include "common/logging/log.h" 17#include "common/logging/log.h"
18#include "common/param_package.h"
18#include "input_common/gcadapter/gc_adapter.h" 19#include "input_common/gcadapter/gc_adapter.h"
20#include "input_common/settings.h"
19 21
20namespace GCAdapter { 22namespace GCAdapter {
21 23
@@ -292,6 +294,92 @@ void Adapter::Reset() {
292 } 294 }
293} 295}
294 296
297std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
298 std::vector<Common::ParamPackage> devices;
299 for (std::size_t port = 0; port < state.size(); ++port) {
300 if (!DeviceConnected(port)) {
301 continue;
302 }
303 std::string name = fmt::format("Gamecube Controller {}", port);
304 devices.emplace_back(Common::ParamPackage{
305 {"class", "gcpad"},
306 {"display", std::move(name)},
307 {"port", std::to_string(port)},
308 });
309 }
310 return devices;
311}
312
313InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
314 const Common::ParamPackage& params) const {
315 // This list is missing ZL/ZR since those are not considered buttons.
316 // We will add those afterwards
317 // This list also excludes any button that can't be really mapped
318 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
319 switch_to_gcadapter_button = {
320 std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A},
321 {Settings::NativeButton::B, PadButton::PAD_BUTTON_B},
322 {Settings::NativeButton::X, PadButton::PAD_BUTTON_X},
323 {Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y},
324 {Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START},
325 {Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT},
326 {Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP},
327 {Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT},
328 {Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN},
329 {Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L},
330 {Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R},
331 {Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z},
332 };
333 if (!params.Has("port")) {
334 return {};
335 }
336
337 InputCommon::ButtonMapping mapping{};
338 for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) {
339 Common::ParamPackage button_params({{"engine", "gcpad"}});
340 button_params.Set("port", params.Get("port", 0));
341 button_params.Set("button", static_cast<int>(gcadapter_button));
342 mapping.insert_or_assign(switch_button, std::move(button_params));
343 }
344
345 // Add the missing bindings for ZL/ZR
346 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadAxes>, 2>
347 switch_to_gcadapter_axis = {
348 std::pair{Settings::NativeButton::ZL, PadAxes::TriggerLeft},
349 {Settings::NativeButton::ZR, PadAxes::TriggerRight},
350 };
351 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
352 Common::ParamPackage button_params({{"engine", "gcpad"}});
353 button_params.Set("port", params.Get("port", 0));
354 button_params.Set("button", static_cast<int>(PadButton::PAD_STICK));
355 button_params.Set("axis", static_cast<int>(gcadapter_axis));
356 mapping.insert_or_assign(switch_button, std::move(button_params));
357 }
358 return mapping;
359}
360
361InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
362 const Common::ParamPackage& params) const {
363 if (!params.Has("port")) {
364 return {};
365 }
366
367 InputCommon::AnalogMapping mapping = {};
368 Common::ParamPackage left_analog_params;
369 left_analog_params.Set("engine", "gcpad");
370 left_analog_params.Set("port", params.Get("port", 0));
371 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
372 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
373 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
374 Common::ParamPackage right_analog_params;
375 right_analog_params.Set("engine", "gcpad");
376 right_analog_params.Set("port", params.Get("port", 0));
377 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
378 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
379 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
380 return mapping;
381}
382
295bool Adapter::DeviceConnected(std::size_t port) const { 383bool Adapter::DeviceConnected(std::size_t port) const {
296 return adapter_controllers_status[port] != ControllerTypes::None; 384 return adapter_controllers_status[port] != ControllerTypes::None;
297} 385}
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index 20e97d283..75bf9fe74 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -10,6 +10,7 @@
10#include <unordered_map> 10#include <unordered_map>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/threadsafe_queue.h" 12#include "common/threadsafe_queue.h"
13#include "input_common/main.h"
13 14
14struct libusb_context; 15struct libusb_context;
15struct libusb_device; 16struct libusb_device;
@@ -75,6 +76,10 @@ public:
75 void BeginConfiguration(); 76 void BeginConfiguration();
76 void EndConfiguration(); 77 void EndConfiguration();
77 78
79 std::vector<Common::ParamPackage> GetInputDevices() const;
80 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
81 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
82
78 /// Returns true if there is a device connected to port 83 /// Returns true if there is a device connected to port
79 bool DeviceConnected(std::size_t port) const; 84 bool DeviceConnected(std::size_t port) const;
80 85
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 062ec66b5..8da829132 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -22,7 +22,7 @@ namespace InputCommon {
22 22
23struct InputSubsystem::Impl { 23struct InputSubsystem::Impl {
24 void Initialize() { 24 void Initialize() {
25 auto gcadapter = std::make_shared<GCAdapter::Adapter>(); 25 gcadapter = std::make_shared<GCAdapter::Adapter>();
26 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter); 26 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
27 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); 27 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
28 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); 28 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
@@ -82,6 +82,8 @@ struct InputSubsystem::Impl {
82#endif 82#endif
83 auto udp_devices = udp->GetInputDevices(); 83 auto udp_devices = udp->GetInputDevices();
84 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); 84 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
85 auto gcpad_devices = gcadapter->GetInputDevices();
86 devices.insert(devices.end(), gcpad_devices.begin(), gcpad_devices.end());
85 return devices; 87 return devices;
86 } 88 }
87 89
@@ -94,6 +96,9 @@ struct InputSubsystem::Impl {
94 // TODO consider returning the SDL key codes for the default keybindings 96 // TODO consider returning the SDL key codes for the default keybindings
95 return {}; 97 return {};
96 } 98 }
99 if (params.Get("class", "") == "gcpad") {
100 return gcadapter->GetAnalogMappingForDevice(params);
101 }
97#ifdef HAVE_SDL2 102#ifdef HAVE_SDL2
98 if (params.Get("class", "") == "sdl") { 103 if (params.Get("class", "") == "sdl") {
99 return sdl->GetAnalogMappingForDevice(params); 104 return sdl->GetAnalogMappingForDevice(params);
@@ -111,6 +116,9 @@ struct InputSubsystem::Impl {
111 // TODO consider returning the SDL key codes for the default keybindings 116 // TODO consider returning the SDL key codes for the default keybindings
112 return {}; 117 return {};
113 } 118 }
119 if (params.Get("class", "") == "gcpad") {
120 return gcadapter->GetButtonMappingForDevice(params);
121 }
114#ifdef HAVE_SDL2 122#ifdef HAVE_SDL2
115 if (params.Get("class", "") == "sdl") { 123 if (params.Get("class", "") == "sdl") {
116 return sdl->GetButtonMappingForDevice(params); 124 return sdl->GetButtonMappingForDevice(params);
@@ -141,6 +149,7 @@ struct InputSubsystem::Impl {
141 std::shared_ptr<UDPMotionFactory> udpmotion; 149 std::shared_ptr<UDPMotionFactory> udpmotion;
142 std::shared_ptr<UDPTouchFactory> udptouch; 150 std::shared_ptr<UDPTouchFactory> udptouch;
143 std::shared_ptr<CemuhookUDP::Client> udp; 151 std::shared_ptr<CemuhookUDP::Client> udp;
152 std::shared_ptr<GCAdapter::Adapter> gcadapter;
144}; 153};
145 154
146InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 155InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index f52b55ef3..da9e9fdda 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -190,6 +190,8 @@ if (ENABLE_VULKAN)
190 renderer_vulkan/vk_blit_screen.h 190 renderer_vulkan/vk_blit_screen.h
191 renderer_vulkan/vk_buffer_cache.cpp 191 renderer_vulkan/vk_buffer_cache.cpp
192 renderer_vulkan/vk_buffer_cache.h 192 renderer_vulkan/vk_buffer_cache.h
193 renderer_vulkan/vk_command_pool.cpp
194 renderer_vulkan/vk_command_pool.h
193 renderer_vulkan/vk_compute_pass.cpp 195 renderer_vulkan/vk_compute_pass.cpp
194 renderer_vulkan/vk_compute_pass.h 196 renderer_vulkan/vk_compute_pass.h
195 renderer_vulkan/vk_compute_pipeline.cpp 197 renderer_vulkan/vk_compute_pipeline.cpp
@@ -204,6 +206,8 @@ if (ENABLE_VULKAN)
204 renderer_vulkan/vk_graphics_pipeline.h 206 renderer_vulkan/vk_graphics_pipeline.h
205 renderer_vulkan/vk_image.cpp 207 renderer_vulkan/vk_image.cpp
206 renderer_vulkan/vk_image.h 208 renderer_vulkan/vk_image.h
209 renderer_vulkan/vk_master_semaphore.cpp
210 renderer_vulkan/vk_master_semaphore.h
207 renderer_vulkan/vk_memory_manager.cpp 211 renderer_vulkan/vk_memory_manager.cpp
208 renderer_vulkan/vk_memory_manager.h 212 renderer_vulkan/vk_memory_manager.h
209 renderer_vulkan/vk_pipeline_cache.cpp 213 renderer_vulkan/vk_pipeline_cache.cpp
@@ -214,8 +218,8 @@ if (ENABLE_VULKAN)
214 renderer_vulkan/vk_rasterizer.h 218 renderer_vulkan/vk_rasterizer.h
215 renderer_vulkan/vk_renderpass_cache.cpp 219 renderer_vulkan/vk_renderpass_cache.cpp
216 renderer_vulkan/vk_renderpass_cache.h 220 renderer_vulkan/vk_renderpass_cache.h
217 renderer_vulkan/vk_resource_manager.cpp 221 renderer_vulkan/vk_resource_pool.cpp
218 renderer_vulkan/vk_resource_manager.h 222 renderer_vulkan/vk_resource_pool.h
219 renderer_vulkan/vk_sampler_cache.cpp 223 renderer_vulkan/vk_sampler_cache.cpp
220 renderer_vulkan/vk_sampler_cache.h 224 renderer_vulkan/vk_sampler_cache.h
221 renderer_vulkan/vk_scheduler.cpp 225 renderer_vulkan/vk_scheduler.cpp
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 33854445f..57ebc785f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -597,7 +597,7 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
597 // Deferred. 597 // Deferred.
598 rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed, 598 rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
599 system.GPU().GetTicks()); 599 system.GPU().GetTicks());
600 return {}; 600 return std::nullopt;
601 default: 601 default:
602 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}", 602 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
603 static_cast<u32>(regs.query.query_get.select.Value())); 603 static_cast<u32>(regs.query.query_get.select.Value()));
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index a50e7b4e0..cd21a2112 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -36,7 +36,7 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
36 } 36 }
37 } else { 37 } else {
38 // Macro not compiled, check if it's uploaded and if so, compile it 38 // Macro not compiled, check if it's uploaded and if so, compile it
39 std::optional<u32> mid_method = std::nullopt; 39 std::optional<u32> mid_method;
40 const auto macro_code = uploaded_macro_code.find(method); 40 const auto macro_code = uploaded_macro_code.find(method);
41 if (macro_code == uploaded_macro_code.end()) { 41 if (macro_code == uploaded_macro_code.end()) {
42 for (const auto& [method_base, code] : uploaded_macro_code) { 42 for (const auto& [method_base, code] : uploaded_macro_code) {
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 16b2aaa27..02cf53d15 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -58,7 +58,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
58std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) { 58std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
59 for (u64 offset{}; offset < size; offset += page_size) { 59 for (u64 offset{}; offset < size; offset += page_size) {
60 if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) { 60 if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
61 return {}; 61 return std::nullopt;
62 } 62 }
63 } 63 }
64 64
@@ -135,13 +135,13 @@ std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size
135 } 135 }
136 } 136 }
137 137
138 return {}; 138 return std::nullopt;
139} 139}
140 140
141std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { 141std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
142 const auto page_entry{GetPageEntry(gpu_addr)}; 142 const auto page_entry{GetPageEntry(gpu_addr)};
143 if (!page_entry.IsValid()) { 143 if (!page_entry.IsValid()) {
144 return {}; 144 return std::nullopt;
145 } 145 }
146 146
147 return page_entry.ToAddress() + (gpu_addr & page_mask); 147 return page_entry.ToAddress() + (gpu_addr & page_mask);
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index d13a66bb6..fc54ca0ef 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -91,8 +91,7 @@ private:
91 std::shared_ptr<HostCounter> last; 91 std::shared_ptr<HostCounter> last;
92}; 92};
93 93
94template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter, 94template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
95 class QueryPool>
96class QueryCacheBase { 95class QueryCacheBase {
97public: 96public:
98 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_, 97 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
@@ -206,9 +205,6 @@ public:
206 committed_flushes.pop_front(); 205 committed_flushes.pop_front();
207 } 206 }
208 207
209protected:
210 std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
211
212private: 208private:
213 /// Flushes a memory range to guest memory and removes it from the cache. 209 /// Flushes a memory range to guest memory and removes it from the cache.
214 void FlushAndRemoveRegion(VAddr addr, std::size_t size) { 210 void FlushAndRemoveRegion(VAddr addr, std::size_t size) {
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index 2bb8ec2b8..1a3d9720e 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -32,10 +32,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
32 32
33QueryCache::QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d, 33QueryCache::QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
34 Tegra::MemoryManager& gpu_memory) 34 Tegra::MemoryManager& gpu_memory)
35 : VideoCommon::QueryCacheBase< 35 : VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter>(
36 QueryCache, CachedQuery, CounterStream, HostCounter, 36 rasterizer, maxwell3d, gpu_memory),
37 std::vector<OGLQuery>>{static_cast<VideoCore::RasterizerInterface&>(rasterizer),
38 maxwell3d, gpu_memory},
39 gl_rasterizer{rasterizer} {} 37 gl_rasterizer{rasterizer} {}
40 38
41QueryCache::~QueryCache() = default; 39QueryCache::~QueryCache() = default;
@@ -91,6 +89,8 @@ u64 HostCounter::BlockingQuery() const {
91CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr) 89CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr)
92 : VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {} 90 : VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {}
93 91
92CachedQuery::~CachedQuery() = default;
93
94CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept 94CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept
95 : VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {} 95 : VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {}
96 96
diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h
index dd626b66b..82cac51ee 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.h
+++ b/src/video_core/renderer_opengl/gl_query_cache.h
@@ -26,8 +26,8 @@ class RasterizerOpenGL;
26 26
27using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>; 27using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
28 28
29class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, 29class QueryCache final
30 HostCounter, std::vector<OGLQuery>> { 30 : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
31public: 31public:
32 explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d, 32 explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
33 Tegra::MemoryManager& gpu_memory); 33 Tegra::MemoryManager& gpu_memory);
@@ -41,6 +41,7 @@ public:
41 41
42private: 42private:
43 RasterizerOpenGL& gl_rasterizer; 43 RasterizerOpenGL& gl_rasterizer;
44 std::array<std::vector<OGLQuery>, VideoCore::NumQueryTypes> query_pools;
44}; 45};
45 46
46class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> { 47class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> {
@@ -63,10 +64,12 @@ class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> {
63public: 64public:
64 explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, 65 explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr,
65 u8* host_ptr); 66 u8* host_ptr);
66 CachedQuery(CachedQuery&& rhs) noexcept; 67 ~CachedQuery() override;
67 CachedQuery(const CachedQuery&) = delete;
68 68
69 CachedQuery(CachedQuery&& rhs) noexcept;
69 CachedQuery& operator=(CachedQuery&& rhs) noexcept; 70 CachedQuery& operator=(CachedQuery&& rhs) noexcept;
71
72 CachedQuery(const CachedQuery&) = delete;
70 CachedQuery& operator=(const CachedQuery&) = delete; 73 CachedQuery& operator=(const CachedQuery&) = delete;
71 74
72 void Flush() override; 75 void Flush() override;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index ce3a65122..bbb8fb095 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -813,7 +813,7 @@ private:
813 const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element); 813 const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
814 const auto it = transform_feedback.find(location); 814 const auto it = transform_feedback.find(location);
815 if (it == transform_feedback.end()) { 815 if (it == transform_feedback.end()) {
816 return {}; 816 return std::nullopt;
817 } 817 }
818 return it->second.components; 818 return it->second.components;
819 } 819 }
@@ -1295,21 +1295,21 @@ private:
1295 switch (element) { 1295 switch (element) {
1296 case 0: 1296 case 0:
1297 UNIMPLEMENTED(); 1297 UNIMPLEMENTED();
1298 return {}; 1298 return std::nullopt;
1299 case 1: 1299 case 1:
1300 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) { 1300 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
1301 return {}; 1301 return std::nullopt;
1302 } 1302 }
1303 return {{"gl_Layer", Type::Int}}; 1303 return {{"gl_Layer", Type::Int}};
1304 case 2: 1304 case 2:
1305 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) { 1305 if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
1306 return {}; 1306 return std::nullopt;
1307 } 1307 }
1308 return {{"gl_ViewportIndex", Type::Int}}; 1308 return {{"gl_ViewportIndex", Type::Int}};
1309 case 3: 1309 case 3:
1310 return {{"gl_PointSize", Type::Float}}; 1310 return {{"gl_PointSize", Type::Float}};
1311 } 1311 }
1312 return {}; 1312 return std::nullopt;
1313 case Attribute::Index::FrontColor: 1313 case Attribute::Index::FrontColor:
1314 return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}}; 1314 return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}};
1315 case Attribute::Index::FrontSecondaryColor: 1315 case Attribute::Index::FrontSecondaryColor:
@@ -1332,7 +1332,7 @@ private:
1332 Type::Float}}; 1332 Type::Float}};
1333 } 1333 }
1334 UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute)); 1334 UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
1335 return {}; 1335 return std::nullopt;
1336 } 1336 }
1337 } 1337 }
1338 1338
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d38e797a4..715182b3b 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -25,9 +25,9 @@
25#include "video_core/renderer_vulkan/renderer_vulkan.h" 25#include "video_core/renderer_vulkan/renderer_vulkan.h"
26#include "video_core/renderer_vulkan/vk_blit_screen.h" 26#include "video_core/renderer_vulkan/vk_blit_screen.h"
27#include "video_core/renderer_vulkan/vk_device.h" 27#include "video_core/renderer_vulkan/vk_device.h"
28#include "video_core/renderer_vulkan/vk_master_semaphore.h"
28#include "video_core/renderer_vulkan/vk_memory_manager.h" 29#include "video_core/renderer_vulkan/vk_memory_manager.h"
29#include "video_core/renderer_vulkan/vk_rasterizer.h" 30#include "video_core/renderer_vulkan/vk_rasterizer.h"
30#include "video_core/renderer_vulkan/vk_resource_manager.h"
31#include "video_core/renderer_vulkan/vk_scheduler.h" 31#include "video_core/renderer_vulkan/vk_scheduler.h"
32#include "video_core/renderer_vulkan/vk_state_tracker.h" 32#include "video_core/renderer_vulkan/vk_state_tracker.h"
33#include "video_core/renderer_vulkan/vk_swapchain.h" 33#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -56,7 +56,7 @@ VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
56 VkDebugUtilsMessageTypeFlagsEXT type, 56 VkDebugUtilsMessageTypeFlagsEXT type,
57 const VkDebugUtilsMessengerCallbackDataEXT* data, 57 const VkDebugUtilsMessengerCallbackDataEXT* data,
58 [[maybe_unused]] void* user_data) { 58 [[maybe_unused]] void* user_data) {
59 const char* message{data->pMessage}; 59 const char* const message{data->pMessage};
60 60
61 if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { 61 if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
62 LOG_CRITICAL(Render_Vulkan, "{}", message); 62 LOG_CRITICAL(Render_Vulkan, "{}", message);
@@ -269,11 +269,11 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
269 scheduler->WaitWorker(); 269 scheduler->WaitWorker();
270 270
271 swapchain->AcquireNextImage(); 271 swapchain->AcquireNextImage();
272 const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated); 272 const VkSemaphore render_semaphore = blit_screen->Draw(*framebuffer, use_accelerated);
273 273
274 scheduler->Flush(false, render_semaphore); 274 scheduler->Flush(render_semaphore);
275 275
276 if (swapchain->Present(render_semaphore, fence)) { 276 if (swapchain->Present(render_semaphore)) {
277 blit_screen->Recreate(); 277 blit_screen->Recreate();
278 } 278 }
279 279
@@ -295,23 +295,21 @@ bool RendererVulkan::Init() {
295 295
296 memory_manager = std::make_unique<VKMemoryManager>(*device); 296 memory_manager = std::make_unique<VKMemoryManager>(*device);
297 297
298 resource_manager = std::make_unique<VKResourceManager>(*device); 298 state_tracker = std::make_unique<StateTracker>(gpu);
299
300 scheduler = std::make_unique<VKScheduler>(*device, *state_tracker);
299 301
300 const auto& framebuffer = render_window.GetFramebufferLayout(); 302 const auto& framebuffer = render_window.GetFramebufferLayout();
301 swapchain = std::make_unique<VKSwapchain>(*surface, *device); 303 swapchain = std::make_unique<VKSwapchain>(*surface, *device, *scheduler);
302 swapchain->Create(framebuffer.width, framebuffer.height, false); 304 swapchain->Create(framebuffer.width, framebuffer.height, false);
303 305
304 state_tracker = std::make_unique<StateTracker>(gpu); 306 rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
305 307 cpu_memory, screen_info, *device,
306 scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker); 308 *memory_manager, *state_tracker, *scheduler);
307
308 rasterizer = std::make_unique<RasterizerVulkan>(
309 render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, *device,
310 *resource_manager, *memory_manager, *state_tracker, *scheduler);
311 309
312 blit_screen = std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, 310 blit_screen =
313 *resource_manager, *memory_manager, *swapchain, 311 std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
314 *scheduler, screen_info); 312 *memory_manager, *swapchain, *scheduler, screen_info);
315 313
316 return true; 314 return true;
317} 315}
@@ -329,7 +327,6 @@ void RendererVulkan::ShutDown() {
329 scheduler.reset(); 327 scheduler.reset();
330 swapchain.reset(); 328 swapchain.reset();
331 memory_manager.reset(); 329 memory_manager.reset();
332 resource_manager.reset();
333 device.reset(); 330 device.reset();
334} 331}
335 332
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 5085310d0..49a4141ec 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -30,9 +30,7 @@ namespace Vulkan {
30class StateTracker; 30class StateTracker;
31class VKBlitScreen; 31class VKBlitScreen;
32class VKDevice; 32class VKDevice;
33class VKFence;
34class VKMemoryManager; 33class VKMemoryManager;
35class VKResourceManager;
36class VKSwapchain; 34class VKSwapchain;
37class VKScheduler; 35class VKScheduler;
38class VKImage; 36class VKImage;
@@ -81,11 +79,10 @@ private:
81 79
82 vk::DebugCallback debug_callback; 80 vk::DebugCallback debug_callback;
83 std::unique_ptr<VKDevice> device; 81 std::unique_ptr<VKDevice> device;
84 std::unique_ptr<VKSwapchain> swapchain;
85 std::unique_ptr<VKMemoryManager> memory_manager; 82 std::unique_ptr<VKMemoryManager> memory_manager;
86 std::unique_ptr<VKResourceManager> resource_manager;
87 std::unique_ptr<StateTracker> state_tracker; 83 std::unique_ptr<StateTracker> state_tracker;
88 std::unique_ptr<VKScheduler> scheduler; 84 std::unique_ptr<VKScheduler> scheduler;
85 std::unique_ptr<VKSwapchain> swapchain;
89 std::unique_ptr<VKBlitScreen> blit_screen; 86 std::unique_ptr<VKBlitScreen> blit_screen;
90}; 87};
91 88
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 2bea7b24d..b5b60309e 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -12,11 +12,9 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/math_util.h" 14#include "common/math_util.h"
15
16#include "core/core.h" 15#include "core/core.h"
17#include "core/frontend/emu_window.h" 16#include "core/frontend/emu_window.h"
18#include "core/memory.h" 17#include "core/memory.h"
19
20#include "video_core/gpu.h" 18#include "video_core/gpu.h"
21#include "video_core/morton.h" 19#include "video_core/morton.h"
22#include "video_core/rasterizer_interface.h" 20#include "video_core/rasterizer_interface.h"
@@ -24,8 +22,8 @@
24#include "video_core/renderer_vulkan/vk_blit_screen.h" 22#include "video_core/renderer_vulkan/vk_blit_screen.h"
25#include "video_core/renderer_vulkan/vk_device.h" 23#include "video_core/renderer_vulkan/vk_device.h"
26#include "video_core/renderer_vulkan/vk_image.h" 24#include "video_core/renderer_vulkan/vk_image.h"
25#include "video_core/renderer_vulkan/vk_master_semaphore.h"
27#include "video_core/renderer_vulkan/vk_memory_manager.h" 26#include "video_core/renderer_vulkan/vk_memory_manager.h"
28#include "video_core/renderer_vulkan/vk_resource_manager.h"
29#include "video_core/renderer_vulkan/vk_scheduler.h" 27#include "video_core/renderer_vulkan/vk_scheduler.h"
30#include "video_core/renderer_vulkan/vk_shader_util.h" 28#include "video_core/renderer_vulkan/vk_shader_util.h"
31#include "video_core/renderer_vulkan/vk_swapchain.h" 29#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -213,16 +211,12 @@ struct VKBlitScreen::BufferData {
213VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, 211VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
214 Core::Frontend::EmuWindow& render_window_, 212 Core::Frontend::EmuWindow& render_window_,
215 VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_, 213 VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_,
216 VKResourceManager& resource_manager_, VKMemoryManager& memory_manager_, 214 VKMemoryManager& memory_manager_, VKSwapchain& swapchain_,
217 VKSwapchain& swapchain_, VKScheduler& scheduler_, 215 VKScheduler& scheduler_, const VKScreenInfo& screen_info_)
218 const VKScreenInfo& screen_info_) 216 : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_},
219 : cpu_memory{cpu_memory_}, render_window{render_window_}, 217 device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_},
220 rasterizer{rasterizer_}, device{device_}, resource_manager{resource_manager_}, 218 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
221 memory_manager{memory_manager_}, swapchain{swapchain_}, scheduler{scheduler_}, 219 resource_ticks.resize(image_count);
222 image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
223 watches.resize(image_count);
224 std::generate(watches.begin(), watches.end(),
225 []() { return std::make_unique<VKFenceWatch>(); });
226 220
227 CreateStaticResources(); 221 CreateStaticResources();
228 CreateDynamicResources(); 222 CreateDynamicResources();
@@ -234,15 +228,16 @@ void VKBlitScreen::Recreate() {
234 CreateDynamicResources(); 228 CreateDynamicResources();
235} 229}
236 230
237std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, 231VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) {
238 bool use_accelerated) {
239 RefreshResources(framebuffer); 232 RefreshResources(framebuffer);
240 233
241 // Finish any pending renderpass 234 // Finish any pending renderpass
242 scheduler.RequestOutsideRenderPassOperationContext(); 235 scheduler.RequestOutsideRenderPassOperationContext();
243 236
244 const std::size_t image_index = swapchain.GetImageIndex(); 237 const std::size_t image_index = swapchain.GetImageIndex();
245 watches[image_index]->Watch(scheduler.GetFence()); 238
239 scheduler.Wait(resource_ticks[image_index]);
240 resource_ticks[image_index] = scheduler.CurrentTick();
246 241
247 VKImage* blit_image = use_accelerated ? screen_info.image : raw_images[image_index].get(); 242 VKImage* blit_image = use_accelerated ? screen_info.image : raw_images[image_index].get();
248 243
@@ -345,7 +340,7 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
345 cmdbuf.EndRenderPass(); 340 cmdbuf.EndRenderPass();
346 }); 341 });
347 342
348 return {scheduler.GetFence(), *semaphores[image_index]}; 343 return *semaphores[image_index];
349} 344}
350 345
351void VKBlitScreen::CreateStaticResources() { 346void VKBlitScreen::CreateStaticResources() {
@@ -713,7 +708,7 @@ void VKBlitScreen::CreateFramebuffers() {
713 708
714void VKBlitScreen::ReleaseRawImages() { 709void VKBlitScreen::ReleaseRawImages() {
715 for (std::size_t i = 0; i < raw_images.size(); ++i) { 710 for (std::size_t i = 0; i < raw_images.size(); ++i) {
716 watches[i]->Wait(); 711 scheduler.Wait(resource_ticks.at(i));
717 } 712 }
718 raw_images.clear(); 713 raw_images.clear();
719 raw_buffer_commits.clear(); 714 raw_buffer_commits.clear();
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 838d38f69..8f2839214 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -5,10 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <tuple>
9 8
10#include "video_core/renderer_vulkan/vk_memory_manager.h" 9#include "video_core/renderer_vulkan/vk_memory_manager.h"
11#include "video_core/renderer_vulkan/vk_resource_manager.h"
12#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/renderer_vulkan/wrapper.h"
13 11
14namespace Core { 12namespace Core {
@@ -34,9 +32,9 @@ class RasterizerInterface;
34namespace Vulkan { 32namespace Vulkan {
35 33
36struct ScreenInfo; 34struct ScreenInfo;
35
37class RasterizerVulkan; 36class RasterizerVulkan;
38class VKDevice; 37class VKDevice;
39class VKFence;
40class VKImage; 38class VKImage;
41class VKScheduler; 39class VKScheduler;
42class VKSwapchain; 40class VKSwapchain;
@@ -46,15 +44,14 @@ public:
46 explicit VKBlitScreen(Core::Memory::Memory& cpu_memory, 44 explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
47 Core::Frontend::EmuWindow& render_window, 45 Core::Frontend::EmuWindow& render_window,
48 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, 46 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
49 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 47 VKMemoryManager& memory_manager, VKSwapchain& swapchain,
50 VKSwapchain& swapchain, VKScheduler& scheduler, 48 VKScheduler& scheduler, const VKScreenInfo& screen_info);
51 const VKScreenInfo& screen_info);
52 ~VKBlitScreen(); 49 ~VKBlitScreen();
53 50
54 void Recreate(); 51 void Recreate();
55 52
56 std::tuple<VKFence&, VkSemaphore> Draw(const Tegra::FramebufferConfig& framebuffer, 53 [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
57 bool use_accelerated); 54 bool use_accelerated);
58 55
59private: 56private:
60 struct BufferData; 57 struct BufferData;
@@ -90,7 +87,6 @@ private:
90 Core::Frontend::EmuWindow& render_window; 87 Core::Frontend::EmuWindow& render_window;
91 VideoCore::RasterizerInterface& rasterizer; 88 VideoCore::RasterizerInterface& rasterizer;
92 const VKDevice& device; 89 const VKDevice& device;
93 VKResourceManager& resource_manager;
94 VKMemoryManager& memory_manager; 90 VKMemoryManager& memory_manager;
95 VKSwapchain& swapchain; 91 VKSwapchain& swapchain;
96 VKScheduler& scheduler; 92 VKScheduler& scheduler;
@@ -111,7 +107,7 @@ private:
111 vk::Buffer buffer; 107 vk::Buffer buffer;
112 VKMemoryCommit buffer_commit; 108 VKMemoryCommit buffer_commit;
113 109
114 std::vector<std::unique_ptr<VKFenceWatch>> watches; 110 std::vector<u64> resource_ticks;
115 111
116 std::vector<vk::Semaphore> semaphores; 112 std::vector<vk::Semaphore> semaphores;
117 std::vector<std::unique_ptr<VKImage>> raw_images; 113 std::vector<std::unique_ptr<VKImage>> raw_images;
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.cpp b/src/video_core/renderer_vulkan/vk_command_pool.cpp
new file mode 100644
index 000000000..f1abd4b1a
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_command_pool.cpp
@@ -0,0 +1,41 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstddef>
6
7#include "video_core/renderer_vulkan/vk_command_pool.h"
8#include "video_core/renderer_vulkan/vk_device.h"
9#include "video_core/renderer_vulkan/wrapper.h"
10
11namespace Vulkan {
12
13constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
14
15CommandPool::CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device)
16 : ResourcePool(master_semaphore, COMMAND_BUFFER_POOL_SIZE), device{device} {}
17
18CommandPool::~CommandPool() = default;
19
20void CommandPool::Allocate(size_t begin, size_t end) {
21 // Command buffers are going to be commited, recorded, executed every single usage cycle.
22 // They are also going to be reseted when commited.
23 Pool& pool = pools.emplace_back();
24 pool.handle = device.GetLogical().CreateCommandPool({
25 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
26 .pNext = nullptr,
27 .flags =
28 VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
29 .queueFamilyIndex = device.GetGraphicsFamily(),
30 });
31 pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
32}
33
34VkCommandBuffer CommandPool::Commit() {
35 const size_t index = CommitResource();
36 const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
37 const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
38 return pools[pool_index].cmdbufs[sub_index];
39}
40
41} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.h b/src/video_core/renderer_vulkan/vk_command_pool.h
new file mode 100644
index 000000000..3aee239b9
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_command_pool.h
@@ -0,0 +1,35 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstddef>
6#include <vector>
7
8#include "video_core/renderer_vulkan/vk_resource_pool.h"
9#include "video_core/renderer_vulkan/wrapper.h"
10
11namespace Vulkan {
12
13class MasterSemaphore;
14class VKDevice;
15
16class CommandPool final : public ResourcePool {
17public:
18 explicit CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device);
19 virtual ~CommandPool();
20
21 void Allocate(size_t begin, size_t end) override;
22
23 VkCommandBuffer Commit();
24
25private:
26 struct Pool {
27 vk::CommandPool handle;
28 vk::CommandBuffers cmdbufs;
29 };
30
31 const VKDevice& device;
32 std::vector<Pool> pools;
33};
34
35} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 182461ed9..9637c6059 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -112,7 +112,8 @@ constexpr u8 quad_array[] = {
112 0xf9, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00, 112 0xf9, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00,
113 0xf9, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4e, 0x00, 0x00, 0x00, 113 0xf9, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4e, 0x00, 0x00, 0x00,
114 0xf9, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00, 114 0xf9, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00,
115 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00}; 115 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
116};
116 117
117VkDescriptorSetLayoutBinding BuildQuadArrayPassDescriptorSetLayoutBinding() { 118VkDescriptorSetLayoutBinding BuildQuadArrayPassDescriptorSetLayoutBinding() {
118 return { 119 return {
@@ -218,7 +219,8 @@ constexpr u8 uint8_pass[] = {
218 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 219 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
219 0x24, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 220 0x24, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
220 0xf9, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 221 0xf9, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00,
221 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00}; 222 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
223};
222 224
223// Quad indexed SPIR-V module. Generated from the "shaders/" directory. 225// Quad indexed SPIR-V module. Generated from the "shaders/" directory.
224constexpr u8 QUAD_INDEXED_SPV[] = { 226constexpr u8 QUAD_INDEXED_SPV[] = {
@@ -341,7 +343,8 @@ constexpr u8 QUAD_INDEXED_SPV[] = {
341 0xf9, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, 343 0xf9, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00,
342 0xf9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00, 344 0xf9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00,
343 0xf9, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 345 0xf9, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00,
344 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00}; 346 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
347};
345 348
346std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() { 349std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() {
347 return {{ 350 return {{
@@ -448,12 +451,12 @@ VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descripto
448 451
449VKComputePass::~VKComputePass() = default; 452VKComputePass::~VKComputePass() = default;
450 453
451VkDescriptorSet VKComputePass::CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue, 454VkDescriptorSet VKComputePass::CommitDescriptorSet(
452 VKFence& fence) { 455 VKUpdateDescriptorQueue& update_descriptor_queue) {
453 if (!descriptor_template) { 456 if (!descriptor_template) {
454 return nullptr; 457 return nullptr;
455 } 458 }
456 const auto set = descriptor_allocator->Commit(fence); 459 const VkDescriptorSet set = descriptor_allocator->Commit();
457 update_descriptor_queue.Send(*descriptor_template, set); 460 update_descriptor_queue.Send(*descriptor_template, set);
458 return set; 461 return set;
459} 462}
@@ -477,7 +480,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
477 480
478 update_descriptor_queue.Acquire(); 481 update_descriptor_queue.Acquire();
479 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 482 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
480 const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence()); 483 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
481 484
482 scheduler.RequestOutsideRenderPassOperationContext(); 485 scheduler.RequestOutsideRenderPassOperationContext();
483 486
@@ -520,13 +523,13 @@ Uint8Pass::~Uint8Pass() = default;
520 523
521std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, 524std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
522 u64 src_offset) { 525 u64 src_offset) {
523 const auto staging_size = static_cast<u32>(num_vertices * sizeof(u16)); 526 const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
524 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 527 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
525 528
526 update_descriptor_queue.Acquire(); 529 update_descriptor_queue.Acquire();
527 update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); 530 update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
528 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 531 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
529 const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence()); 532 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
530 533
531 scheduler.RequestOutsideRenderPassOperationContext(); 534 scheduler.RequestOutsideRenderPassOperationContext();
532 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, 535 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
@@ -589,7 +592,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
589 update_descriptor_queue.Acquire(); 592 update_descriptor_queue.Acquire();
590 update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); 593 update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
591 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 594 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
592 const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence()); 595 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
593 596
594 scheduler.RequestOutsideRenderPassOperationContext(); 597 scheduler.RequestOutsideRenderPassOperationContext();
595 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, 598 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index 230b526bc..acc94f27e 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -15,7 +15,6 @@
15namespace Vulkan { 15namespace Vulkan {
16 16
17class VKDevice; 17class VKDevice;
18class VKFence;
19class VKScheduler; 18class VKScheduler;
20class VKStagingBufferPool; 19class VKStagingBufferPool;
21class VKUpdateDescriptorQueue; 20class VKUpdateDescriptorQueue;
@@ -30,8 +29,7 @@ public:
30 ~VKComputePass(); 29 ~VKComputePass();
31 30
32protected: 31protected:
33 VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue, 32 VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue);
34 VKFence& fence);
35 33
36 vk::DescriptorUpdateTemplateKHR descriptor_template; 34 vk::DescriptorUpdateTemplateKHR descriptor_template;
37 vk::PipelineLayout layout; 35 vk::PipelineLayout layout;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index ed9d2991c..9be72dc9b 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -32,7 +32,7 @@ VkDescriptorSet VKComputePipeline::CommitDescriptorSet() {
32 if (!descriptor_template) { 32 if (!descriptor_template) {
33 return {}; 33 return {};
34 } 34 }
35 const auto set = descriptor_allocator.Commit(scheduler.GetFence()); 35 const VkDescriptorSet set = descriptor_allocator.Commit();
36 update_descriptor_queue.Send(*descriptor_template, set); 36 update_descriptor_queue.Send(*descriptor_template, set);
37 return set; 37 return set;
38} 38}
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
index ac4a0884e..f38e089d5 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -7,7 +7,8 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 8#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
9#include "video_core/renderer_vulkan/vk_device.h" 9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_resource_manager.h" 10#include "video_core/renderer_vulkan/vk_resource_pool.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h"
11#include "video_core/renderer_vulkan/wrapper.h" 12#include "video_core/renderer_vulkan/wrapper.h"
12 13
13namespace Vulkan { 14namespace Vulkan {
@@ -15,14 +16,15 @@ namespace Vulkan {
15// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines. 16// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines.
16constexpr std::size_t SETS_GROW_RATE = 0x20; 17constexpr std::size_t SETS_GROW_RATE = 0x20;
17 18
18DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool, 19DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool_,
19 VkDescriptorSetLayout layout) 20 VkDescriptorSetLayout layout_)
20 : VKFencedPool{SETS_GROW_RATE}, descriptor_pool{descriptor_pool}, layout{layout} {} 21 : ResourcePool(descriptor_pool_.master_semaphore, SETS_GROW_RATE),
22 descriptor_pool{descriptor_pool_}, layout{layout_} {}
21 23
22DescriptorAllocator::~DescriptorAllocator() = default; 24DescriptorAllocator::~DescriptorAllocator() = default;
23 25
24VkDescriptorSet DescriptorAllocator::Commit(VKFence& fence) { 26VkDescriptorSet DescriptorAllocator::Commit() {
25 const std::size_t index = CommitResource(fence); 27 const std::size_t index = CommitResource();
26 return descriptors_allocations[index / SETS_GROW_RATE][index % SETS_GROW_RATE]; 28 return descriptors_allocations[index / SETS_GROW_RATE][index % SETS_GROW_RATE];
27} 29}
28 30
@@ -30,8 +32,9 @@ void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) {
30 descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin)); 32 descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin));
31} 33}
32 34
33VKDescriptorPool::VKDescriptorPool(const VKDevice& device) 35VKDescriptorPool::VKDescriptorPool(const VKDevice& device_, VKScheduler& scheduler)
34 : device{device}, active_pool{AllocateNewPool()} {} 36 : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{
37 AllocateNewPool()} {}
35 38
36VKDescriptorPool::~VKDescriptorPool() = default; 39VKDescriptorPool::~VKDescriptorPool() = default;
37 40
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
index 9efa66bef..544f32a20 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
@@ -6,21 +6,24 @@
6 6
7#include <vector> 7#include <vector>
8 8
9#include "video_core/renderer_vulkan/vk_resource_manager.h" 9#include "video_core/renderer_vulkan/vk_resource_pool.h"
10#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/renderer_vulkan/wrapper.h"
11 11
12namespace Vulkan { 12namespace Vulkan {
13 13
14class VKDevice;
14class VKDescriptorPool; 15class VKDescriptorPool;
16class VKScheduler;
15 17
16class DescriptorAllocator final : public VKFencedPool { 18class DescriptorAllocator final : public ResourcePool {
17public: 19public:
18 explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, VkDescriptorSetLayout layout); 20 explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, VkDescriptorSetLayout layout);
19 ~DescriptorAllocator() override; 21 ~DescriptorAllocator() override;
20 22
23 DescriptorAllocator& operator=(const DescriptorAllocator&) = delete;
21 DescriptorAllocator(const DescriptorAllocator&) = delete; 24 DescriptorAllocator(const DescriptorAllocator&) = delete;
22 25
23 VkDescriptorSet Commit(VKFence& fence); 26 VkDescriptorSet Commit();
24 27
25protected: 28protected:
26 void Allocate(std::size_t begin, std::size_t end) override; 29 void Allocate(std::size_t begin, std::size_t end) override;
@@ -36,15 +39,19 @@ class VKDescriptorPool final {
36 friend DescriptorAllocator; 39 friend DescriptorAllocator;
37 40
38public: 41public:
39 explicit VKDescriptorPool(const VKDevice& device); 42 explicit VKDescriptorPool(const VKDevice& device, VKScheduler& scheduler);
40 ~VKDescriptorPool(); 43 ~VKDescriptorPool();
41 44
45 VKDescriptorPool(const VKDescriptorPool&) = delete;
46 VKDescriptorPool& operator=(const VKDescriptorPool&) = delete;
47
42private: 48private:
43 vk::DescriptorPool* AllocateNewPool(); 49 vk::DescriptorPool* AllocateNewPool();
44 50
45 vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count); 51 vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count);
46 52
47 const VKDevice& device; 53 const VKDevice& device;
54 MasterSemaphore& master_semaphore;
48 55
49 std::vector<vk::DescriptorPool> pools; 56 std::vector<vk::DescriptorPool> pools;
50 vk::DescriptorPool* active_pool; 57 vk::DescriptorPool* active_pool;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 4205bd573..05e31f1de 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -42,6 +42,7 @@ constexpr std::array REQUIRED_EXTENSIONS{
42 VK_KHR_8BIT_STORAGE_EXTENSION_NAME, 42 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
43 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, 43 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
44 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, 44 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
45 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
45 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, 46 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
46 VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, 47 VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
47 VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, 48 VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
@@ -250,6 +251,13 @@ bool VKDevice::Create() {
250 .inheritedQueries = false, 251 .inheritedQueries = false,
251 }; 252 };
252 253
254 VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{
255 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR,
256 .pNext = nullptr,
257 .timelineSemaphore = true,
258 };
259 SetNext(next, timeline_semaphore);
260
253 VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{ 261 VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{
254 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, 262 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
255 .pNext = nullptr, 263 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index 55a8348fc..5babbdd0b 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -29,8 +29,8 @@ void InnerFence::Queue() {
29 } 29 }
30 ASSERT(!event); 30 ASSERT(!event);
31 31
32 event = device.GetLogical().CreateNewEvent(); 32 event = device.GetLogical().CreateEvent();
33 ticks = scheduler.Ticks(); 33 ticks = scheduler.CurrentTick();
34 34
35 scheduler.RequestOutsideRenderPassOperationContext(); 35 scheduler.RequestOutsideRenderPassOperationContext();
36 scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) { 36 scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
@@ -52,7 +52,7 @@ void InnerFence::Wait() {
52 } 52 }
53 ASSERT(event); 53 ASSERT(event);
54 54
55 if (ticks >= scheduler.Ticks()) { 55 if (ticks >= scheduler.CurrentTick()) {
56 scheduler.Flush(); 56 scheduler.Flush();
57 } 57 }
58 while (!IsEventSignalled()) { 58 while (!IsEventSignalled()) {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 2e46c6278..a4b9e7ef5 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -93,7 +93,7 @@ VkDescriptorSet VKGraphicsPipeline::CommitDescriptorSet() {
93 if (!descriptor_template) { 93 if (!descriptor_template) {
94 return {}; 94 return {};
95 } 95 }
96 const auto set = descriptor_allocator.Commit(scheduler.GetFence()); 96 const VkDescriptorSet set = descriptor_allocator.Commit();
97 update_descriptor_queue.Send(*descriptor_template, set); 97 update_descriptor_queue.Send(*descriptor_template, set);
98 return set; 98 return set;
99} 99}
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
new file mode 100644
index 000000000..ae26e558d
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -0,0 +1,56 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <chrono>
7
8#include "core/settings.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_master_semaphore.h"
11#include "video_core/renderer_vulkan/wrapper.h"
12
13namespace Vulkan {
14
15using namespace std::chrono_literals;
16
17MasterSemaphore::MasterSemaphore(const VKDevice& device) {
18 static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{
19 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR,
20 .pNext = nullptr,
21 .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR,
22 .initialValue = 0,
23 };
24 static constexpr VkSemaphoreCreateInfo semaphore_ci{
25 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
26 .pNext = &semaphore_type_ci,
27 .flags = 0,
28 };
29 semaphore = device.GetLogical().CreateSemaphore(semaphore_ci);
30
31 if (!Settings::values.renderer_debug) {
32 return;
33 }
34 // Validation layers have a bug where they fail to track resource usage when using timeline
35 // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have
36 // a separate thread waiting for each timeline semaphore value.
37 debug_thread = std::thread([this] {
38 u64 counter = 0;
39 while (!shutdown) {
40 if (semaphore.Wait(counter, 10'000'000)) {
41 ++counter;
42 }
43 }
44 });
45}
46
47MasterSemaphore::~MasterSemaphore() {
48 shutdown = true;
49
50 // This thread might not be started
51 if (debug_thread.joinable()) {
52 debug_thread.join();
53 }
54}
55
56} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
new file mode 100644
index 000000000..0e93706d7
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -0,0 +1,70 @@
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 <atomic>
8#include <thread>
9
10#include "common/common_types.h"
11#include "video_core/renderer_vulkan/wrapper.h"
12
13namespace Vulkan {
14
15class VKDevice;
16
17class MasterSemaphore {
18public:
19 explicit MasterSemaphore(const VKDevice& device);
20 ~MasterSemaphore();
21
22 /// Returns the current logical tick.
23 [[nodiscard]] u64 CurrentTick() const noexcept {
24 return current_tick;
25 }
26
27 /// Returns the timeline semaphore handle.
28 [[nodiscard]] VkSemaphore Handle() const noexcept {
29 return *semaphore;
30 }
31
32 /// Returns true when a tick has been hit by the GPU.
33 [[nodiscard]] bool IsFree(u64 tick) {
34 return gpu_tick >= tick;
35 }
36
37 /// Advance to the logical tick.
38 void NextTick() noexcept {
39 ++current_tick;
40 }
41
42 /// Refresh the known GPU tick
43 void Refresh() {
44 gpu_tick = semaphore.GetCounter();
45 }
46
47 /// Waits for a tick to be hit on the GPU
48 void Wait(u64 tick) {
49 // No need to wait if the GPU is ahead of the tick
50 if (IsFree(tick)) {
51 return;
52 }
53 // Update the GPU tick and try again
54 Refresh();
55 if (IsFree(tick)) {
56 return;
57 }
58 // If none of the above is hit, fallback to a regular wait
59 semaphore.Wait(tick);
60 }
61
62private:
63 vk::Semaphore semaphore; ///< Timeline semaphore.
64 std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
65 std::atomic<u64> current_tick{1}; ///< Current logical tick.
66 std::atomic<bool> shutdown{false}; ///< True when the object is being destroyed.
67 std::thread debug_thread; ///< Debug thread to workaround validation layer bugs.
68};
69
70} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 1a31fd9f6..e558e6658 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -38,7 +38,6 @@ class RasterizerVulkan;
38class VKComputePipeline; 38class VKComputePipeline;
39class VKDescriptorPool; 39class VKDescriptorPool;
40class VKDevice; 40class VKDevice;
41class VKFence;
42class VKScheduler; 41class VKScheduler;
43class VKUpdateDescriptorQueue; 42class VKUpdateDescriptorQueue;
44 43
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 5a97c959d..ee2d871e3 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -9,35 +9,33 @@
9 9
10#include "video_core/renderer_vulkan/vk_device.h" 10#include "video_core/renderer_vulkan/vk_device.h"
11#include "video_core/renderer_vulkan/vk_query_cache.h" 11#include "video_core/renderer_vulkan/vk_query_cache.h"
12#include "video_core/renderer_vulkan/vk_resource_manager.h" 12#include "video_core/renderer_vulkan/vk_resource_pool.h"
13#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/renderer_vulkan/wrapper.h"
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
18using VideoCore::QueryType;
19
18namespace { 20namespace {
19 21
20constexpr std::array QUERY_TARGETS = {VK_QUERY_TYPE_OCCLUSION}; 22constexpr std::array QUERY_TARGETS = {VK_QUERY_TYPE_OCCLUSION};
21 23
22constexpr VkQueryType GetTarget(VideoCore::QueryType type) { 24constexpr VkQueryType GetTarget(QueryType type) {
23 return QUERY_TARGETS[static_cast<std::size_t>(type)]; 25 return QUERY_TARGETS[static_cast<std::size_t>(type)];
24} 26}
25 27
26} // Anonymous namespace 28} // Anonymous namespace
27 29
28QueryPool::QueryPool() : VKFencedPool{GROW_STEP} {} 30QueryPool::QueryPool(const VKDevice& device_, VKScheduler& scheduler, QueryType type_)
31 : ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {}
29 32
30QueryPool::~QueryPool() = default; 33QueryPool::~QueryPool() = default;
31 34
32void QueryPool::Initialize(const VKDevice& device_, VideoCore::QueryType type_) { 35std::pair<VkQueryPool, u32> QueryPool::Commit() {
33 device = &device_;
34 type = type_;
35}
36
37std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) {
38 std::size_t index; 36 std::size_t index;
39 do { 37 do {
40 index = CommitResource(fence); 38 index = CommitResource();
41 } while (usage[index]); 39 } while (usage[index]);
42 usage[index] = true; 40 usage[index] = true;
43 41
@@ -47,7 +45,7 @@ std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) {
47void QueryPool::Allocate(std::size_t begin, std::size_t end) { 45void QueryPool::Allocate(std::size_t begin, std::size_t end) {
48 usage.resize(end); 46 usage.resize(end);
49 47
50 pools.push_back(device->GetLogical().CreateQueryPool({ 48 pools.push_back(device.GetLogical().CreateQueryPool({
51 .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, 49 .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
52 .pNext = nullptr, 50 .pNext = nullptr,
53 .flags = 0, 51 .flags = 0,
@@ -71,28 +69,36 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
71VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer, 69VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
72 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, 70 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
73 const VKDevice& device, VKScheduler& scheduler) 71 const VKDevice& device, VKScheduler& scheduler)
74 : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, 72 : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream,
75 QueryPool>{rasterizer, maxwell3d, gpu_memory}, 73 HostCounter>{rasterizer, maxwell3d, gpu_memory},
76 device{device}, scheduler{scheduler} { 74 device{device}, scheduler{scheduler}, query_pools{
77 for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) { 75 QueryPool{device, scheduler,
78 query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i)); 76 QueryType::SamplesPassed},
77 } {}
78
79VKQueryCache::~VKQueryCache() {
80 // TODO(Rodrigo): This is a hack to destroy all HostCounter instances before the base class
81 // destructor is called. The query cache should be redesigned to have a proper ownership model
82 // instead of using shared pointers.
83 for (size_t query_type = 0; query_type < VideoCore::NumQueryTypes; ++query_type) {
84 auto& stream = Stream(static_cast<QueryType>(query_type));
85 stream.Update(false);
86 stream.Reset();
79 } 87 }
80} 88}
81 89
82VKQueryCache::~VKQueryCache() = default; 90std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(QueryType type) {
83 91 return query_pools[static_cast<std::size_t>(type)].Commit();
84std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(VideoCore::QueryType type) {
85 return query_pools[static_cast<std::size_t>(type)].Commit(scheduler.GetFence());
86} 92}
87 93
88void VKQueryCache::Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query) { 94void VKQueryCache::Reserve(QueryType type, std::pair<VkQueryPool, u32> query) {
89 query_pools[static_cast<std::size_t>(type)].Reserve(query); 95 query_pools[static_cast<std::size_t>(type)].Reserve(query);
90} 96}
91 97
92HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency, 98HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency,
93 VideoCore::QueryType type) 99 QueryType type)
94 : VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache}, 100 : VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache},
95 type{type}, query{cache.AllocateQuery(type)}, ticks{cache.Scheduler().Ticks()} { 101 type{type}, query{cache.AllocateQuery(type)}, tick{cache.Scheduler().CurrentTick()} {
96 const vk::Device* logical = &cache.Device().GetLogical(); 102 const vk::Device* logical = &cache.Device().GetLogical();
97 cache.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { 103 cache.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
98 logical->ResetQueryPoolEXT(query.first, query.second, 1); 104 logical->ResetQueryPoolEXT(query.first, query.second, 1);
@@ -110,7 +116,7 @@ void HostCounter::EndQuery() {
110} 116}
111 117
112u64 HostCounter::BlockingQuery() const { 118u64 HostCounter::BlockingQuery() const {
113 if (ticks >= cache.Scheduler().Ticks()) { 119 if (tick >= cache.Scheduler().CurrentTick()) {
114 cache.Scheduler().Flush(); 120 cache.Scheduler().Flush();
115 } 121 }
116 u64 data; 122 u64 data;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index 9be996e55..2e57fb75d 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -11,7 +11,7 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "video_core/query_cache.h" 13#include "video_core/query_cache.h"
14#include "video_core/renderer_vulkan/vk_resource_manager.h" 14#include "video_core/renderer_vulkan/vk_resource_pool.h"
15#include "video_core/renderer_vulkan/wrapper.h" 15#include "video_core/renderer_vulkan/wrapper.h"
16 16
17namespace VideoCore { 17namespace VideoCore {
@@ -28,14 +28,12 @@ class VKScheduler;
28 28
29using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>; 29using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>;
30 30
31class QueryPool final : public VKFencedPool { 31class QueryPool final : public ResourcePool {
32public: 32public:
33 explicit QueryPool(); 33 explicit QueryPool(const VKDevice& device, VKScheduler& scheduler, VideoCore::QueryType type);
34 ~QueryPool() override; 34 ~QueryPool() override;
35 35
36 void Initialize(const VKDevice& device, VideoCore::QueryType type); 36 std::pair<VkQueryPool, u32> Commit();
37
38 std::pair<VkQueryPool, u32> Commit(VKFence& fence);
39 37
40 void Reserve(std::pair<VkQueryPool, u32> query); 38 void Reserve(std::pair<VkQueryPool, u32> query);
41 39
@@ -45,16 +43,15 @@ protected:
45private: 43private:
46 static constexpr std::size_t GROW_STEP = 512; 44 static constexpr std::size_t GROW_STEP = 512;
47 45
48 const VKDevice* device = nullptr; 46 const VKDevice& device;
49 VideoCore::QueryType type = {}; 47 const VideoCore::QueryType type;
50 48
51 std::vector<vk::QueryPool> pools; 49 std::vector<vk::QueryPool> pools;
52 std::vector<bool> usage; 50 std::vector<bool> usage;
53}; 51};
54 52
55class VKQueryCache final 53class VKQueryCache final
56 : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, 54 : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter> {
57 QueryPool> {
58public: 55public:
59 explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer, 56 explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
60 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, 57 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
@@ -76,6 +73,7 @@ public:
76private: 73private:
77 const VKDevice& device; 74 const VKDevice& device;
78 VKScheduler& scheduler; 75 VKScheduler& scheduler;
76 std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
79}; 77};
80 78
81class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> { 79class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> {
@@ -92,7 +90,7 @@ private:
92 VKQueryCache& cache; 90 VKQueryCache& cache;
93 const VideoCore::QueryType type; 91 const VideoCore::QueryType type;
94 const std::pair<VkQueryPool, u32> query; 92 const std::pair<VkQueryPool, u32> query;
95 const u64 ticks; 93 const u64 tick;
96}; 94};
97 95
98class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> { 96class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index bafebe294..f3c2483c8 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -31,7 +31,6 @@
31#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 31#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
32#include "video_core/renderer_vulkan/vk_rasterizer.h" 32#include "video_core/renderer_vulkan/vk_rasterizer.h"
33#include "video_core/renderer_vulkan/vk_renderpass_cache.h" 33#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
34#include "video_core/renderer_vulkan/vk_resource_manager.h"
35#include "video_core/renderer_vulkan/vk_sampler_cache.h" 34#include "video_core/renderer_vulkan/vk_sampler_cache.h"
36#include "video_core/renderer_vulkan/vk_scheduler.h" 35#include "video_core/renderer_vulkan/vk_scheduler.h"
37#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 36#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -384,27 +383,25 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
384RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_, 383RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_,
385 Tegra::MemoryManager& gpu_memory_, 384 Tegra::MemoryManager& gpu_memory_,
386 Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_, 385 Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_,
387 const VKDevice& device_, VKResourceManager& resource_manager_, 386 const VKDevice& device_, VKMemoryManager& memory_manager_,
388 VKMemoryManager& memory_manager_, StateTracker& state_tracker_, 387 StateTracker& state_tracker_, VKScheduler& scheduler_)
389 VKScheduler& scheduler_)
390 : RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_), 388 : RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_),
391 maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_), 389 maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_),
392 device(device_), resource_manager(resource_manager_), memory_manager(memory_manager_), 390 device(device_), memory_manager(memory_manager_), state_tracker(state_tracker_),
393 state_tracker(state_tracker_), scheduler(scheduler_), 391 scheduler(scheduler_), staging_pool(device, memory_manager, scheduler),
394 staging_pool(device, memory_manager, scheduler), descriptor_pool(device), 392 descriptor_pool(device, scheduler_), update_descriptor_queue(device, scheduler),
395 update_descriptor_queue(device, scheduler), renderpass_cache(device), 393 renderpass_cache(device),
396 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 394 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
397 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 395 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
398 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 396 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
399 texture_cache(*this, maxwell3d, gpu_memory, device, resource_manager, memory_manager, 397 texture_cache(*this, maxwell3d, gpu_memory, device, memory_manager, scheduler, staging_pool),
400 scheduler, staging_pool),
401 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, 398 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
402 descriptor_pool, update_descriptor_queue, renderpass_cache), 399 descriptor_pool, update_descriptor_queue, renderpass_cache),
403 buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool), 400 buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool),
404 sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler), 401 sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler),
405 fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device, 402 fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device,
406 scheduler), 403 scheduler),
407 wfi_event(device.GetLogical().CreateNewEvent()), async_shaders(emu_window) { 404 wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window) {
408 scheduler.SetQueryCache(query_cache); 405 scheduler.SetQueryCache(query_cache);
409 if (device.UseAsynchronousShaders()) { 406 if (device.UseAsynchronousShaders()) {
410 async_shaders.AllocateWorkers(); 407 async_shaders.AllocateWorkers();
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 16251d0f6..b47c8fc13 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -25,7 +25,6 @@
25#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 25#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
26#include "video_core/renderer_vulkan/vk_query_cache.h" 26#include "video_core/renderer_vulkan/vk_query_cache.h"
27#include "video_core/renderer_vulkan/vk_renderpass_cache.h" 27#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
28#include "video_core/renderer_vulkan/vk_resource_manager.h"
29#include "video_core/renderer_vulkan/vk_sampler_cache.h" 28#include "video_core/renderer_vulkan/vk_sampler_cache.h"
30#include "video_core/renderer_vulkan/vk_scheduler.h" 29#include "video_core/renderer_vulkan/vk_scheduler.h"
31#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 30#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -109,8 +108,8 @@ public:
109 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu, 108 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
110 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory, 109 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
111 VKScreenInfo& screen_info, const VKDevice& device, 110 VKScreenInfo& screen_info, const VKDevice& device,
112 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 111 VKMemoryManager& memory_manager, StateTracker& state_tracker,
113 StateTracker& state_tracker, VKScheduler& scheduler); 112 VKScheduler& scheduler);
114 ~RasterizerVulkan() override; 113 ~RasterizerVulkan() override;
115 114
116 void Draw(bool is_indexed, bool is_instanced) override; 115 void Draw(bool is_indexed, bool is_instanced) override;
@@ -286,7 +285,6 @@ private:
286 285
287 VKScreenInfo& screen_info; 286 VKScreenInfo& screen_info;
288 const VKDevice& device; 287 const VKDevice& device;
289 VKResourceManager& resource_manager;
290 VKMemoryManager& memory_manager; 288 VKMemoryManager& memory_manager;
291 StateTracker& state_tracker; 289 StateTracker& state_tracker;
292 VKScheduler& scheduler; 290 VKScheduler& scheduler;
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.cpp b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
deleted file mode 100644
index f19330a36..000000000
--- a/src/video_core/renderer_vulkan/vk_resource_manager.cpp
+++ /dev/null
@@ -1,311 +0,0 @@
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 <algorithm>
6#include <optional>
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_resource_manager.h"
11#include "video_core/renderer_vulkan/wrapper.h"
12
13namespace Vulkan {
14
15namespace {
16
17// TODO(Rodrigo): Fine tune these numbers.
18constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
19constexpr std::size_t FENCES_GROW_STEP = 0x40;
20
21constexpr VkFenceCreateInfo BuildFenceCreateInfo() {
22 return {
23 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
24 .pNext = nullptr,
25 .flags = 0,
26 };
27}
28
29} // Anonymous namespace
30
31class CommandBufferPool final : public VKFencedPool {
32public:
33 explicit CommandBufferPool(const VKDevice& device)
34 : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {}
35
36 void Allocate(std::size_t begin, std::size_t end) override {
37 // Command buffers are going to be commited, recorded, executed every single usage cycle.
38 // They are also going to be reseted when commited.
39 Pool& pool = pools.emplace_back();
40 pool.handle = device.GetLogical().CreateCommandPool({
41 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
42 .pNext = nullptr,
43 .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
44 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
45 .queueFamilyIndex = device.GetGraphicsFamily(),
46 });
47 pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
48 }
49
50 VkCommandBuffer Commit(VKFence& fence) {
51 const std::size_t index = CommitResource(fence);
52 const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
53 const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
54 return pools[pool_index].cmdbufs[sub_index];
55 }
56
57private:
58 struct Pool {
59 vk::CommandPool handle;
60 vk::CommandBuffers cmdbufs;
61 };
62
63 const VKDevice& device;
64 std::vector<Pool> pools;
65};
66
67VKResource::VKResource() = default;
68
69VKResource::~VKResource() = default;
70
71VKFence::VKFence(const VKDevice& device)
72 : device{device}, handle{device.GetLogical().CreateFence(BuildFenceCreateInfo())} {}
73
74VKFence::~VKFence() = default;
75
76void VKFence::Wait() {
77 switch (const VkResult result = handle.Wait()) {
78 case VK_SUCCESS:
79 return;
80 case VK_ERROR_DEVICE_LOST:
81 device.ReportLoss();
82 [[fallthrough]];
83 default:
84 throw vk::Exception(result);
85 }
86}
87
88void VKFence::Release() {
89 ASSERT(is_owned);
90 is_owned = false;
91}
92
93void VKFence::Commit() {
94 is_owned = true;
95 is_used = true;
96}
97
98bool VKFence::Tick(bool gpu_wait, bool owner_wait) {
99 if (!is_used) {
100 // If a fence is not used it's always free.
101 return true;
102 }
103 if (is_owned && !owner_wait) {
104 // The fence is still being owned (Release has not been called) and ownership wait has
105 // not been asked.
106 return false;
107 }
108
109 if (gpu_wait) {
110 // Wait for the fence if it has been requested.
111 (void)handle.Wait();
112 } else {
113 if (handle.GetStatus() != VK_SUCCESS) {
114 // Vulkan fence is not ready, not much it can do here
115 return false;
116 }
117 }
118
119 // Broadcast resources their free state.
120 for (auto* resource : protected_resources) {
121 resource->OnFenceRemoval(this);
122 }
123 protected_resources.clear();
124
125 // Prepare fence for reusage.
126 handle.Reset();
127 is_used = false;
128 return true;
129}
130
131void VKFence::Protect(VKResource* resource) {
132 protected_resources.push_back(resource);
133}
134
135void VKFence::Unprotect(VKResource* resource) {
136 const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource);
137 ASSERT(it != protected_resources.end());
138
139 resource->OnFenceRemoval(this);
140 protected_resources.erase(it);
141}
142
143void VKFence::RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept {
144 std::replace(std::begin(protected_resources), std::end(protected_resources), old_resource,
145 new_resource);
146}
147
148VKFenceWatch::VKFenceWatch() = default;
149
150VKFenceWatch::VKFenceWatch(VKFence& initial_fence) {
151 Watch(initial_fence);
152}
153
154VKFenceWatch::VKFenceWatch(VKFenceWatch&& rhs) noexcept {
155 fence = std::exchange(rhs.fence, nullptr);
156 if (fence) {
157 fence->RedirectProtection(&rhs, this);
158 }
159}
160
161VKFenceWatch& VKFenceWatch::operator=(VKFenceWatch&& rhs) noexcept {
162 fence = std::exchange(rhs.fence, nullptr);
163 if (fence) {
164 fence->RedirectProtection(&rhs, this);
165 }
166 return *this;
167}
168
169VKFenceWatch::~VKFenceWatch() {
170 if (fence) {
171 fence->Unprotect(this);
172 }
173}
174
175void VKFenceWatch::Wait() {
176 if (fence == nullptr) {
177 return;
178 }
179 fence->Wait();
180 fence->Unprotect(this);
181}
182
183void VKFenceWatch::Watch(VKFence& new_fence) {
184 Wait();
185 fence = &new_fence;
186 fence->Protect(this);
187}
188
189bool VKFenceWatch::TryWatch(VKFence& new_fence) {
190 if (fence) {
191 return false;
192 }
193 fence = &new_fence;
194 fence->Protect(this);
195 return true;
196}
197
198void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) {
199 ASSERT_MSG(signaling_fence == fence, "Removing the wrong fence");
200 fence = nullptr;
201}
202
203VKFencedPool::VKFencedPool(std::size_t grow_step) : grow_step{grow_step} {}
204
205VKFencedPool::~VKFencedPool() = default;
206
207std::size_t VKFencedPool::CommitResource(VKFence& fence) {
208 const auto Search = [&](std::size_t begin, std::size_t end) -> std::optional<std::size_t> {
209 for (std::size_t iterator = begin; iterator < end; ++iterator) {
210 if (watches[iterator]->TryWatch(fence)) {
211 // The resource is now being watched, a free resource was successfully found.
212 return iterator;
213 }
214 }
215 return {};
216 };
217 // Try to find a free resource from the hinted position to the end.
218 auto found = Search(free_iterator, watches.size());
219 if (!found) {
220 // Search from beginning to the hinted position.
221 found = Search(0, free_iterator);
222 if (!found) {
223 // Both searches failed, the pool is full; handle it.
224 const std::size_t free_resource = ManageOverflow();
225
226 // Watch will wait for the resource to be free.
227 watches[free_resource]->Watch(fence);
228 found = free_resource;
229 }
230 }
231 // Free iterator is hinted to the resource after the one that's been commited.
232 free_iterator = (*found + 1) % watches.size();
233 return *found;
234}
235
236std::size_t VKFencedPool::ManageOverflow() {
237 const std::size_t old_capacity = watches.size();
238 Grow();
239
240 // The last entry is guaranted to be free, since it's the first element of the freshly
241 // allocated resources.
242 return old_capacity;
243}
244
245void VKFencedPool::Grow() {
246 const std::size_t old_capacity = watches.size();
247 watches.resize(old_capacity + grow_step);
248 std::generate(watches.begin() + old_capacity, watches.end(),
249 []() { return std::make_unique<VKFenceWatch>(); });
250 Allocate(old_capacity, old_capacity + grow_step);
251}
252
253VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} {
254 GrowFences(FENCES_GROW_STEP);
255 command_buffer_pool = std::make_unique<CommandBufferPool>(device);
256}
257
258VKResourceManager::~VKResourceManager() = default;
259
260VKFence& VKResourceManager::CommitFence() {
261 const auto StepFences = [&](bool gpu_wait, bool owner_wait) -> VKFence* {
262 const auto Tick = [=](auto& fence) { return fence->Tick(gpu_wait, owner_wait); };
263 const auto hinted = fences.begin() + fences_iterator;
264
265 auto it = std::find_if(hinted, fences.end(), Tick);
266 if (it == fences.end()) {
267 it = std::find_if(fences.begin(), hinted, Tick);
268 if (it == hinted) {
269 return nullptr;
270 }
271 }
272 fences_iterator = std::distance(fences.begin(), it) + 1;
273 if (fences_iterator >= fences.size())
274 fences_iterator = 0;
275
276 auto& fence = *it;
277 fence->Commit();
278 return fence.get();
279 };
280
281 VKFence* found_fence = StepFences(false, false);
282 if (!found_fence) {
283 // Try again, this time waiting.
284 found_fence = StepFences(true, false);
285
286 if (!found_fence) {
287 // Allocate new fences and try again.
288 LOG_INFO(Render_Vulkan, "Allocating new fences {} -> {}", fences.size(),
289 fences.size() + FENCES_GROW_STEP);
290
291 GrowFences(FENCES_GROW_STEP);
292 found_fence = StepFences(true, false);
293 ASSERT(found_fence != nullptr);
294 }
295 }
296 return *found_fence;
297}
298
299VkCommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) {
300 return command_buffer_pool->Commit(fence);
301}
302
303void VKResourceManager::GrowFences(std::size_t new_fences_count) {
304 const std::size_t previous_size = fences.size();
305 fences.resize(previous_size + new_fences_count);
306
307 std::generate(fences.begin() + previous_size, fences.end(),
308 [this] { return std::make_unique<VKFence>(device); });
309}
310
311} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.h b/src/video_core/renderer_vulkan/vk_resource_manager.h
deleted file mode 100644
index f683d2276..000000000
--- a/src/video_core/renderer_vulkan/vk_resource_manager.h
+++ /dev/null
@@ -1,196 +0,0 @@
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 <cstddef>
8#include <memory>
9#include <vector>
10#include "video_core/renderer_vulkan/wrapper.h"
11
12namespace Vulkan {
13
14class VKDevice;
15class VKFence;
16class VKResourceManager;
17
18class CommandBufferPool;
19
20/// Interface for a Vulkan resource
21class VKResource {
22public:
23 explicit VKResource();
24 virtual ~VKResource();
25
26 /**
27 * Signals the object that an owning fence has been signaled.
28 * @param signaling_fence Fence that signals its usage end.
29 */
30 virtual void OnFenceRemoval(VKFence* signaling_fence) = 0;
31};
32
33/**
34 * Fences take ownership of objects, protecting them from GPU-side or driver-side concurrent access.
35 * They must be commited from the resource manager. Their usage flow is: commit the fence from the
36 * resource manager, protect resources with it and use them, send the fence to an execution queue
37 * and Wait for it if needed and then call Release. Used resources will automatically be signaled
38 * when they are free to be reused.
39 * @brief Protects resources for concurrent usage and signals its release.
40 */
41class VKFence {
42 friend class VKResourceManager;
43
44public:
45 explicit VKFence(const VKDevice& device);
46 ~VKFence();
47
48 /**
49 * Waits for the fence to be signaled.
50 * @warning You must have ownership of the fence and it has to be previously sent to a queue to
51 * call this function.
52 */
53 void Wait();
54
55 /**
56 * Releases ownership of the fence. Pass after it has been sent to an execution queue.
57 * Unmanaged usage of the fence after the call will result in undefined behavior because it may
58 * be being used for something else.
59 */
60 void Release();
61
62 /// Protects a resource with this fence.
63 void Protect(VKResource* resource);
64
65 /// Removes protection for a resource.
66 void Unprotect(VKResource* resource);
67
68 /// Redirects one protected resource to a new address.
69 void RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept;
70
71 /// Retreives the fence.
72 operator VkFence() const {
73 return *handle;
74 }
75
76private:
77 /// Take ownership of the fence.
78 void Commit();
79
80 /**
81 * Updates the fence status.
82 * @warning Waiting for the owner might soft lock the execution.
83 * @param gpu_wait Wait for the fence to be signaled by the driver.
84 * @param owner_wait Wait for the owner to signal its freedom.
85 * @returns True if the fence is free. Waiting for gpu and owner will always return true.
86 */
87 bool Tick(bool gpu_wait, bool owner_wait);
88
89 const VKDevice& device; ///< Device handler
90 vk::Fence handle; ///< Vulkan fence
91 std::vector<VKResource*> protected_resources; ///< List of resources protected by this fence
92 bool is_owned = false; ///< The fence has been commited but not released yet.
93 bool is_used = false; ///< The fence has been commited but it has not been checked to be free.
94};
95
96/**
97 * A fence watch is used to keep track of the usage of a fence and protect a resource or set of
98 * resources without having to inherit VKResource from their handlers.
99 */
100class VKFenceWatch final : public VKResource {
101public:
102 explicit VKFenceWatch();
103 VKFenceWatch(VKFence& initial_fence);
104 VKFenceWatch(VKFenceWatch&&) noexcept;
105 VKFenceWatch(const VKFenceWatch&) = delete;
106 ~VKFenceWatch() override;
107
108 VKFenceWatch& operator=(VKFenceWatch&&) noexcept;
109
110 /// Waits for the fence to be released.
111 void Wait();
112
113 /**
114 * Waits for a previous fence and watches a new one.
115 * @param new_fence New fence to wait to.
116 */
117 void Watch(VKFence& new_fence);
118
119 /**
120 * Checks if it's currently being watched and starts watching it if it's available.
121 * @returns True if a watch has started, false if it's being watched.
122 */
123 bool TryWatch(VKFence& new_fence);
124
125 void OnFenceRemoval(VKFence* signaling_fence) override;
126
127 /**
128 * Do not use it paired with Watch. Use TryWatch instead.
129 * Returns true when the watch is free.
130 */
131 bool IsUsed() const {
132 return fence != nullptr;
133 }
134
135private:
136 VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
137};
138
139/**
140 * Handles a pool of resources protected by fences. Manages resource overflow allocating more
141 * resources.
142 */
143class VKFencedPool {
144public:
145 explicit VKFencedPool(std::size_t grow_step);
146 virtual ~VKFencedPool();
147
148protected:
149 /**
150 * Commits a free resource and protects it with a fence. It may allocate new resources.
151 * @param fence Fence that protects the commited resource.
152 * @returns Index of the resource commited.
153 */
154 std::size_t CommitResource(VKFence& fence);
155
156 /// Called when a chunk of resources have to be allocated.
157 virtual void Allocate(std::size_t begin, std::size_t end) = 0;
158
159private:
160 /// Manages pool overflow allocating new resources.
161 std::size_t ManageOverflow();
162
163 /// Allocates a new page of resources.
164 void Grow();
165
166 std::size_t grow_step = 0; ///< Number of new resources created after an overflow
167 std::size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
168 std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Set of watched resources
169};
170
171/**
172 * The resource manager handles all resources that can be protected with a fence avoiding
173 * driver-side or GPU-side concurrent usage. Usage is documented in VKFence.
174 */
175class VKResourceManager final {
176public:
177 explicit VKResourceManager(const VKDevice& device);
178 ~VKResourceManager();
179
180 /// Commits a fence. It has to be sent to a queue and released.
181 VKFence& CommitFence();
182
183 /// Commits an unused command buffer and protects it with a fence.
184 VkCommandBuffer CommitCommandBuffer(VKFence& fence);
185
186private:
187 /// Allocates new fences.
188 void GrowFences(std::size_t new_fences_count);
189
190 const VKDevice& device; ///< Device handler.
191 std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
192 std::vector<std::unique_ptr<VKFence>> fences; ///< Pool of fences.
193 std::unique_ptr<CommandBufferPool> command_buffer_pool; ///< Pool of command buffers.
194};
195
196} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.cpp b/src/video_core/renderer_vulkan/vk_resource_pool.cpp
new file mode 100644
index 000000000..ee274ac59
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_resource_pool.cpp
@@ -0,0 +1,63 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <optional>
6
7#include "video_core/renderer_vulkan/vk_master_semaphore.h"
8#include "video_core/renderer_vulkan/vk_resource_pool.h"
9
10namespace Vulkan {
11
12ResourcePool::ResourcePool(MasterSemaphore& master_semaphore_, size_t grow_step_)
13 : master_semaphore{master_semaphore_}, grow_step{grow_step_} {}
14
15ResourcePool::~ResourcePool() = default;
16
17size_t ResourcePool::CommitResource() {
18 // Refresh semaphore to query updated results
19 master_semaphore.Refresh();
20
21 const auto search = [this](size_t begin, size_t end) -> std::optional<size_t> {
22 for (size_t iterator = begin; iterator < end; ++iterator) {
23 if (master_semaphore.IsFree(ticks[iterator])) {
24 ticks[iterator] = master_semaphore.CurrentTick();
25 return iterator;
26 }
27 }
28 return {};
29 };
30 // Try to find a free resource from the hinted position to the end.
31 auto found = search(free_iterator, ticks.size());
32 if (!found) {
33 // Search from beginning to the hinted position.
34 found = search(0, free_iterator);
35 if (!found) {
36 // Both searches failed, the pool is full; handle it.
37 const size_t free_resource = ManageOverflow();
38
39 ticks[free_resource] = master_semaphore.CurrentTick();
40 found = free_resource;
41 }
42 }
43 // Free iterator is hinted to the resource after the one that's been commited.
44 free_iterator = (*found + 1) % ticks.size();
45 return *found;
46}
47
48size_t ResourcePool::ManageOverflow() {
49 const size_t old_capacity = ticks.size();
50 Grow();
51
52 // The last entry is guaranted to be free, since it's the first element of the freshly
53 // allocated resources.
54 return old_capacity;
55}
56
57void ResourcePool::Grow() {
58 const size_t old_capacity = ticks.size();
59 ticks.resize(old_capacity + grow_step);
60 Allocate(old_capacity, old_capacity + grow_step);
61}
62
63} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.h b/src/video_core/renderer_vulkan/vk_resource_pool.h
new file mode 100644
index 000000000..a018c7ec2
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_resource_pool.h
@@ -0,0 +1,43 @@
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 <vector>
8
9#include "common/common_types.h"
10
11namespace Vulkan {
12
13class MasterSemaphore;
14
15/**
16 * Handles a pool of resources protected by fences. Manages resource overflow allocating more
17 * resources.
18 */
19class ResourcePool {
20public:
21 explicit ResourcePool(MasterSemaphore& master_semaphore, size_t grow_step);
22 virtual ~ResourcePool();
23
24protected:
25 size_t CommitResource();
26
27 /// Called when a chunk of resources have to be allocated.
28 virtual void Allocate(size_t begin, size_t end) = 0;
29
30private:
31 /// Manages pool overflow allocating new resources.
32 size_t ManageOverflow();
33
34 /// Allocates a new page of resources.
35 void Grow();
36
37 MasterSemaphore& master_semaphore;
38 size_t grow_step = 0; ///< Number of new resources created after an overflow
39 size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
40 std::vector<u64> ticks; ///< Ticks for each resource
41};
42
43} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index dbbd0961a..1a483dc71 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -10,9 +10,10 @@
10 10
11#include "common/microprofile.h" 11#include "common/microprofile.h"
12#include "common/thread.h" 12#include "common/thread.h"
13#include "video_core/renderer_vulkan/vk_command_pool.h"
13#include "video_core/renderer_vulkan/vk_device.h" 14#include "video_core/renderer_vulkan/vk_device.h"
15#include "video_core/renderer_vulkan/vk_master_semaphore.h"
14#include "video_core/renderer_vulkan/vk_query_cache.h" 16#include "video_core/renderer_vulkan/vk_query_cache.h"
15#include "video_core/renderer_vulkan/vk_resource_manager.h"
16#include "video_core/renderer_vulkan/vk_scheduler.h" 17#include "video_core/renderer_vulkan/vk_scheduler.h"
17#include "video_core/renderer_vulkan/vk_state_tracker.h" 18#include "video_core/renderer_vulkan/vk_state_tracker.h"
18#include "video_core/renderer_vulkan/wrapper.h" 19#include "video_core/renderer_vulkan/wrapper.h"
@@ -35,10 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
35 last = nullptr; 36 last = nullptr;
36} 37}
37 38
38VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager, 39VKScheduler::VKScheduler(const VKDevice& device_, StateTracker& state_tracker_)
39 StateTracker& state_tracker) 40 : device{device_}, state_tracker{state_tracker_},
40 : device{device}, resource_manager{resource_manager}, state_tracker{state_tracker}, 41 master_semaphore{std::make_unique<MasterSemaphore>(device)},
41 next_fence{&resource_manager.CommitFence()} { 42 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
42 AcquireNewChunk(); 43 AcquireNewChunk();
43 AllocateNewContext(); 44 AllocateNewContext();
44 worker_thread = std::thread(&VKScheduler::WorkerThread, this); 45 worker_thread = std::thread(&VKScheduler::WorkerThread, this);
@@ -50,20 +51,27 @@ VKScheduler::~VKScheduler() {
50 worker_thread.join(); 51 worker_thread.join();
51} 52}
52 53
53void VKScheduler::Flush(bool release_fence, VkSemaphore semaphore) { 54u64 VKScheduler::CurrentTick() const noexcept {
55 return master_semaphore->CurrentTick();
56}
57
58bool VKScheduler::IsFree(u64 tick) const noexcept {
59 return master_semaphore->IsFree(tick);
60}
61
62void VKScheduler::Wait(u64 tick) {
63 master_semaphore->Wait(tick);
64}
65
66void VKScheduler::Flush(VkSemaphore semaphore) {
54 SubmitExecution(semaphore); 67 SubmitExecution(semaphore);
55 if (release_fence) {
56 current_fence->Release();
57 }
58 AllocateNewContext(); 68 AllocateNewContext();
59} 69}
60 70
61void VKScheduler::Finish(bool release_fence, VkSemaphore semaphore) { 71void VKScheduler::Finish(VkSemaphore semaphore) {
72 const u64 presubmit_tick = CurrentTick();
62 SubmitExecution(semaphore); 73 SubmitExecution(semaphore);
63 current_fence->Wait(); 74 Wait(presubmit_tick);
64 if (release_fence) {
65 current_fence->Release();
66 }
67 AllocateNewContext(); 75 AllocateNewContext();
68} 76}
69 77
@@ -160,18 +168,38 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
160 168
161 current_cmdbuf.End(); 169 current_cmdbuf.End();
162 170
171 const VkSemaphore timeline_semaphore = master_semaphore->Handle();
172 const u32 num_signal_semaphores = semaphore ? 2U : 1U;
173
174 const u64 signal_value = master_semaphore->CurrentTick();
175 const u64 wait_value = signal_value - 1;
176 const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
177
178 master_semaphore->NextTick();
179
180 const std::array signal_values{signal_value, u64(0)};
181 const std::array signal_semaphores{timeline_semaphore, semaphore};
182
183 const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
184 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
185 .pNext = nullptr,
186 .waitSemaphoreValueCount = 1,
187 .pWaitSemaphoreValues = &wait_value,
188 .signalSemaphoreValueCount = num_signal_semaphores,
189 .pSignalSemaphoreValues = signal_values.data(),
190 };
163 const VkSubmitInfo submit_info{ 191 const VkSubmitInfo submit_info{
164 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 192 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
165 .pNext = nullptr, 193 .pNext = &timeline_si,
166 .waitSemaphoreCount = 0, 194 .waitSemaphoreCount = 1,
167 .pWaitSemaphores = nullptr, 195 .pWaitSemaphores = &timeline_semaphore,
168 .pWaitDstStageMask = nullptr, 196 .pWaitDstStageMask = &wait_stage_mask,
169 .commandBufferCount = 1, 197 .commandBufferCount = 1,
170 .pCommandBuffers = current_cmdbuf.address(), 198 .pCommandBuffers = current_cmdbuf.address(),
171 .signalSemaphoreCount = semaphore ? 1U : 0U, 199 .signalSemaphoreCount = num_signal_semaphores,
172 .pSignalSemaphores = &semaphore, 200 .pSignalSemaphores = signal_semaphores.data(),
173 }; 201 };
174 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) { 202 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
175 case VK_SUCCESS: 203 case VK_SUCCESS:
176 break; 204 break;
177 case VK_ERROR_DEVICE_LOST: 205 case VK_ERROR_DEVICE_LOST:
@@ -183,14 +211,9 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
183} 211}
184 212
185void VKScheduler::AllocateNewContext() { 213void VKScheduler::AllocateNewContext() {
186 ++ticks;
187
188 std::unique_lock lock{mutex}; 214 std::unique_lock lock{mutex};
189 current_fence = next_fence;
190 next_fence = &resource_manager.CommitFence();
191 215
192 current_cmdbuf = vk::CommandBuffer(resource_manager.CommitCommandBuffer(*current_fence), 216 current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
193 device.GetDispatchLoader());
194 current_cmdbuf.Begin({ 217 current_cmdbuf.Begin({
195 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 218 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
196 .pNext = nullptr, 219 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 970a65566..7be8a19f0 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -16,42 +16,33 @@
16 16
17namespace Vulkan { 17namespace Vulkan {
18 18
19class CommandPool;
20class MasterSemaphore;
19class StateTracker; 21class StateTracker;
20class VKDevice; 22class VKDevice;
21class VKFence;
22class VKQueryCache; 23class VKQueryCache;
23class VKResourceManager;
24
25class VKFenceView {
26public:
27 VKFenceView() = default;
28 VKFenceView(VKFence* const& fence) : fence{fence} {}
29
30 VKFence* operator->() const noexcept {
31 return fence;
32 }
33
34 operator VKFence&() const noexcept {
35 return *fence;
36 }
37
38private:
39 VKFence* const& fence;
40};
41 24
42/// The scheduler abstracts command buffer and fence management with an interface that's able to do 25/// The scheduler abstracts command buffer and fence management with an interface that's able to do
43/// OpenGL-like operations on Vulkan command buffers. 26/// OpenGL-like operations on Vulkan command buffers.
44class VKScheduler { 27class VKScheduler {
45public: 28public:
46 explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager, 29 explicit VKScheduler(const VKDevice& device, StateTracker& state_tracker);
47 StateTracker& state_tracker);
48 ~VKScheduler(); 30 ~VKScheduler();
49 31
32 /// Returns the current command buffer tick.
33 [[nodiscard]] u64 CurrentTick() const noexcept;
34
35 /// Returns true when a tick has been triggered by the GPU.
36 [[nodiscard]] bool IsFree(u64 tick) const noexcept;
37
38 /// Waits for the given tick to trigger on the GPU.
39 void Wait(u64 tick);
40
50 /// Sends the current execution context to the GPU. 41 /// Sends the current execution context to the GPU.
51 void Flush(bool release_fence = true, VkSemaphore semaphore = nullptr); 42 void Flush(VkSemaphore semaphore = nullptr);
52 43
53 /// Sends the current execution context to the GPU and waits for it to complete. 44 /// Sends the current execution context to the GPU and waits for it to complete.
54 void Finish(bool release_fence = true, VkSemaphore semaphore = nullptr); 45 void Finish(VkSemaphore semaphore = nullptr);
55 46
56 /// Waits for the worker thread to finish executing everything. After this function returns it's 47 /// Waits for the worker thread to finish executing everything. After this function returns it's
57 /// safe to touch worker resources. 48 /// safe to touch worker resources.
@@ -86,14 +77,9 @@ public:
86 (void)chunk->Record(command); 77 (void)chunk->Record(command);
87 } 78 }
88 79
89 /// Gets a reference to the current fence. 80 /// Returns the master timeline semaphore.
90 VKFenceView GetFence() const { 81 [[nodiscard]] MasterSemaphore& GetMasterSemaphore() const noexcept {
91 return current_fence; 82 return *master_semaphore;
92 }
93
94 /// Returns the current command buffer tick.
95 u64 Ticks() const {
96 return ticks;
97 } 83 }
98 84
99private: 85private:
@@ -171,6 +157,13 @@ private:
171 std::array<u8, 0x8000> data{}; 157 std::array<u8, 0x8000> data{};
172 }; 158 };
173 159
160 struct State {
161 VkRenderPass renderpass = nullptr;
162 VkFramebuffer framebuffer = nullptr;
163 VkExtent2D render_area = {0, 0};
164 VkPipeline graphics_pipeline = nullptr;
165 };
166
174 void WorkerThread(); 167 void WorkerThread();
175 168
176 void SubmitExecution(VkSemaphore semaphore); 169 void SubmitExecution(VkSemaphore semaphore);
@@ -186,30 +179,23 @@ private:
186 void AcquireNewChunk(); 179 void AcquireNewChunk();
187 180
188 const VKDevice& device; 181 const VKDevice& device;
189 VKResourceManager& resource_manager;
190 StateTracker& state_tracker; 182 StateTracker& state_tracker;
191 183
184 std::unique_ptr<MasterSemaphore> master_semaphore;
185 std::unique_ptr<CommandPool> command_pool;
186
192 VKQueryCache* query_cache = nullptr; 187 VKQueryCache* query_cache = nullptr;
193 188
194 vk::CommandBuffer current_cmdbuf; 189 vk::CommandBuffer current_cmdbuf;
195 VKFence* current_fence = nullptr;
196 VKFence* next_fence = nullptr;
197
198 struct State {
199 VkRenderPass renderpass = nullptr;
200 VkFramebuffer framebuffer = nullptr;
201 VkExtent2D render_area = {0, 0};
202 VkPipeline graphics_pipeline = nullptr;
203 } state;
204 190
205 std::unique_ptr<CommandChunk> chunk; 191 std::unique_ptr<CommandChunk> chunk;
206 std::thread worker_thread; 192 std::thread worker_thread;
207 193
194 State state;
208 Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue; 195 Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue;
209 Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve; 196 Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
210 std::mutex mutex; 197 std::mutex mutex;
211 std::condition_variable cv; 198 std::condition_variable cv;
212 std::atomic<u64> ticks = 0;
213 bool quit = false; 199 bool quit = false;
214}; 200};
215 201
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 5eca0ab91..2fd3b7f39 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -10,36 +10,18 @@
10#include "common/bit_util.h" 10#include "common/bit_util.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/renderer_vulkan/vk_device.h" 12#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/vk_resource_manager.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
15#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 14#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
16#include "video_core/renderer_vulkan/wrapper.h" 15#include "video_core/renderer_vulkan/wrapper.h"
17 16
18namespace Vulkan { 17namespace Vulkan {
19 18
20VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, 19VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_)
21 u64 last_epoch) 20 : buffer{std::move(buffer_)} {}
22 : buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {}
23 21
24VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept { 22VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device_, VKMemoryManager& memory_manager_,
25 buffer = std::move(rhs.buffer); 23 VKScheduler& scheduler_)
26 watch = std::move(rhs.watch); 24 : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
27 last_epoch = rhs.last_epoch;
28}
29
30VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default;
31
32VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=(
33 StagingBuffer&& rhs) noexcept {
34 buffer = std::move(rhs.buffer);
35 watch = std::move(rhs.watch);
36 last_epoch = rhs.last_epoch;
37 return *this;
38}
39
40VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
41 VKScheduler& scheduler)
42 : device{device}, memory_manager{memory_manager}, scheduler{scheduler} {}
43 25
44VKStagingBufferPool::~VKStagingBufferPool() = default; 26VKStagingBufferPool::~VKStagingBufferPool() = default;
45 27
@@ -51,7 +33,6 @@ VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visib
51} 33}
52 34
53void VKStagingBufferPool::TickFrame() { 35void VKStagingBufferPool::TickFrame() {
54 ++epoch;
55 current_delete_level = (current_delete_level + 1) % NumLevels; 36 current_delete_level = (current_delete_level + 1) % NumLevels;
56 37
57 ReleaseCache(true); 38 ReleaseCache(true);
@@ -59,11 +40,12 @@ void VKStagingBufferPool::TickFrame() {
59} 40}
60 41
61VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { 42VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
62 for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { 43 for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
63 if (entry.watch.TryWatch(scheduler.GetFence())) { 44 if (!scheduler.IsFree(entry.tick)) {
64 entry.last_epoch = epoch; 45 continue;
65 return &*entry.buffer;
66 } 46 }
47 entry.tick = scheduler.CurrentTick();
48 return &*entry.buffer;
67 } 49 }
68 return nullptr; 50 return nullptr;
69} 51}
@@ -86,8 +68,10 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v
86 }); 68 });
87 buffer->commit = memory_manager.Commit(buffer->handle, host_visible); 69 buffer->commit = memory_manager.Commit(buffer->handle, host_visible);
88 70
89 auto& entries = GetCache(host_visible)[log2].entries; 71 std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries;
90 return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer; 72 StagingBuffer& entry = entries.emplace_back(std::move(buffer));
73 entry.tick = scheduler.CurrentTick();
74 return *entry.buffer;
91} 75}
92 76
93VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { 77VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
@@ -109,9 +93,8 @@ u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t lo
109 auto& entries = staging.entries; 93 auto& entries = staging.entries;
110 const std::size_t old_size = entries.size(); 94 const std::size_t old_size = entries.size();
111 95
112 const auto is_deleteable = [this](const auto& entry) { 96 const auto is_deleteable = [this](const StagingBuffer& entry) {
113 static constexpr u64 epochs_to_destroy = 180; 97 return scheduler.IsFree(entry.tick);
114 return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
115 }; 98 };
116 const std::size_t begin_offset = staging.delete_index; 99 const std::size_t begin_offset = staging.delete_index;
117 const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); 100 const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index 3c4901437..2dd5049ac 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -10,13 +10,11 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12#include "video_core/renderer_vulkan/vk_memory_manager.h" 12#include "video_core/renderer_vulkan/vk_memory_manager.h"
13#include "video_core/renderer_vulkan/vk_resource_manager.h"
14#include "video_core/renderer_vulkan/wrapper.h" 13#include "video_core/renderer_vulkan/wrapper.h"
15 14
16namespace Vulkan { 15namespace Vulkan {
17 16
18class VKDevice; 17class VKDevice;
19class VKFenceWatch;
20class VKScheduler; 18class VKScheduler;
21 19
22struct VKBuffer final { 20struct VKBuffer final {
@@ -36,16 +34,10 @@ public:
36 34
37private: 35private:
38 struct StagingBuffer final { 36 struct StagingBuffer final {
39 explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch); 37 explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer);
40 StagingBuffer(StagingBuffer&& rhs) noexcept;
41 StagingBuffer(const StagingBuffer&) = delete;
42 ~StagingBuffer();
43
44 StagingBuffer& operator=(StagingBuffer&& rhs) noexcept;
45 38
46 std::unique_ptr<VKBuffer> buffer; 39 std::unique_ptr<VKBuffer> buffer;
47 VKFenceWatch watch; 40 u64 tick = 0;
48 u64 last_epoch = 0;
49 }; 41 };
50 42
51 struct StagingBuffers final { 43 struct StagingBuffers final {
@@ -73,8 +65,6 @@ private:
73 StagingBuffersCache host_staging_buffers; 65 StagingBuffersCache host_staging_buffers;
74 StagingBuffersCache device_staging_buffers; 66 StagingBuffersCache device_staging_buffers;
75 67
76 u64 epoch = 0;
77
78 std::size_t current_delete_level = 0; 68 std::size_t current_delete_level = 0;
79}; 69};
80 70
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index 3c9171a5e..5218c875b 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -11,7 +11,6 @@
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/assert.h" 12#include "common/assert.h"
13#include "video_core/renderer_vulkan/vk_device.h" 13#include "video_core/renderer_vulkan/vk_device.h"
14#include "video_core/renderer_vulkan/vk_resource_manager.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_stream_buffer.h" 15#include "video_core/renderer_vulkan/vk_stream_buffer.h"
17#include "video_core/renderer_vulkan/wrapper.h" 16#include "video_core/renderer_vulkan/wrapper.h"
@@ -111,7 +110,7 @@ void VKStreamBuffer::Unmap(u64 size) {
111 } 110 }
112 auto& watch = current_watches[current_watch_cursor++]; 111 auto& watch = current_watches[current_watch_cursor++];
113 watch.upper_bound = offset; 112 watch.upper_bound = offset;
114 watch.fence.Watch(scheduler.GetFence()); 113 watch.tick = scheduler.CurrentTick();
115} 114}
116 115
117void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) { 116void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
@@ -157,7 +156,7 @@ void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) {
157 while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) { 156 while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) {
158 auto& watch = previous_watches[wait_cursor]; 157 auto& watch = previous_watches[wait_cursor];
159 wait_bound = watch.upper_bound; 158 wait_bound = watch.upper_bound;
160 watch.fence.Wait(); 159 scheduler.Wait(watch.tick);
161 ++wait_cursor; 160 ++wait_cursor;
162 } 161 }
163} 162}
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
index 689f0d276..5e15ad78f 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.h
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h
@@ -14,7 +14,6 @@
14namespace Vulkan { 14namespace Vulkan {
15 15
16class VKDevice; 16class VKDevice;
17class VKFence;
18class VKFenceWatch; 17class VKFenceWatch;
19class VKScheduler; 18class VKScheduler;
20 19
@@ -44,8 +43,8 @@ public:
44 } 43 }
45 44
46private: 45private:
47 struct Watch final { 46 struct Watch {
48 VKFenceWatch fence; 47 u64 tick{};
49 u64 upper_bound{}; 48 u64 upper_bound{};
50 }; 49 };
51 50
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 6bfd2abae..9636a7c65 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -12,7 +12,7 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h" 13#include "core/frontend/framebuffer_layout.h"
14#include "video_core/renderer_vulkan/vk_device.h" 14#include "video_core/renderer_vulkan/vk_device.h"
15#include "video_core/renderer_vulkan/vk_resource_manager.h" 15#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_swapchain.h" 16#include "video_core/renderer_vulkan/vk_swapchain.h"
17#include "video_core/renderer_vulkan/wrapper.h" 17#include "video_core/renderer_vulkan/wrapper.h"
18 18
@@ -56,8 +56,8 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
56 56
57} // Anonymous namespace 57} // Anonymous namespace
58 58
59VKSwapchain::VKSwapchain(VkSurfaceKHR surface, const VKDevice& device) 59VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const VKDevice& device_, VKScheduler& scheduler_)
60 : surface{surface}, device{device} {} 60 : surface{surface_}, device{device_}, scheduler{scheduler_} {}
61 61
62VKSwapchain::~VKSwapchain() = default; 62VKSwapchain::~VKSwapchain() = default;
63 63
@@ -75,21 +75,18 @@ void VKSwapchain::Create(u32 width, u32 height, bool srgb) {
75 CreateSemaphores(); 75 CreateSemaphores();
76 CreateImageViews(); 76 CreateImageViews();
77 77
78 fences.resize(image_count, nullptr); 78 resource_ticks.clear();
79 resource_ticks.resize(image_count);
79} 80}
80 81
81void VKSwapchain::AcquireNextImage() { 82void VKSwapchain::AcquireNextImage() {
82 device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(), 83 device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
83 *present_semaphores[frame_index], {}, &image_index); 84 *present_semaphores[frame_index], {}, &image_index);
84 85
85 if (auto& fence = fences[image_index]; fence) { 86 scheduler.Wait(resource_ticks[image_index]);
86 fence->Wait();
87 fence->Release();
88 fence = nullptr;
89 }
90} 87}
91 88
92bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) { 89bool VKSwapchain::Present(VkSemaphore render_semaphore) {
93 const VkSemaphore present_semaphore{*present_semaphores[frame_index]}; 90 const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
94 const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore}; 91 const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
95 const auto present_queue{device.GetPresentQueue()}; 92 const auto present_queue{device.GetPresentQueue()};
@@ -123,8 +120,7 @@ bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) {
123 break; 120 break;
124 } 121 }
125 122
126 ASSERT(fences[image_index] == nullptr); 123 resource_ticks[image_index] = scheduler.CurrentTick();
127 fences[image_index] = &fence;
128 frame_index = (frame_index + 1) % static_cast<u32>(image_count); 124 frame_index = (frame_index + 1) % static_cast<u32>(image_count);
129 return recreated; 125 return recreated;
130} 126}
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index a35d61345..6b39befdf 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -16,11 +16,11 @@ struct FramebufferLayout;
16namespace Vulkan { 16namespace Vulkan {
17 17
18class VKDevice; 18class VKDevice;
19class VKFence; 19class VKScheduler;
20 20
21class VKSwapchain { 21class VKSwapchain {
22public: 22public:
23 explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device); 23 explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device, VKScheduler& scheduler);
24 ~VKSwapchain(); 24 ~VKSwapchain();
25 25
26 /// Creates (or recreates) the swapchain with a given size. 26 /// Creates (or recreates) the swapchain with a given size.
@@ -31,7 +31,7 @@ public:
31 31
32 /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be 32 /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be
33 /// recreated. Takes responsability for the ownership of fence. 33 /// recreated. Takes responsability for the ownership of fence.
34 bool Present(VkSemaphore render_semaphore, VKFence& fence); 34 bool Present(VkSemaphore render_semaphore);
35 35
36 /// Returns true when the framebuffer layout has changed. 36 /// Returns true when the framebuffer layout has changed.
37 bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const; 37 bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const;
@@ -74,6 +74,7 @@ private:
74 74
75 const VkSurfaceKHR surface; 75 const VkSurfaceKHR surface;
76 const VKDevice& device; 76 const VKDevice& device;
77 VKScheduler& scheduler;
77 78
78 vk::SwapchainKHR swapchain; 79 vk::SwapchainKHR swapchain;
79 80
@@ -81,7 +82,7 @@ private:
81 std::vector<VkImage> images; 82 std::vector<VkImage> images;
82 std::vector<vk::ImageView> image_views; 83 std::vector<vk::ImageView> image_views;
83 std::vector<vk::Framebuffer> framebuffers; 84 std::vector<vk::Framebuffer> framebuffers;
84 std::vector<VKFence*> fences; 85 std::vector<u64> resource_ticks;
85 std::vector<vk::Semaphore> present_semaphores; 86 std::vector<vk::Semaphore> present_semaphores;
86 87
87 u32 image_index{}; 88 u32 image_index{};
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 06182d909..f2c8f2ae1 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -188,13 +188,11 @@ u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::Swizzl
188 188
189} // Anonymous namespace 189} // Anonymous namespace
190 190
191CachedSurface::CachedSurface(const VKDevice& device, VKResourceManager& resource_manager, 191CachedSurface::CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager,
192 VKMemoryManager& memory_manager, VKScheduler& scheduler, 192 VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
193 VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr, 193 GPUVAddr gpu_addr, const SurfaceParams& params)
194 const SurfaceParams& params)
195 : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, device{device}, 194 : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, device{device},
196 resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, 195 memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {
197 staging_pool{staging_pool} {
198 if (params.IsBuffer()) { 196 if (params.IsBuffer()) {
199 buffer = CreateBuffer(device, params, host_memory_size); 197 buffer = CreateBuffer(device, params, host_memory_size);
200 commit = memory_manager.Commit(buffer, false); 198 commit = memory_manager.Commit(buffer, false);
@@ -493,18 +491,17 @@ VkImageView CachedSurfaceView::GetAttachment() {
493VKTextureCache::VKTextureCache(VideoCore::RasterizerInterface& rasterizer, 491VKTextureCache::VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
494 Tegra::Engines::Maxwell3D& maxwell3d, 492 Tegra::Engines::Maxwell3D& maxwell3d,
495 Tegra::MemoryManager& gpu_memory, const VKDevice& device_, 493 Tegra::MemoryManager& gpu_memory, const VKDevice& device_,
496 VKResourceManager& resource_manager_,
497 VKMemoryManager& memory_manager_, VKScheduler& scheduler_, 494 VKMemoryManager& memory_manager_, VKScheduler& scheduler_,
498 VKStagingBufferPool& staging_pool_) 495 VKStagingBufferPool& staging_pool_)
499 : TextureCache(rasterizer, maxwell3d, gpu_memory, device_.IsOptimalAstcSupported()), 496 : TextureCache(rasterizer, maxwell3d, gpu_memory, device_.IsOptimalAstcSupported()),
500 device{device_}, resource_manager{resource_manager_}, 497 device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{
501 memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{staging_pool_} {} 498 staging_pool_} {}
502 499
503VKTextureCache::~VKTextureCache() = default; 500VKTextureCache::~VKTextureCache() = default;
504 501
505Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) { 502Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
506 return std::make_shared<CachedSurface>(device, resource_manager, memory_manager, scheduler, 503 return std::make_shared<CachedSurface>(device, memory_manager, scheduler, staging_pool,
507 staging_pool, gpu_addr, params); 504 gpu_addr, params);
508} 505}
509 506
510void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface, 507void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index e47d02c41..39202feba 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -23,7 +23,6 @@ namespace Vulkan {
23 23
24class RasterizerVulkan; 24class RasterizerVulkan;
25class VKDevice; 25class VKDevice;
26class VKResourceManager;
27class VKScheduler; 26class VKScheduler;
28class VKStagingBufferPool; 27class VKStagingBufferPool;
29 28
@@ -41,10 +40,9 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> {
41 friend CachedSurfaceView; 40 friend CachedSurfaceView;
42 41
43public: 42public:
44 explicit CachedSurface(const VKDevice& device, VKResourceManager& resource_manager, 43 explicit CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager,
45 VKMemoryManager& memory_manager, VKScheduler& scheduler, 44 VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
46 VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr, 45 GPUVAddr gpu_addr, const SurfaceParams& params);
47 const SurfaceParams& params);
48 ~CachedSurface(); 46 ~CachedSurface();
49 47
50 void UploadTexture(const std::vector<u8>& staging_buffer) override; 48 void UploadTexture(const std::vector<u8>& staging_buffer) override;
@@ -98,7 +96,6 @@ private:
98 VkImageSubresourceRange GetImageSubresourceRange() const; 96 VkImageSubresourceRange GetImageSubresourceRange() const;
99 97
100 const VKDevice& device; 98 const VKDevice& device;
101 VKResourceManager& resource_manager;
102 VKMemoryManager& memory_manager; 99 VKMemoryManager& memory_manager;
103 VKScheduler& scheduler; 100 VKScheduler& scheduler;
104 VKStagingBufferPool& staging_pool; 101 VKStagingBufferPool& staging_pool;
@@ -198,9 +195,8 @@ class VKTextureCache final : public TextureCacheBase {
198public: 195public:
199 explicit VKTextureCache(VideoCore::RasterizerInterface& rasterizer, 196 explicit VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
200 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory, 197 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
201 const VKDevice& device, VKResourceManager& resource_manager, 198 const VKDevice& device, VKMemoryManager& memory_manager,
202 VKMemoryManager& memory_manager, VKScheduler& scheduler, 199 VKScheduler& scheduler, VKStagingBufferPool& staging_pool);
203 VKStagingBufferPool& staging_pool);
204 ~VKTextureCache(); 200 ~VKTextureCache();
205 201
206private: 202private:
@@ -215,7 +211,6 @@ private:
215 void BufferCopy(Surface& src_surface, Surface& dst_surface) override; 211 void BufferCopy(Surface& src_surface, Surface& dst_surface) override;
216 212
217 const VKDevice& device; 213 const VKDevice& device;
218 VKResourceManager& resource_manager;
219 VKMemoryManager& memory_manager; 214 VKMemoryManager& memory_manager;
220 VKScheduler& scheduler; 215 VKScheduler& scheduler;
221 VKStagingBufferPool& staging_pool; 216 VKStagingBufferPool& staging_pool;
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index fe291a148..1fb14e190 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -148,6 +148,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
148 X(vkGetFenceStatus); 148 X(vkGetFenceStatus);
149 X(vkGetImageMemoryRequirements); 149 X(vkGetImageMemoryRequirements);
150 X(vkGetQueryPoolResults); 150 X(vkGetQueryPoolResults);
151 X(vkGetSemaphoreCounterValueKHR);
151 X(vkMapMemory); 152 X(vkMapMemory);
152 X(vkQueueSubmit); 153 X(vkQueueSubmit);
153 X(vkResetFences); 154 X(vkResetFences);
@@ -156,6 +157,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
156 X(vkUpdateDescriptorSetWithTemplateKHR); 157 X(vkUpdateDescriptorSetWithTemplateKHR);
157 X(vkUpdateDescriptorSets); 158 X(vkUpdateDescriptorSets);
158 X(vkWaitForFences); 159 X(vkWaitForFences);
160 X(vkWaitSemaphoresKHR);
159#undef X 161#undef X
160} 162}
161 163
@@ -574,7 +576,10 @@ Semaphore Device::CreateSemaphore() const {
574 .pNext = nullptr, 576 .pNext = nullptr,
575 .flags = 0, 577 .flags = 0,
576 }; 578 };
579 return CreateSemaphore(ci);
580}
577 581
582Semaphore Device::CreateSemaphore(const VkSemaphoreCreateInfo& ci) const {
578 VkSemaphore object; 583 VkSemaphore object;
579 Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object)); 584 Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object));
580 return Semaphore(object, handle, *dld); 585 return Semaphore(object, handle, *dld);
@@ -660,7 +665,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
660 return ShaderModule(object, handle, *dld); 665 return ShaderModule(object, handle, *dld);
661} 666}
662 667
663Event Device::CreateNewEvent() const { 668Event Device::CreateEvent() const {
664 static constexpr VkEventCreateInfo ci{ 669 static constexpr VkEventCreateInfo ci{
665 .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, 670 .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
666 .pNext = nullptr, 671 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index b9d3fedc1..234e01693 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -267,6 +267,7 @@ struct DeviceDispatch : public InstanceDispatch {
267 PFN_vkGetFenceStatus vkGetFenceStatus; 267 PFN_vkGetFenceStatus vkGetFenceStatus;
268 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; 268 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
269 PFN_vkGetQueryPoolResults vkGetQueryPoolResults; 269 PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
270 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR;
270 PFN_vkMapMemory vkMapMemory; 271 PFN_vkMapMemory vkMapMemory;
271 PFN_vkQueueSubmit vkQueueSubmit; 272 PFN_vkQueueSubmit vkQueueSubmit;
272 PFN_vkResetFences vkResetFences; 273 PFN_vkResetFences vkResetFences;
@@ -275,6 +276,7 @@ struct DeviceDispatch : public InstanceDispatch {
275 PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; 276 PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR;
276 PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; 277 PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
277 PFN_vkWaitForFences vkWaitForFences; 278 PFN_vkWaitForFences vkWaitForFences;
279 PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR;
278}; 280};
279 281
280/// Loads instance agnostic function pointers. 282/// Loads instance agnostic function pointers.
@@ -550,7 +552,6 @@ using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>;
550using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>; 552using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>;
551using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>; 553using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>;
552using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>; 554using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>;
553using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>;
554using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>; 555using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>;
555using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>; 556using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>;
556 557
@@ -582,7 +583,8 @@ public:
582 /// Construct a queue handle. 583 /// Construct a queue handle.
583 constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} 584 constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
584 585
585 VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept { 586 VkResult Submit(Span<VkSubmitInfo> submit_infos,
587 VkFence fence = VK_NULL_HANDLE) const noexcept {
586 return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence); 588 return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
587 } 589 }
588 590
@@ -674,6 +676,44 @@ public:
674 } 676 }
675}; 677};
676 678
679class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> {
680 using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle;
681
682public:
683 [[nodiscard]] u64 GetCounter() const {
684 u64 value;
685 Check(dld->vkGetSemaphoreCounterValueKHR(owner, handle, &value));
686 return value;
687 }
688
689 /**
690 * Waits for a timeline semaphore on the host.
691 *
692 * @param value Value to wait
693 * @param timeout Time in nanoseconds to timeout
694 * @return True on successful wait, false on timeout
695 */
696 bool Wait(u64 value, u64 timeout = std::numeric_limits<u64>::max()) const {
697 const VkSemaphoreWaitInfoKHR wait_info{
698 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR,
699 .pNext = nullptr,
700 .flags = 0,
701 .semaphoreCount = 1,
702 .pSemaphores = &handle,
703 .pValues = &value,
704 };
705 const VkResult result = dld->vkWaitSemaphoresKHR(owner, &wait_info, timeout);
706 switch (result) {
707 case VK_SUCCESS:
708 return true;
709 case VK_TIMEOUT:
710 return false;
711 default:
712 throw Exception(result);
713 }
714 }
715};
716
677class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { 717class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
678 using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle; 718 using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
679 719
@@ -694,6 +734,8 @@ public:
694 734
695 Semaphore CreateSemaphore() const; 735 Semaphore CreateSemaphore() const;
696 736
737 Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
738
697 Fence CreateFence(const VkFenceCreateInfo& ci) const; 739 Fence CreateFence(const VkFenceCreateInfo& ci) const;
698 740
699 DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const; 741 DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
@@ -721,7 +763,7 @@ public:
721 763
722 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; 764 ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
723 765
724 Event CreateNewEvent() const; 766 Event CreateEvent() const;
725 767
726 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; 768 SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
727 769
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h
index cca13bcde..8e5a22ab3 100644
--- a/src/video_core/shader/ast.h
+++ b/src/video_core/shader/ast.h
@@ -199,55 +199,48 @@ public:
199 } 199 }
200 200
201 std::optional<u32> GetGotoLabel() const { 201 std::optional<u32> GetGotoLabel() const {
202 auto inner = std::get_if<ASTGoto>(&data); 202 if (const auto* inner = std::get_if<ASTGoto>(&data)) {
203 if (inner) {
204 return {inner->label}; 203 return {inner->label};
205 } 204 }
206 return {}; 205 return std::nullopt;
207 } 206 }
208 207
209 Expr GetGotoCondition() const { 208 Expr GetGotoCondition() const {
210 auto inner = std::get_if<ASTGoto>(&data); 209 if (const auto* inner = std::get_if<ASTGoto>(&data)) {
211 if (inner) {
212 return inner->condition; 210 return inner->condition;
213 } 211 }
214 return nullptr; 212 return nullptr;
215 } 213 }
216 214
217 void MarkLabelUnused() { 215 void MarkLabelUnused() {
218 auto inner = std::get_if<ASTLabel>(&data); 216 if (auto* inner = std::get_if<ASTLabel>(&data)) {
219 if (inner) {
220 inner->unused = true; 217 inner->unused = true;
221 } 218 }
222 } 219 }
223 220
224 bool IsLabelUnused() const { 221 bool IsLabelUnused() const {
225 auto inner = std::get_if<ASTLabel>(&data); 222 if (const auto* inner = std::get_if<ASTLabel>(&data)) {
226 if (inner) {
227 return inner->unused; 223 return inner->unused;
228 } 224 }
229 return true; 225 return true;
230 } 226 }
231 227
232 std::optional<u32> GetLabelIndex() const { 228 std::optional<u32> GetLabelIndex() const {
233 auto inner = std::get_if<ASTLabel>(&data); 229 if (const auto* inner = std::get_if<ASTLabel>(&data)) {
234 if (inner) {
235 return {inner->index}; 230 return {inner->index};
236 } 231 }
237 return {}; 232 return std::nullopt;
238 } 233 }
239 234
240 Expr GetIfCondition() const { 235 Expr GetIfCondition() const {
241 auto inner = std::get_if<ASTIfThen>(&data); 236 if (const auto* inner = std::get_if<ASTIfThen>(&data)) {
242 if (inner) {
243 return inner->condition; 237 return inner->condition;
244 } 238 }
245 return nullptr; 239 return nullptr;
246 } 240 }
247 241
248 void SetGotoCondition(Expr new_condition) { 242 void SetGotoCondition(Expr new_condition) {
249 auto inner = std::get_if<ASTGoto>(&data); 243 if (auto* inner = std::get_if<ASTGoto>(&data)) {
250 if (inner) {
251 inner->condition = std::move(new_condition); 244 inner->condition = std::move(new_condition);
252 } 245 }
253 } 246 }
diff --git a/src/video_core/shader/async_shaders.h b/src/video_core/shader/async_shaders.h
index 7cf8d994c..7a99e1dc5 100644
--- a/src/video_core/shader/async_shaders.h
+++ b/src/video_core/shader/async_shaders.h
@@ -9,6 +9,17 @@
9#include <shared_mutex> 9#include <shared_mutex>
10#include <thread> 10#include <thread>
11 11
12// This header includes both Vulkan and OpenGL headers, this has to be fixed
13// Unfortunately, including OpenGL will include Windows.h that defines macros that can cause issues.
14// Forcefully include glad early and undefine macros
15#include <glad/glad.h>
16#ifdef CreateEvent
17#undef CreateEvent
18#endif
19#ifdef CreateSemaphore
20#undef CreateSemaphore
21#endif
22
12#include "common/common_types.h" 23#include "common/common_types.h"
13#include "video_core/renderer_opengl/gl_device.h" 24#include "video_core/renderer_opengl/gl_device.h"
14#include "video_core/renderer_opengl/gl_resource_manager.h" 25#include "video_core/renderer_opengl/gl_resource_manager.h"
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 336397cdb..4c8971615 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -547,13 +547,13 @@ bool TryQuery(CFGRebuildState& state) {
547 gather_labels(q2.ssy_stack, state.ssy_labels, block); 547 gather_labels(q2.ssy_stack, state.ssy_labels, block);
548 gather_labels(q2.pbk_stack, state.pbk_labels, block); 548 gather_labels(q2.pbk_stack, state.pbk_labels, block);
549 if (std::holds_alternative<SingleBranch>(*block.branch)) { 549 if (std::holds_alternative<SingleBranch>(*block.branch)) {
550 const auto branch = std::get_if<SingleBranch>(block.branch.get()); 550 auto* branch = std::get_if<SingleBranch>(block.branch.get());
551 if (!branch->condition.IsUnconditional()) { 551 if (!branch->condition.IsUnconditional()) {
552 q2.address = block.end + 1; 552 q2.address = block.end + 1;
553 state.queries.push_back(q2); 553 state.queries.push_back(q2);
554 } 554 }
555 555
556 Query conditional_query{q2}; 556 auto& conditional_query = state.queries.emplace_back(q2);
557 if (branch->is_sync) { 557 if (branch->is_sync) {
558 if (branch->address == unassigned_branch) { 558 if (branch->address == unassigned_branch) {
559 branch->address = conditional_query.ssy_stack.top(); 559 branch->address = conditional_query.ssy_stack.top();
@@ -567,21 +567,21 @@ bool TryQuery(CFGRebuildState& state) {
567 conditional_query.pbk_stack.pop(); 567 conditional_query.pbk_stack.pop();
568 } 568 }
569 conditional_query.address = branch->address; 569 conditional_query.address = branch->address;
570 state.queries.push_back(std::move(conditional_query));
571 return true; 570 return true;
572 } 571 }
573 const auto multi_branch = std::get_if<MultiBranch>(block.branch.get()); 572
573 const auto* multi_branch = std::get_if<MultiBranch>(block.branch.get());
574 for (const auto& branch_case : multi_branch->branches) { 574 for (const auto& branch_case : multi_branch->branches) {
575 Query conditional_query{q2}; 575 auto& conditional_query = state.queries.emplace_back(q2);
576 conditional_query.address = branch_case.address; 576 conditional_query.address = branch_case.address;
577 state.queries.push_back(std::move(conditional_query));
578 } 577 }
578
579 return true; 579 return true;
580} 580}
581 581
582void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) { 582void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
583 const auto get_expr = ([&](const Condition& cond) -> Expr { 583 const auto get_expr = [](const Condition& cond) -> Expr {
584 Expr result{}; 584 Expr result;
585 if (cond.cc != ConditionCode::T) { 585 if (cond.cc != ConditionCode::T) {
586 result = MakeExpr<ExprCondCode>(cond.cc); 586 result = MakeExpr<ExprCondCode>(cond.cc);
587 } 587 }
@@ -594,10 +594,10 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
594 } 594 }
595 Expr extra = MakeExpr<ExprPredicate>(pred); 595 Expr extra = MakeExpr<ExprPredicate>(pred);
596 if (negate) { 596 if (negate) {
597 extra = MakeExpr<ExprNot>(extra); 597 extra = MakeExpr<ExprNot>(std::move(extra));
598 } 598 }
599 if (result) { 599 if (result) {
600 return MakeExpr<ExprAnd>(extra, result); 600 return MakeExpr<ExprAnd>(std::move(extra), std::move(result));
601 } 601 }
602 return extra; 602 return extra;
603 } 603 }
@@ -605,9 +605,10 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
605 return result; 605 return result;
606 } 606 }
607 return MakeExpr<ExprBoolean>(true); 607 return MakeExpr<ExprBoolean>(true);
608 }); 608 };
609
609 if (std::holds_alternative<SingleBranch>(*branch_info)) { 610 if (std::holds_alternative<SingleBranch>(*branch_info)) {
610 const auto branch = std::get_if<SingleBranch>(branch_info.get()); 611 const auto* branch = std::get_if<SingleBranch>(branch_info.get());
611 if (branch->address < 0) { 612 if (branch->address < 0) {
612 if (branch->kill) { 613 if (branch->kill) {
613 mm.InsertReturn(get_expr(branch->condition), true); 614 mm.InsertReturn(get_expr(branch->condition), true);
@@ -619,7 +620,7 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
619 mm.InsertGoto(get_expr(branch->condition), branch->address); 620 mm.InsertGoto(get_expr(branch->condition), branch->address);
620 return; 621 return;
621 } 622 }
622 const auto multi_branch = std::get_if<MultiBranch>(branch_info.get()); 623 const auto* multi_branch = std::get_if<MultiBranch>(branch_info.get());
623 for (const auto& branch_case : multi_branch->branches) { 624 for (const auto& branch_case : multi_branch->branches) {
624 mm.InsertGoto(MakeExpr<ExprGprEqual>(multi_branch->gpr, branch_case.cmp_value), 625 mm.InsertGoto(MakeExpr<ExprGprEqual>(multi_branch->gpr, branch_case.cmp_value),
625 branch_case.address); 626 branch_case.address);
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index d5ed81442..6be3ea92b 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -205,12 +205,12 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code,
205 const auto result = TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1); 205 const auto result = TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1);
206 const auto& found = result.first; 206 const auto& found = result.first;
207 if (!found) { 207 if (!found) {
208 return {}; 208 return std::nullopt;
209 } 209 }
210 if (const auto immediate = std::get_if<ImmediateNode>(&*found)) { 210 if (const auto immediate = std::get_if<ImmediateNode>(&*found)) {
211 return immediate->GetValue(); 211 return immediate->GetValue();
212 } 212 }
213 return {}; 213 return std::nullopt;
214} 214}
215 215
216std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code, 216std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index dfcf36e0b..b44c09d71 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -115,20 +115,24 @@ std::optional<std::pair<u32, u32>> SurfaceBaseImpl::GetLayerMipmap(
115 if (gpu_addr == candidate_gpu_addr) { 115 if (gpu_addr == candidate_gpu_addr) {
116 return {{0, 0}}; 116 return {{0, 0}};
117 } 117 }
118
118 if (candidate_gpu_addr < gpu_addr) { 119 if (candidate_gpu_addr < gpu_addr) {
119 return {}; 120 return std::nullopt;
120 } 121 }
122
121 const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)}; 123 const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)};
122 const auto layer{static_cast<u32>(relative_address / layer_size)}; 124 const auto layer{static_cast<u32>(relative_address / layer_size)};
123 if (layer >= params.depth) { 125 if (layer >= params.depth) {
124 return {}; 126 return std::nullopt;
125 } 127 }
128
126 const GPUVAddr mipmap_address = relative_address - layer_size * layer; 129 const GPUVAddr mipmap_address = relative_address - layer_size * layer;
127 const auto mipmap_it = 130 const auto mipmap_it =
128 Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); 131 Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address);
129 if (mipmap_it == mipmap_offsets.end()) { 132 if (mipmap_it == mipmap_offsets.end()) {
130 return {}; 133 return std::nullopt;
131 } 134 }
135
132 const auto level{static_cast<u32>(std::distance(mipmap_offsets.begin(), mipmap_it))}; 136 const auto level{static_cast<u32>(std::distance(mipmap_offsets.begin(), mipmap_it))};
133 return std::make_pair(layer, level); 137 return std::make_pair(layer, level);
134} 138}
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 6a71d9644..a9738e298 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -56,7 +56,7 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
56 case Qt::Key_Return: 56 case Qt::Key_Return:
57 case Qt::Key_Enter: { 57 case Qt::Key_Enter: {
58 if (gamelist->search_field->visible == 1) { 58 if (gamelist->search_field->visible == 1) {
59 QString file_path = gamelist->getLastFilterResultItem(); 59 const QString file_path = gamelist->GetLastFilterResultItem();
60 60
61 // To avoid loading error dialog loops while confirming them using enter 61 // To avoid loading error dialog loops while confirming them using enter
62 // Also users usually want to run a different game after closing one 62 // Also users usually want to run a different game after closing one
@@ -83,22 +83,25 @@ void GameListSearchField::setFilterResult(int visible, int total) {
83 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); 83 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
84} 84}
85 85
86QString GameList::getLastFilterResultItem() const { 86QString GameList::GetLastFilterResultItem() const {
87 QStandardItem* folder;
88 QStandardItem* child;
89 QString file_path; 87 QString file_path;
90 const int folder_count = item_model->rowCount(); 88 const int folder_count = item_model->rowCount();
89
91 for (int i = 0; i < folder_count; ++i) { 90 for (int i = 0; i < folder_count; ++i) {
92 folder = item_model->item(i, 0); 91 const QStandardItem* folder = item_model->item(i, 0);
93 const QModelIndex folder_index = folder->index(); 92 const QModelIndex folder_index = folder->index();
94 const int children_count = folder->rowCount(); 93 const int children_count = folder->rowCount();
94
95 for (int j = 0; j < children_count; ++j) { 95 for (int j = 0; j < children_count; ++j) {
96 if (!tree_view->isRowHidden(j, folder_index)) { 96 if (tree_view->isRowHidden(j, folder_index)) {
97 child = folder->child(j, 0); 97 continue;
98 file_path = child->data(GameListItemPath::FullPathRole).toString();
99 } 98 }
99
100 const QStandardItem* child = folder->child(j, 0);
101 file_path = child->data(GameListItemPath::FullPathRole).toString();
100 } 102 }
101 } 103 }
104
102 return file_path; 105 return file_path;
103} 106}
104 107
@@ -123,7 +126,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
123 edit_filter->setPlaceholderText(tr("Enter pattern to filter")); 126 edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
124 edit_filter->installEventFilter(key_release_eater); 127 edit_filter->installEventFilter(key_release_eater);
125 edit_filter->setClearButtonEnabled(true); 128 edit_filter->setClearButtonEnabled(true);
126 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged); 129 connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged);
127 label_filter_result = new QLabel; 130 label_filter_result = new QLabel;
128 button_filter_close = new QToolButton(this); 131 button_filter_close = new QToolButton(this);
129 button_filter_close->setText(QStringLiteral("X")); 132 button_filter_close->setText(QStringLiteral("X"));
@@ -133,7 +136,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
133 "#000000; font-weight: bold; background: #F0F0F0; }" 136 "#000000; font-weight: bold; background: #F0F0F0; }"
134 "QToolButton:hover{ border: none; padding: 0px; color: " 137 "QToolButton:hover{ border: none; padding: 0px; color: "
135 "#EEEEEE; font-weight: bold; background: #E81123}")); 138 "#EEEEEE; font-weight: bold; background: #E81123}"));
136 connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked); 139 connect(button_filter_close, &QToolButton::clicked, parent, &GameList::OnFilterCloseClicked);
137 layout_filter->setSpacing(10); 140 layout_filter->setSpacing(10);
138 layout_filter->addWidget(label_filter); 141 layout_filter->addWidget(label_filter);
139 layout_filter->addWidget(edit_filter); 142 layout_filter->addWidget(edit_filter);
@@ -159,16 +162,22 @@ static bool ContainsAllWords(const QString& haystack, const QString& userinput)
159} 162}
160 163
161// Syncs the expanded state of Game Directories with settings to persist across sessions 164// Syncs the expanded state of Game Directories with settings to persist across sessions
162void GameList::onItemExpanded(const QModelIndex& item) { 165void GameList::OnItemExpanded(const QModelIndex& item) {
163 const auto type = item.data(GameListItem::TypeRole).value<GameListItemType>(); 166 const auto type = item.data(GameListItem::TypeRole).value<GameListItemType>();
164 if (type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || 167 const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
165 type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir) 168 type == GameListItemType::UserNandDir ||
166 item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>()->expanded = 169 type == GameListItemType::SysNandDir;
167 tree_view->isExpanded(item); 170
171 if (!is_dir) {
172 return;
173 }
174
175 auto* game_dir = item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
176 game_dir->expanded = tree_view->isExpanded(item);
168} 177}
169 178
170// Event in order to filter the gamelist after editing the searchfield 179// Event in order to filter the gamelist after editing the searchfield
171void GameList::onTextChanged(const QString& new_text) { 180void GameList::OnTextChanged(const QString& new_text) {
172 const int folder_count = tree_view->model()->rowCount(); 181 const int folder_count = tree_view->model()->rowCount();
173 QString edit_filter_text = new_text.toLower(); 182 QString edit_filter_text = new_text.toLower();
174 QStandardItem* folder; 183 QStandardItem* folder;
@@ -224,7 +233,7 @@ void GameList::onTextChanged(const QString& new_text) {
224 } 233 }
225} 234}
226 235
227void GameList::onUpdateThemedIcons() { 236void GameList::OnUpdateThemedIcons() {
228 for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { 237 for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
229 QStandardItem* child = item_model->invisibleRootItem()->child(i); 238 QStandardItem* child = item_model->invisibleRootItem()->child(i);
230 239
@@ -276,7 +285,7 @@ void GameList::onUpdateThemedIcons() {
276 } 285 }
277} 286}
278 287
279void GameList::onFilterCloseClicked() { 288void GameList::OnFilterCloseClicked() {
280 main_window->filterBarSetChecked(false); 289 main_window->filterBarSetChecked(false);
281} 290}
282 291
@@ -317,11 +326,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
317 } 326 }
318 item_model->setSortRole(GameListItemPath::SortRole); 327 item_model->setSortRole(GameListItemPath::SortRole);
319 328
320 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons); 329 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
321 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 330 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
322 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 331 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
323 connect(tree_view, &QTreeView::expanded, this, &GameList::onItemExpanded); 332 connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
324 connect(tree_view, &QTreeView::collapsed, this, &GameList::onItemExpanded); 333 connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
325 334
326 // We must register all custom types with the Qt Automoc system so that we are able to use 335 // We must register all custom types with the Qt Automoc system so that we are able to use
327 // it with signals/slots. In this case, QList falls under the umbrells of custom types. 336 // it with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -338,17 +347,17 @@ GameList::~GameList() {
338 emit ShouldCancelWorker(); 347 emit ShouldCancelWorker();
339} 348}
340 349
341void GameList::setFilterFocus() { 350void GameList::SetFilterFocus() {
342 if (tree_view->model()->rowCount() > 0) { 351 if (tree_view->model()->rowCount() > 0) {
343 search_field->setFocus(); 352 search_field->setFocus();
344 } 353 }
345} 354}
346 355
347void GameList::setFilterVisible(bool visibility) { 356void GameList::SetFilterVisible(bool visibility) {
348 search_field->setVisible(visibility); 357 search_field->setVisible(visibility);
349} 358}
350 359
351void GameList::clearFilter() { 360void GameList::ClearFilter() {
352 search_field->clear(); 361 search_field->clear();
353} 362}
354 363
@@ -397,10 +406,11 @@ void GameList::ValidateEntry(const QModelIndex& item) {
397 } 406 }
398} 407}
399 408
400bool GameList::isEmpty() const { 409bool GameList::IsEmpty() const {
401 for (int i = 0; i < item_model->rowCount(); i++) { 410 for (int i = 0; i < item_model->rowCount(); i++) {
402 const QStandardItem* child = item_model->invisibleRootItem()->child(i); 411 const QStandardItem* child = item_model->invisibleRootItem()->child(i);
403 const auto type = static_cast<GameListItemType>(child->type()); 412 const auto type = static_cast<GameListItemType>(child->type());
413
404 if (!child->hasChildren() && 414 if (!child->hasChildren() &&
405 (type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir || 415 (type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir ||
406 type == GameListItemType::SysNandDir)) { 416 type == GameListItemType::SysNandDir)) {
@@ -408,11 +418,12 @@ bool GameList::isEmpty() const {
408 i--; 418 i--;
409 } 419 }
410 } 420 }
421
411 return !item_model->invisibleRootItem()->hasChildren(); 422 return !item_model->invisibleRootItem()->hasChildren();
412} 423}
413 424
414void GameList::DonePopulating(QStringList watch_list) { 425void GameList::DonePopulating(const QStringList& watch_list) {
415 emit ShowList(!isEmpty()); 426 emit ShowList(!IsEmpty());
416 427
417 item_model->invisibleRootItem()->appendRow(new GameListAddDir()); 428 item_model->invisibleRootItem()->appendRow(new GameListAddDir());
418 429
@@ -472,7 +483,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
472 context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); 483 context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
473} 484}
474 485
475void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { 486void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) {
476 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 487 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
477 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location")); 488 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
478 QAction* open_transferable_shader_cache = 489 QAction* open_transferable_shader_cache =
@@ -690,12 +701,15 @@ void GameList::SaveInterfaceLayout() {
690} 701}
691 702
692void GameList::LoadInterfaceLayout() { 703void GameList::LoadInterfaceLayout() {
693 auto header = tree_view->header(); 704 auto* header = tree_view->header();
694 if (!header->restoreState(UISettings::values.gamelist_header_state)) { 705
695 // We are using the name column to display icons and titles 706 if (header->restoreState(UISettings::values.gamelist_header_state)) {
696 // so make it as large as possible as default. 707 return;
697 header->resizeSection(COLUMN_NAME, header->width());
698 } 708 }
709
710 // We are using the name column to display icons and titles
711 // so make it as large as possible as default.
712 header->resizeSection(COLUMN_NAME, header->width());
699} 713}
700 714
701const QStringList GameList::supported_file_extensions = { 715const QStringList GameList::supported_file_extensions = {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 78e2ba169..58059a3c4 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -67,11 +67,11 @@ public:
67 FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr); 67 FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr);
68 ~GameList() override; 68 ~GameList() override;
69 69
70 QString getLastFilterResultItem() const; 70 QString GetLastFilterResultItem() const;
71 void clearFilter(); 71 void ClearFilter();
72 void setFilterFocus(); 72 void SetFilterFocus();
73 void setFilterVisible(bool visibility); 73 void SetFilterVisible(bool visibility);
74 bool isEmpty() const; 74 bool IsEmpty() const;
75 75
76 void LoadCompatibilityList(); 76 void LoadCompatibilityList();
77 void PopulateAsync(QVector<UISettings::GameDir>& game_dirs); 77 void PopulateAsync(QVector<UISettings::GameDir>& game_dirs);
@@ -82,7 +82,7 @@ public:
82 static const QStringList supported_file_extensions; 82 static const QStringList supported_file_extensions;
83 83
84signals: 84signals:
85 void GameChosen(QString game_path); 85 void GameChosen(const QString& game_path);
86 void ShouldCancelWorker(); 86 void ShouldCancelWorker();
87 void OpenFolderRequested(u64 program_id, GameListOpenTarget target, 87 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
88 const std::string& game_path); 88 const std::string& game_path);
@@ -99,21 +99,21 @@ signals:
99 void ShowList(bool show); 99 void ShowList(bool show);
100 100
101private slots: 101private slots:
102 void onItemExpanded(const QModelIndex& item); 102 void OnItemExpanded(const QModelIndex& item);
103 void onTextChanged(const QString& new_text); 103 void OnTextChanged(const QString& new_text);
104 void onFilterCloseClicked(); 104 void OnFilterCloseClicked();
105 void onUpdateThemedIcons(); 105 void OnUpdateThemedIcons();
106 106
107private: 107private:
108 void AddDirEntry(GameListDir* entry_items); 108 void AddDirEntry(GameListDir* entry_items);
109 void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent); 109 void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent);
110 void ValidateEntry(const QModelIndex& item); 110 void ValidateEntry(const QModelIndex& item);
111 void DonePopulating(QStringList watch_list); 111 void DonePopulating(const QStringList& watch_list);
112 112
113 void RefreshGameDirectory(); 113 void RefreshGameDirectory();
114 114
115 void PopupContextMenu(const QPoint& menu_location); 115 void PopupContextMenu(const QPoint& menu_location);
116 void AddGamePopup(QMenu& context_menu, u64 program_id, std::string path); 116 void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path);
117 void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected); 117 void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
118 void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); 118 void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
119 119
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h
index e4aba1b06..68e03fe4e 100644
--- a/src/yuzu/install_dialog.h
+++ b/src/yuzu/install_dialog.h
@@ -20,9 +20,8 @@ public:
20 explicit InstallDialog(QWidget* parent, const QStringList& files); 20 explicit InstallDialog(QWidget* parent, const QStringList& files);
21 ~InstallDialog() override; 21 ~InstallDialog() override;
22 22
23 QStringList GetFiles() const; 23 [[nodiscard]] QStringList GetFiles() const;
24 bool ShouldOverwriteFiles() const; 24 [[nodiscard]] int GetMinimumWidth() const;
25 int GetMinimumWidth() const;
26 25
27private: 26private:
28 QListWidget* file_list; 27 QListWidget* file_list;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bb3a08ac7..6a2a88dd8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -838,7 +838,7 @@ void GMainWindow::RestoreUIState() {
838 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked()); 838 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
839 839
840 ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar); 840 ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar);
841 game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); 841 game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
842 842
843 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); 843 ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
844 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); 844 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
@@ -1199,11 +1199,12 @@ void GMainWindow::ShutdownGame() {
1199 render_window->hide(); 1199 render_window->hide();
1200 loading_screen->hide(); 1200 loading_screen->hide();
1201 loading_screen->Clear(); 1201 loading_screen->Clear();
1202 if (game_list->isEmpty()) 1202 if (game_list->IsEmpty()) {
1203 game_list_placeholder->show(); 1203 game_list_placeholder->show();
1204 else 1204 } else {
1205 game_list->show(); 1205 game_list->show();
1206 game_list->setFilterFocus(); 1206 }
1207 game_list->SetFilterFocus();
1207 1208
1208 setMouseTracking(false); 1209 setMouseTracking(false);
1209 ui.centralwidget->setMouseTracking(false); 1210 ui.centralwidget->setMouseTracking(false);
@@ -2361,11 +2362,11 @@ void GMainWindow::OnAbout() {
2361} 2362}
2362 2363
2363void GMainWindow::OnToggleFilterBar() { 2364void GMainWindow::OnToggleFilterBar() {
2364 game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); 2365 game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
2365 if (ui.action_Show_Filter_Bar->isChecked()) { 2366 if (ui.action_Show_Filter_Bar->isChecked()) {
2366 game_list->setFilterFocus(); 2367 game_list->SetFilterFocus();
2367 } else { 2368 } else {
2368 game_list->clearFilter(); 2369 game_list->ClearFilter();
2369 } 2370 }
2370} 2371}
2371 2372