vk_scheduler.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <memory>
  5. #include <mutex>
  6. #include <optional>
  7. #include <thread>
  8. #include <utility>
  9. #include "common/microprofile.h"
  10. #include "video_core/renderer_vulkan/vk_device.h"
  11. #include "video_core/renderer_vulkan/vk_query_cache.h"
  12. #include "video_core/renderer_vulkan/vk_resource_manager.h"
  13. #include "video_core/renderer_vulkan/vk_scheduler.h"
  14. #include "video_core/renderer_vulkan/vk_state_tracker.h"
  15. #include "video_core/renderer_vulkan/wrapper.h"
  16. namespace Vulkan {
  17. MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
  18. void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
  19. auto command = first;
  20. while (command != nullptr) {
  21. auto next = command->GetNext();
  22. command->Execute(cmdbuf);
  23. command->~Command();
  24. command = next;
  25. }
  26. command_offset = 0;
  27. first = nullptr;
  28. last = nullptr;
  29. }
  30. VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
  31. StateTracker& state_tracker)
  32. : device{device}, resource_manager{resource_manager}, state_tracker{state_tracker},
  33. next_fence{&resource_manager.CommitFence()} {
  34. AcquireNewChunk();
  35. AllocateNewContext();
  36. worker_thread = std::thread(&VKScheduler::WorkerThread, this);
  37. }
  38. VKScheduler::~VKScheduler() {
  39. quit = true;
  40. cv.notify_all();
  41. worker_thread.join();
  42. }
  43. void VKScheduler::Flush(bool release_fence, VkSemaphore semaphore) {
  44. SubmitExecution(semaphore);
  45. if (release_fence) {
  46. current_fence->Release();
  47. }
  48. AllocateNewContext();
  49. }
  50. void VKScheduler::Finish(bool release_fence, VkSemaphore semaphore) {
  51. SubmitExecution(semaphore);
  52. current_fence->Wait();
  53. if (release_fence) {
  54. current_fence->Release();
  55. }
  56. AllocateNewContext();
  57. }
  58. void VKScheduler::WaitWorker() {
  59. MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
  60. DispatchWork();
  61. bool finished = false;
  62. do {
  63. cv.notify_all();
  64. std::unique_lock lock{mutex};
  65. finished = chunk_queue.Empty();
  66. } while (!finished);
  67. }
  68. void VKScheduler::DispatchWork() {
  69. if (chunk->Empty()) {
  70. return;
  71. }
  72. chunk_queue.Push(std::move(chunk));
  73. cv.notify_all();
  74. AcquireNewChunk();
  75. }
  76. void VKScheduler::RequestRenderpass(VkRenderPass renderpass, VkFramebuffer framebuffer,
  77. VkExtent2D render_area) {
  78. if (renderpass == state.renderpass && framebuffer == state.framebuffer &&
  79. render_area.width == state.render_area.width &&
  80. render_area.height == state.render_area.height) {
  81. return;
  82. }
  83. const bool end_renderpass = state.renderpass != nullptr;
  84. state.renderpass = renderpass;
  85. state.framebuffer = framebuffer;
  86. state.render_area = render_area;
  87. VkRenderPassBeginInfo renderpass_bi;
  88. renderpass_bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
  89. renderpass_bi.pNext = nullptr;
  90. renderpass_bi.renderPass = renderpass;
  91. renderpass_bi.framebuffer = framebuffer;
  92. renderpass_bi.renderArea.offset.x = 0;
  93. renderpass_bi.renderArea.offset.y = 0;
  94. renderpass_bi.renderArea.extent = render_area;
  95. renderpass_bi.clearValueCount = 0;
  96. renderpass_bi.pClearValues = nullptr;
  97. Record([renderpass_bi, end_renderpass](vk::CommandBuffer cmdbuf) {
  98. if (end_renderpass) {
  99. cmdbuf.EndRenderPass();
  100. }
  101. cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
  102. });
  103. }
  104. void VKScheduler::RequestOutsideRenderPassOperationContext() {
  105. EndRenderPass();
  106. }
  107. void VKScheduler::BindGraphicsPipeline(VkPipeline pipeline) {
  108. if (state.graphics_pipeline == pipeline) {
  109. return;
  110. }
  111. state.graphics_pipeline = pipeline;
  112. Record([pipeline](vk::CommandBuffer cmdbuf) {
  113. cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
  114. });
  115. }
  116. void VKScheduler::WorkerThread() {
  117. std::unique_lock lock{mutex};
  118. do {
  119. cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; });
  120. if (quit) {
  121. continue;
  122. }
  123. auto extracted_chunk = std::move(chunk_queue.Front());
  124. chunk_queue.Pop();
  125. extracted_chunk->ExecuteAll(current_cmdbuf);
  126. chunk_reserve.Push(std::move(extracted_chunk));
  127. } while (!quit);
  128. }
  129. void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
  130. EndPendingOperations();
  131. InvalidateState();
  132. WaitWorker();
  133. std::unique_lock lock{mutex};
  134. current_cmdbuf.End();
  135. VkSubmitInfo submit_info;
  136. submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  137. submit_info.pNext = nullptr;
  138. submit_info.waitSemaphoreCount = 0;
  139. submit_info.pWaitSemaphores = nullptr;
  140. submit_info.pWaitDstStageMask = nullptr;
  141. submit_info.commandBufferCount = 1;
  142. submit_info.pCommandBuffers = current_cmdbuf.address();
  143. submit_info.signalSemaphoreCount = semaphore ? 1 : 0;
  144. submit_info.pSignalSemaphores = &semaphore;
  145. switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) {
  146. case VK_SUCCESS:
  147. break;
  148. case VK_ERROR_DEVICE_LOST:
  149. device.ReportLoss();
  150. [[fallthrough]];
  151. default:
  152. vk::Check(result);
  153. }
  154. }
  155. void VKScheduler::AllocateNewContext() {
  156. ++ticks;
  157. VkCommandBufferBeginInfo cmdbuf_bi;
  158. cmdbuf_bi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  159. cmdbuf_bi.pNext = nullptr;
  160. cmdbuf_bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  161. cmdbuf_bi.pInheritanceInfo = nullptr;
  162. std::unique_lock lock{mutex};
  163. current_fence = next_fence;
  164. next_fence = &resource_manager.CommitFence();
  165. current_cmdbuf = vk::CommandBuffer(resource_manager.CommitCommandBuffer(*current_fence),
  166. device.GetDispatchLoader());
  167. current_cmdbuf.Begin(cmdbuf_bi);
  168. // Enable counters once again. These are disabled when a command buffer is finished.
  169. if (query_cache) {
  170. query_cache->UpdateCounters();
  171. }
  172. }
  173. void VKScheduler::InvalidateState() {
  174. state.graphics_pipeline = nullptr;
  175. state_tracker.InvalidateCommandBufferState();
  176. }
  177. void VKScheduler::EndPendingOperations() {
  178. query_cache->DisableStreams();
  179. EndRenderPass();
  180. }
  181. void VKScheduler::EndRenderPass() {
  182. if (!state.renderpass) {
  183. return;
  184. }
  185. state.renderpass = nullptr;
  186. Record([](vk::CommandBuffer cmdbuf) { cmdbuf.EndRenderPass(); });
  187. }
  188. void VKScheduler::AcquireNewChunk() {
  189. if (chunk_reserve.Empty()) {
  190. chunk = std::make_unique<CommandChunk>();
  191. return;
  192. }
  193. chunk = std::move(chunk_reserve.Front());
  194. chunk_reserve.Pop();
  195. }
  196. } // namespace Vulkan