summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/service/acc/acc.cpp11
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-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/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_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/async_shaders.h11
-rw-r--r--src/video_core/shader/control_flow.cpp27
-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
54 files changed, 847 insertions, 885 deletions
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index eb54cb123..6b1613510 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -774,6 +774,17 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
774 rb.Push(RESULT_SUCCESS); 774 rb.Push(RESULT_SUCCESS);
775} 775}
776 776
777void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
778 LOG_WARNING(Service_ACC, "(STUBBED) called");
779
780 // This is similar to GetBaasAccountManagerForApplication
781 // This command is used concurrently with ListOpenContextStoredUsers
782 // TODO: Find the differences between this and GetBaasAccountManagerForApplication
783 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
784 rb.Push(RESULT_SUCCESS);
785 rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser());
786}
787
777void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { 788void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
778 LOG_WARNING(Service_ACC, "(STUBBED) called"); 789 LOG_WARNING(Service_ACC, "(STUBBED) called");
779 790
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index d4c6395c6..c611efd89 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -34,6 +34,7 @@ public:
34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); 34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
35 void GetProfileEditor(Kernel::HLERequestContext& ctx); 35 void GetProfileEditor(Kernel::HLERequestContext& ctx);
36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx); 36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
37 void LoadOpenContext(Kernel::HLERequestContext& ctx);
37 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); 38 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
38 39
39 private: 40 private:
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index cb44e06b7..75a24f8f5 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -29,7 +29,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
29 {110, nullptr, "StoreSaveDataThumbnail"}, 29 {110, nullptr, "StoreSaveDataThumbnail"},
30 {111, nullptr, "ClearSaveDataThumbnail"}, 30 {111, nullptr, "ClearSaveDataThumbnail"},
31 {120, nullptr, "CreateGuestLoginRequest"}, 31 {120, nullptr, "CreateGuestLoginRequest"},
32 {130, nullptr, "LoadOpenContext"}, // 5.0.0+ 32 {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+
33 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ 33 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ 34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ 35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
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/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_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/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/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