summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-04-26 22:11:31 -0300
committerGravatar ameerj2021-07-22 21:51:29 -0400
commit53acdda772a8b7650c46ba9d998119b8c8e30844 (patch)
tree5f98a9a19093704513fffd17dc7326cdbf5e6f91
parentvk_compute_pass: Fix -Wshadow warning (diff)
downloadyuzu-53acdda772a8b7650c46ba9d998119b8c8e30844.tar.gz
yuzu-53acdda772a8b7650c46ba9d998119b8c8e30844.tar.xz
yuzu-53acdda772a8b7650c46ba9d998119b8c8e30844.zip
vk_scheduler: Allow command submission on worker thread
This changes how Scheduler::Flush works. It queues the current command buffer to be sent to the GPU but does not do it immediately. The Vulkan worker thread takes care of that. Users will have to use Scheduler::Flush + Scheduler::WaitWorker to get the previous behavior. Scheduler::Finish is unchanged. To avoid waiting on work never queued, Scheduler::Wait sends the current command buffer if that's what the caller wants to wait.
Diffstat (limited to '')
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp75
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp94
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp120
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp39
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h23
8 files changed, 200 insertions, 182 deletions
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index bec3a81d9..7e39b65bd 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -97,19 +97,14 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
97 Core::Frontend::EmuWindow& emu_window, 97 Core::Frontend::EmuWindow& emu_window,
98 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, 98 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
99 std::unique_ptr<Core::Frontend::GraphicsContext> context_) try 99 std::unique_ptr<Core::Frontend::GraphicsContext> context_) try
100 : RendererBase(emu_window, std::move(context_)), 100 : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
101 telemetry_session(telemetry_session_), 101 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()),
102 cpu_memory(cpu_memory_),
103 gpu(gpu_),
104 library(OpenLibrary()),
105 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 102 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
106 true, Settings::values.renderer_debug.GetValue())), 103 true, Settings::values.renderer_debug.GetValue())),
107 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 104 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
108 surface(CreateSurface(instance, render_window)), 105 surface(CreateSurface(instance, render_window)),
109 device(CreateDevice(instance, dld, *surface)), 106 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
110 memory_allocator(device, false), 107 state_tracker(gpu), scheduler(device, state_tracker),
111 state_tracker(gpu),
112 scheduler(device, state_tracker),
113 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, 108 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
114 render_window.GetFramebufferLayout().height, false), 109 render_window.GetFramebufferLayout().height, false),
115 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, 110 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler,
@@ -130,35 +125,47 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
130 if (!framebuffer) { 125 if (!framebuffer) {
131 return; 126 return;
132 } 127 }
133 const auto& layout = render_window.GetFramebufferLayout(); 128 SCOPE_EXIT({ render_window.OnFrameDisplayed(); });
134 if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) { 129 if (!render_window.IsShown()) {
135 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 130 return;
136 const bool use_accelerated = 131 }
137 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 132 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
138 const bool is_srgb = use_accelerated && screen_info.is_srgb; 133 const bool use_accelerated =
139 if (swapchain.HasFramebufferChanged(layout) || swapchain.GetSrgbState() != is_srgb) { 134 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
140 swapchain.Create(layout.width, layout.height, is_srgb); 135 const bool is_srgb = use_accelerated && screen_info.is_srgb;
141 blit_screen.Recreate(); 136
142 } 137 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
143 138 bool has_been_recreated = false;
144 scheduler.WaitWorker(); 139 const auto recreate_swapchain = [&] {
145 140 if (!has_been_recreated) {
146 while (!swapchain.AcquireNextImage()) { 141 has_been_recreated = true;
147 swapchain.Create(layout.width, layout.height, is_srgb); 142 scheduler.WaitWorker();
148 blit_screen.Recreate();
149 } 143 }
150 const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated); 144 swapchain.Create(layout.width, layout.height, is_srgb);
151 145 };
152 scheduler.Flush(render_semaphore); 146 if (swapchain.NeedsRecreate() ||
153 147 swapchain.HasDifferentLayout(layout.width, layout.height, is_srgb)) {
154 if (swapchain.Present(render_semaphore)) { 148 recreate_swapchain();
155 blit_screen.Recreate(); 149 }
150 bool needs_recreate;
151 do {
152 needs_recreate = false;
153 swapchain.AcquireNextImage();
154 if (swapchain.NeedsRecreate()) {
155 recreate_swapchain();
156 needs_recreate = true;
156 } 157 }
157 gpu.RendererFrameEndNotify(); 158 } while (needs_recreate);
158 rasterizer.TickFrame(); 159 if (has_been_recreated) {
160 blit_screen.Recreate();
159 } 161 }
162 const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated);
163 scheduler.Flush(render_semaphore);
164 scheduler.WaitWorker();
165 swapchain.Present(render_semaphore);
160 166
161 render_window.OnFrameDisplayed(); 167 gpu.RendererFrameEndNotify();
168 rasterizer.TickFrame();
162} 169}
163 170
164void RendererVulkan::Report() const { 171void RendererVulkan::Report() const {
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 363134129..516f428e7 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -184,47 +184,43 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
184 .depth = 1, 184 .depth = 1,
185 }, 185 },
186 }; 186 };
187 scheduler.Record( 187 scheduler.Record([this, copy, image_index](vk::CommandBuffer cmdbuf) {
188 [buffer = *buffer, image = *raw_images[image_index], copy](vk::CommandBuffer cmdbuf) { 188 const VkImage image = *raw_images[image_index];
189 const VkImageMemoryBarrier base_barrier{ 189 const VkImageMemoryBarrier base_barrier{
190 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 190 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
191 .pNext = nullptr, 191 .pNext = nullptr,
192 .srcAccessMask = 0, 192 .srcAccessMask = 0,
193 .dstAccessMask = 0, 193 .dstAccessMask = 0,
194 .oldLayout = VK_IMAGE_LAYOUT_GENERAL, 194 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
195 .newLayout = VK_IMAGE_LAYOUT_GENERAL, 195 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
196 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 196 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
197 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 197 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
198 .image = image, 198 .image = image,
199 .subresourceRange = 199 .subresourceRange{
200 { 200 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
201 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 201 .baseMipLevel = 0,
202 .baseMipLevel = 0, 202 .levelCount = 1,
203 .levelCount = 1, 203 .baseArrayLayer = 0,
204 .baseArrayLayer = 0, 204 .layerCount = 1,
205 .layerCount = 1, 205 },
206 }, 206 };
207 }; 207 VkImageMemoryBarrier read_barrier = base_barrier;
208 VkImageMemoryBarrier read_barrier = base_barrier; 208 read_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
209 read_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; 209 read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
210 read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 210 read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
211 read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; 211
212 212 VkImageMemoryBarrier write_barrier = base_barrier;
213 VkImageMemoryBarrier write_barrier = base_barrier; 213 write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
214 write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 214 write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
215 write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; 215
216 216 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
217 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 217 read_barrier);
218 0, read_barrier); 218 cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy);
219 cmdbuf.CopyBufferToImage(buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy); 219 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
220 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, 220 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
221 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); 221 });
222 });
223 } 222 }
224 scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index], 223 scheduler.Record([this, image_index, size = swapchain.GetSize()](vk::CommandBuffer cmdbuf) {
225 descriptor_set = descriptor_sets[image_index], buffer = *buffer,
226 size = swapchain.GetSize(), pipeline = *pipeline,
227 layout = *pipeline_layout](vk::CommandBuffer cmdbuf) {
228 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 224 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
229 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 225 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
230 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; 226 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
@@ -234,8 +230,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
234 const VkRenderPassBeginInfo renderpass_bi{ 230 const VkRenderPassBeginInfo renderpass_bi{
235 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 231 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
236 .pNext = nullptr, 232 .pNext = nullptr,
237 .renderPass = renderpass, 233 .renderPass = *renderpass,
238 .framebuffer = framebuffer, 234 .framebuffer = *framebuffers[image_index],
239 .renderArea = 235 .renderArea =
240 { 236 {
241 .offset = {0, 0}, 237 .offset = {0, 0},
@@ -257,12 +253,13 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
257 .extent = size, 253 .extent = size,
258 }; 254 };
259 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); 255 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
260 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); 256 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
261 cmdbuf.SetViewport(0, viewport); 257 cmdbuf.SetViewport(0, viewport);
262 cmdbuf.SetScissor(0, scissor); 258 cmdbuf.SetScissor(0, scissor);
263 259
264 cmdbuf.BindVertexBuffer(0, buffer, offsetof(BufferData, vertices)); 260 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
265 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {}); 261 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
262 descriptor_sets[image_index], {});
266 cmdbuf.Draw(4, 1, 0, 0); 263 cmdbuf.Draw(4, 1, 0, 0);
267 cmdbuf.EndRenderPass(); 264 cmdbuf.EndRenderPass();
268 }); 265 });
@@ -304,8 +301,7 @@ void VKBlitScreen::CreateShaders() {
304 301
305void VKBlitScreen::CreateSemaphores() { 302void VKBlitScreen::CreateSemaphores() {
306 semaphores.resize(image_count); 303 semaphores.resize(image_count);
307 std::generate(semaphores.begin(), semaphores.end(), 304 std::ranges::generate(semaphores, [this] { return device.GetLogical().CreateSemaphore(); });
308 [this] { return device.GetLogical().CreateSemaphore(); });
309} 305}
310 306
311void VKBlitScreen::CreateDescriptorPool() { 307void VKBlitScreen::CreateDescriptorPool() {
@@ -633,8 +629,8 @@ void VKBlitScreen::CreateFramebuffers() {
633} 629}
634 630
635void VKBlitScreen::ReleaseRawImages() { 631void VKBlitScreen::ReleaseRawImages() {
636 for (std::size_t i = 0; i < raw_images.size(); ++i) { 632 for (const u64 tick : resource_ticks) {
637 scheduler.Wait(resource_ticks.at(i)); 633 scheduler.Wait(tick);
638 } 634 }
639 raw_images.clear(); 635 raw_images.clear();
640 raw_buffer_commits.clear(); 636 raw_buffer_commits.clear();
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 7cadd5147..1dd78328c 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -114,10 +114,13 @@ void HostCounter::EndQuery() {
114} 114}
115 115
116u64 HostCounter::BlockingQuery() const { 116u64 HostCounter::BlockingQuery() const {
117 if (tick >= cache.GetScheduler().CurrentTick()) { 117 auto& scheduler{cache.GetScheduler()};
118 cache.GetScheduler().Flush(); 118 if (tick >= scheduler.CurrentTick()) {
119 scheduler.Flush();
120 // This may not be necessary, but it's better to play it safe and assume drivers don't
121 // support wait before signal on vkGetQueryPoolResults
122 scheduler.WaitWorker();
119 } 123 }
120
121 u64 data; 124 u64 data;
122 const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults( 125 const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults(
123 query.first, query.second, 1, sizeof(data), &data, sizeof(data), 126 query.first, query.second, 1, sizeof(data), &data, sizeof(data),
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index fa6daeb3a..0f15ad2f7 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -452,10 +452,11 @@ void RasterizerVulkan::TiledCacheBarrier() {
452} 452}
453 453
454void RasterizerVulkan::FlushCommands() { 454void RasterizerVulkan::FlushCommands() {
455 if (draw_counter > 0) { 455 if (draw_counter == 0) {
456 draw_counter = 0; 456 return;
457 scheduler.Flush();
458 } 457 }
458 draw_counter = 0;
459 scheduler.Flush();
459} 460}
460 461
461void RasterizerVulkan::TickFrame() { 462void RasterizerVulkan::TickFrame() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 25a4933e5..81cb330d9 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -31,7 +31,7 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
31 command->~Command(); 31 command->~Command();
32 command = next; 32 command = next;
33 } 33 }
34 34 submit = false;
35 command_offset = 0; 35 command_offset = 0;
36 first = nullptr; 36 first = nullptr;
37 last = nullptr; 37 last = nullptr;
@@ -42,7 +42,7 @@ VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
42 master_semaphore{std::make_unique<MasterSemaphore>(device)}, 42 master_semaphore{std::make_unique<MasterSemaphore>(device)},
43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} { 43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
44 AcquireNewChunk(); 44 AcquireNewChunk();
45 AllocateNewContext(); 45 AllocateWorkerCommandBuffer();
46 worker_thread = std::thread(&VKScheduler::WorkerThread, this); 46 worker_thread = std::thread(&VKScheduler::WorkerThread, this);
47} 47}
48 48
@@ -60,6 +60,7 @@ void VKScheduler::Flush(VkSemaphore semaphore) {
60void VKScheduler::Finish(VkSemaphore semaphore) { 60void VKScheduler::Finish(VkSemaphore semaphore) {
61 const u64 presubmit_tick = CurrentTick(); 61 const u64 presubmit_tick = CurrentTick();
62 SubmitExecution(semaphore); 62 SubmitExecution(semaphore);
63 WaitWorker();
63 Wait(presubmit_tick); 64 Wait(presubmit_tick);
64 AllocateNewContext(); 65 AllocateNewContext();
65} 66}
@@ -140,75 +141,82 @@ void VKScheduler::WorkerThread() {
140 if (quit) { 141 if (quit) {
141 continue; 142 continue;
142 } 143 }
143 auto extracted_chunk = std::move(chunk_queue.Front()); 144 while (!chunk_queue.Empty()) {
144 chunk_queue.Pop(); 145 auto extracted_chunk = std::move(chunk_queue.Front());
145 extracted_chunk->ExecuteAll(current_cmdbuf); 146 chunk_queue.Pop();
146 chunk_reserve.Push(std::move(extracted_chunk)); 147 const bool has_submit = extracted_chunk->HasSubmit();
148 extracted_chunk->ExecuteAll(current_cmdbuf);
149 if (has_submit) {
150 AllocateWorkerCommandBuffer();
151 }
152 chunk_reserve.Push(std::move(extracted_chunk));
153 }
147 } while (!quit); 154 } while (!quit);
148} 155}
149 156
157void VKScheduler::AllocateWorkerCommandBuffer() {
158 current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
159 current_cmdbuf.Begin({
160 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
161 .pNext = nullptr,
162 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
163 .pInheritanceInfo = nullptr,
164 });
165}
166
150void VKScheduler::SubmitExecution(VkSemaphore semaphore) { 167void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
151 EndPendingOperations(); 168 EndPendingOperations();
152 InvalidateState(); 169 InvalidateState();
153 WaitWorker();
154
155 std::unique_lock lock{mutex};
156
157 current_cmdbuf.End();
158
159 const VkSemaphore timeline_semaphore = master_semaphore->Handle();
160 const u32 num_signal_semaphores = semaphore ? 2U : 1U;
161 170
162 const u64 signal_value = master_semaphore->CurrentTick(); 171 const u64 signal_value = master_semaphore->CurrentTick();
163 const u64 wait_value = signal_value - 1;
164 const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
165
166 master_semaphore->NextTick(); 172 master_semaphore->NextTick();
167 173
168 const std::array signal_values{signal_value, u64(0)}; 174 Record([semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
169 const std::array signal_semaphores{timeline_semaphore, semaphore}; 175 cmdbuf.End();
170 176
171 const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ 177 const u32 num_signal_semaphores = semaphore ? 2U : 1U;
172 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
173 .pNext = nullptr,
174 .waitSemaphoreValueCount = 1,
175 .pWaitSemaphoreValues = &wait_value,
176 .signalSemaphoreValueCount = num_signal_semaphores,
177 .pSignalSemaphoreValues = signal_values.data(),
178 };
179 const VkSubmitInfo submit_info{
180 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
181 .pNext = &timeline_si,
182 .waitSemaphoreCount = 1,
183 .pWaitSemaphores = &timeline_semaphore,
184 .pWaitDstStageMask = &wait_stage_mask,
185 .commandBufferCount = 1,
186 .pCommandBuffers = current_cmdbuf.address(),
187 .signalSemaphoreCount = num_signal_semaphores,
188 .pSignalSemaphores = signal_semaphores.data(),
189 };
190 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
191 case VK_SUCCESS:
192 break;
193 case VK_ERROR_DEVICE_LOST:
194 device.ReportLoss();
195 [[fallthrough]];
196 default:
197 vk::Check(result);
198 }
199}
200 178
201void VKScheduler::AllocateNewContext() { 179 const u64 wait_value = signal_value - 1;
202 std::unique_lock lock{mutex}; 180 const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
203 181
204 current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader()); 182 const VkSemaphore timeline_semaphore = master_semaphore->Handle();
205 current_cmdbuf.Begin({ 183 const std::array signal_values{signal_value, u64(0)};
206 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 184 const std::array signal_semaphores{timeline_semaphore, semaphore};
207 .pNext = nullptr, 185
208 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 186 const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
209 .pInheritanceInfo = nullptr, 187 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
188 .pNext = nullptr,
189 .waitSemaphoreValueCount = 1,
190 .pWaitSemaphoreValues = &wait_value,
191 .signalSemaphoreValueCount = num_signal_semaphores,
192 .pSignalSemaphoreValues = signal_values.data(),
193 };
194 const VkSubmitInfo submit_info{
195 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
196 .pNext = &timeline_si,
197 .waitSemaphoreCount = 1,
198 .pWaitSemaphores = &timeline_semaphore,
199 .pWaitDstStageMask = &wait_stage_mask,
200 .commandBufferCount = 1,
201 .pCommandBuffers = cmdbuf.address(),
202 .signalSemaphoreCount = num_signal_semaphores,
203 .pSignalSemaphores = signal_semaphores.data(),
204 };
205 switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
206 case VK_SUCCESS:
207 break;
208 case VK_ERROR_DEVICE_LOST:
209 device.ReportLoss();
210 [[fallthrough]];
211 default:
212 vk::Check(result);
213 }
210 }); 214 });
215 chunk->MarkSubmit();
216 DispatchWork();
217}
211 218
219void VKScheduler::AllocateNewContext() {
212 // Enable counters once again. These are disabled when a command buffer is finished. 220 // Enable counters once again. These are disabled when a command buffer is finished.
213 if (query_cache) { 221 if (query_cache) {
214 query_cache->UpdateCounters(); 222 query_cache->UpdateCounters();
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index a40bb8bcd..40215c4c5 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -86,6 +86,10 @@ public:
86 86
87 /// Waits for the given tick to trigger on the GPU. 87 /// Waits for the given tick to trigger on the GPU.
88 void Wait(u64 tick) { 88 void Wait(u64 tick) {
89 if (tick >= master_semaphore->CurrentTick()) {
90 // Make sure we are not waiting for the current tick without signalling
91 Flush();
92 }
89 master_semaphore->Wait(tick); 93 master_semaphore->Wait(tick);
90 } 94 }
91 95
@@ -155,15 +159,24 @@ private:
155 return true; 159 return true;
156 } 160 }
157 161
162 void MarkSubmit() {
163 submit = true;
164 }
165
158 bool Empty() const { 166 bool Empty() const {
159 return command_offset == 0; 167 return command_offset == 0;
160 } 168 }
161 169
170 bool HasSubmit() const {
171 return submit;
172 }
173
162 private: 174 private:
163 Command* first = nullptr; 175 Command* first = nullptr;
164 Command* last = nullptr; 176 Command* last = nullptr;
165 177
166 size_t command_offset = 0; 178 size_t command_offset = 0;
179 bool submit = false;
167 alignas(std::max_align_t) std::array<u8, 0x8000> data{}; 180 alignas(std::max_align_t) std::array<u8, 0x8000> data{};
168 }; 181 };
169 182
@@ -176,6 +189,8 @@ private:
176 189
177 void WorkerThread(); 190 void WorkerThread();
178 191
192 void AllocateWorkerCommandBuffer();
193
179 void SubmitExecution(VkSemaphore semaphore); 194 void SubmitExecution(VkSemaphore semaphore);
180 195
181 void AllocateNewContext(); 196 void AllocateNewContext();
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index dfd5c65ba..a71b0b01e 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -65,6 +65,8 @@ VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const Device& device_, VKSchedul
65VKSwapchain::~VKSwapchain() = default; 65VKSwapchain::~VKSwapchain() = default;
66 66
67void VKSwapchain::Create(u32 width, u32 height, bool srgb) { 67void VKSwapchain::Create(u32 width, u32 height, bool srgb) {
68 needs_recreate = false;
69
68 const auto physical_device = device.GetPhysical(); 70 const auto physical_device = device.GetPhysical();
69 const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; 71 const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)};
70 if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) { 72 if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) {
@@ -82,21 +84,20 @@ void VKSwapchain::Create(u32 width, u32 height, bool srgb) {
82 resource_ticks.resize(image_count); 84 resource_ticks.resize(image_count);
83} 85}
84 86
85bool VKSwapchain::AcquireNextImage() { 87void VKSwapchain::AcquireNextImage() {
86 const VkResult result = 88 const VkResult result =
87 device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(), 89 device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
88 *present_semaphores[frame_index], {}, &image_index); 90 *present_semaphores[frame_index], {}, &image_index);
91 needs_recreate |= result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR;
89 92
90 scheduler.Wait(resource_ticks[image_index]); 93 scheduler.Wait(resource_ticks[image_index]);
91 return result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR; 94 resource_ticks[image_index] = scheduler.CurrentTick();
92} 95}
93 96
94bool VKSwapchain::Present(VkSemaphore render_semaphore) { 97void VKSwapchain::Present(VkSemaphore render_semaphore) {
95 const VkSemaphore present_semaphore{*present_semaphores[frame_index]}; 98 const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
96 const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore}; 99 const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
97 const auto present_queue{device.GetPresentQueue()}; 100 const auto present_queue{device.GetPresentQueue()};
98 bool recreated = false;
99
100 const VkPresentInfoKHR present_info{ 101 const VkPresentInfoKHR present_info{
101 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 102 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
102 .pNext = nullptr, 103 .pNext = nullptr,
@@ -107,7 +108,6 @@ bool VKSwapchain::Present(VkSemaphore render_semaphore) {
107 .pImageIndices = &image_index, 108 .pImageIndices = &image_index,
108 .pResults = nullptr, 109 .pResults = nullptr,
109 }; 110 };
110
111 switch (const VkResult result = present_queue.Present(present_info)) { 111 switch (const VkResult result = present_queue.Present(present_info)) {
112 case VK_SUCCESS: 112 case VK_SUCCESS:
113 break; 113 break;
@@ -115,24 +115,16 @@ bool VKSwapchain::Present(VkSemaphore render_semaphore) {
115 LOG_DEBUG(Render_Vulkan, "Suboptimal swapchain"); 115 LOG_DEBUG(Render_Vulkan, "Suboptimal swapchain");
116 break; 116 break;
117 case VK_ERROR_OUT_OF_DATE_KHR: 117 case VK_ERROR_OUT_OF_DATE_KHR:
118 if (current_width > 0 && current_height > 0) { 118 needs_recreate = true;
119 Create(current_width, current_height, current_srgb);
120 recreated = true;
121 }
122 break; 119 break;
123 default: 120 default:
124 LOG_CRITICAL(Render_Vulkan, "Failed to present with error {}", vk::ToString(result)); 121 LOG_CRITICAL(Render_Vulkan, "Failed to present with error {}", vk::ToString(result));
125 break; 122 break;
126 } 123 }
127 124 ++frame_index;
128 resource_ticks[image_index] = scheduler.CurrentTick(); 125 if (frame_index >= image_count) {
129 frame_index = (frame_index + 1) % static_cast<u32>(image_count); 126 frame_index = 0;
130 return recreated; 127 }
131}
132
133bool VKSwapchain::HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const {
134 // TODO(Rodrigo): Handle framebuffer pixel format changes
135 return framebuffer.width != current_width || framebuffer.height != current_height;
136} 128}
137 129
138void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, 130void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width,
@@ -148,7 +140,6 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
148 if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) { 140 if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
149 requested_image_count = capabilities.maxImageCount; 141 requested_image_count = capabilities.maxImageCount;
150 } 142 }
151
152 VkSwapchainCreateInfoKHR swapchain_ci{ 143 VkSwapchainCreateInfoKHR swapchain_ci{
153 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 144 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
154 .pNext = nullptr, 145 .pNext = nullptr,
@@ -169,7 +160,6 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
169 .clipped = VK_FALSE, 160 .clipped = VK_FALSE,
170 .oldSwapchain = nullptr, 161 .oldSwapchain = nullptr,
171 }; 162 };
172
173 const u32 graphics_family{device.GetGraphicsFamily()}; 163 const u32 graphics_family{device.GetGraphicsFamily()};
174 const u32 present_family{device.GetPresentFamily()}; 164 const u32 present_family{device.GetPresentFamily()};
175 const std::array<u32, 2> queue_indices{graphics_family, present_family}; 165 const std::array<u32, 2> queue_indices{graphics_family, present_family};
@@ -178,7 +168,6 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
178 swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size()); 168 swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
179 swapchain_ci.pQueueFamilyIndices = queue_indices.data(); 169 swapchain_ci.pQueueFamilyIndices = queue_indices.data();
180 } 170 }
181
182 // Request the size again to reduce the possibility of a TOCTOU race condition. 171 // Request the size again to reduce the possibility of a TOCTOU race condition.
183 const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface); 172 const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface);
184 swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height); 173 swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height);
@@ -186,8 +175,6 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
186 swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci); 175 swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci);
187 176
188 extent = swapchain_ci.imageExtent; 177 extent = swapchain_ci.imageExtent;
189 current_width = extent.width;
190 current_height = extent.height;
191 current_srgb = srgb; 178 current_srgb = srgb;
192 179
193 images = swapchain.GetImages(); 180 images = swapchain.GetImages();
@@ -197,8 +184,8 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
197 184
198void VKSwapchain::CreateSemaphores() { 185void VKSwapchain::CreateSemaphores() {
199 present_semaphores.resize(image_count); 186 present_semaphores.resize(image_count);
200 std::generate(present_semaphores.begin(), present_semaphores.end(), 187 std::ranges::generate(present_semaphores,
201 [this] { return device.GetLogical().CreateSemaphore(); }); 188 [this] { return device.GetLogical().CreateSemaphore(); });
202} 189}
203 190
204void VKSwapchain::CreateImageViews() { 191void VKSwapchain::CreateImageViews() {
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index adc8d27cf..b38fd9dc2 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -28,14 +28,20 @@ public:
28 void Create(u32 width, u32 height, bool srgb); 28 void Create(u32 width, u32 height, bool srgb);
29 29
30 /// Acquires the next image in the swapchain, waits as needed. 30 /// Acquires the next image in the swapchain, waits as needed.
31 bool AcquireNextImage(); 31 void AcquireNextImage();
32 32
33 /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be 33 /// Presents the rendered image to the swapchain.
34 /// recreated. Takes responsability for the ownership of fence. 34 void Present(VkSemaphore render_semaphore);
35 bool Present(VkSemaphore render_semaphore);
36 35
37 /// Returns true when the framebuffer layout has changed. 36 /// Returns true when the framebuffer layout has changed.
38 bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const; 37 bool HasDifferentLayout(u32 width, u32 height, bool is_srgb) const {
38 return extent.width != width || extent.height != height || current_srgb != is_srgb;
39 }
40
41 /// Returns true when the image has to be recreated.
42 bool NeedsRecreate() const {
43 return needs_recreate;
44 }
39 45
40 VkExtent2D GetSize() const { 46 VkExtent2D GetSize() const {
41 return extent; 47 return extent;
@@ -61,10 +67,6 @@ public:
61 return image_format; 67 return image_format;
62 } 68 }
63 69
64 bool GetSrgbState() const {
65 return current_srgb;
66 }
67
68private: 70private:
69 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, 71 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height,
70 bool srgb); 72 bool srgb);
@@ -92,9 +94,8 @@ private:
92 VkFormat image_format{}; 94 VkFormat image_format{};
93 VkExtent2D extent{}; 95 VkExtent2D extent{};
94 96
95 u32 current_width{};
96 u32 current_height{};
97 bool current_srgb{}; 97 bool current_srgb{};
98 bool needs_recreate{};
98}; 99};
99 100
100} // namespace Vulkan 101} // namespace Vulkan