summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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