vk_scheduler.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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 "common/thread.h"
  11. #include "video_core/renderer_vulkan/vk_command_pool.h"
  12. #include "video_core/renderer_vulkan/vk_master_semaphore.h"
  13. #include "video_core/renderer_vulkan/vk_query_cache.h"
  14. #include "video_core/renderer_vulkan/vk_scheduler.h"
  15. #include "video_core/renderer_vulkan/vk_state_tracker.h"
  16. #include "video_core/renderer_vulkan/vk_texture_cache.h"
  17. #include "video_core/vulkan_common/vulkan_device.h"
  18. #include "video_core/vulkan_common/vulkan_wrapper.h"
  19. namespace Vulkan {
  20. MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
  21. void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
  22. auto command = first;
  23. while (command != nullptr) {
  24. auto next = command->GetNext();
  25. command->Execute(cmdbuf);
  26. command->~Command();
  27. command = next;
  28. }
  29. command_offset = 0;
  30. first = nullptr;
  31. last = nullptr;
  32. }
  33. VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
  34. : device{device_}, state_tracker{state_tracker_},
  35. master_semaphore{std::make_unique<MasterSemaphore>(device)},
  36. command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
  37. AcquireNewChunk();
  38. AllocateNewContext();
  39. worker_thread = std::thread(&VKScheduler::WorkerThread, this);
  40. }
  41. VKScheduler::~VKScheduler() {
  42. quit = true;
  43. cv.notify_all();
  44. worker_thread.join();
  45. }
  46. u64 VKScheduler::CurrentTick() const noexcept {
  47. return master_semaphore->CurrentTick();
  48. }
  49. bool VKScheduler::IsFree(u64 tick) const noexcept {
  50. return master_semaphore->IsFree(tick);
  51. }
  52. void VKScheduler::Wait(u64 tick) {
  53. master_semaphore->Wait(tick);
  54. }
  55. void VKScheduler::Flush(VkSemaphore semaphore) {
  56. SubmitExecution(semaphore);
  57. AllocateNewContext();
  58. }
  59. void VKScheduler::Finish(VkSemaphore semaphore) {
  60. const u64 presubmit_tick = CurrentTick();
  61. SubmitExecution(semaphore);
  62. Wait(presubmit_tick);
  63. AllocateNewContext();
  64. }
  65. void VKScheduler::WaitWorker() {
  66. MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
  67. DispatchWork();
  68. bool finished = false;
  69. do {
  70. cv.notify_all();
  71. std::unique_lock lock{mutex};
  72. finished = chunk_queue.Empty();
  73. } while (!finished);
  74. }
  75. void VKScheduler::DispatchWork() {
  76. if (chunk->Empty()) {
  77. return;
  78. }
  79. chunk_queue.Push(std::move(chunk));
  80. cv.notify_all();
  81. AcquireNewChunk();
  82. }
  83. void VKScheduler::RequestRenderpass(const Framebuffer* framebuffer) {
  84. const VkRenderPass renderpass = framebuffer->RenderPass();
  85. const VkFramebuffer framebuffer_handle = framebuffer->Handle();
  86. const VkExtent2D render_area = framebuffer->RenderArea();
  87. if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer &&
  88. render_area.width == state.render_area.width &&
  89. render_area.height == state.render_area.height) {
  90. return;
  91. }
  92. EndRenderPass();
  93. state.renderpass = renderpass;
  94. state.framebuffer = framebuffer_handle;
  95. state.render_area = render_area;
  96. Record([renderpass, framebuffer_handle, render_area](vk::CommandBuffer cmdbuf) {
  97. const VkRenderPassBeginInfo renderpass_bi{
  98. .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
  99. .pNext = nullptr,
  100. .renderPass = renderpass,
  101. .framebuffer = framebuffer_handle,
  102. .renderArea =
  103. {
  104. .offset = {.x = 0, .y = 0},
  105. .extent = render_area,
  106. },
  107. .clearValueCount = 0,
  108. .pClearValues = nullptr,
  109. };
  110. cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
  111. });
  112. num_renderpass_images = framebuffer->NumImages();
  113. renderpass_images = framebuffer->Images();
  114. renderpass_image_ranges = framebuffer->ImageRanges();
  115. }
  116. void VKScheduler::RequestOutsideRenderPassOperationContext() {
  117. EndRenderPass();
  118. }
  119. void VKScheduler::BindGraphicsPipeline(VkPipeline pipeline) {
  120. if (state.graphics_pipeline == pipeline) {
  121. return;
  122. }
  123. state.graphics_pipeline = pipeline;
  124. Record([pipeline](vk::CommandBuffer cmdbuf) {
  125. cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
  126. });
  127. }
  128. void VKScheduler::WorkerThread() {
  129. Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
  130. std::unique_lock lock{mutex};
  131. do {
  132. cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; });
  133. if (quit) {
  134. continue;
  135. }
  136. auto extracted_chunk = std::move(chunk_queue.Front());
  137. chunk_queue.Pop();
  138. extracted_chunk->ExecuteAll(current_cmdbuf);
  139. chunk_reserve.Push(std::move(extracted_chunk));
  140. } while (!quit);
  141. }
  142. void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
  143. EndPendingOperations();
  144. InvalidateState();
  145. WaitWorker();
  146. std::unique_lock lock{mutex};
  147. current_cmdbuf.End();
  148. const VkSemaphore timeline_semaphore = master_semaphore->Handle();
  149. const u32 num_signal_semaphores = semaphore ? 2U : 1U;
  150. const u64 signal_value = master_semaphore->CurrentTick();
  151. const u64 wait_value = signal_value - 1;
  152. const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
  153. master_semaphore->NextTick();
  154. const std::array signal_values{signal_value, u64(0)};
  155. const std::array signal_semaphores{timeline_semaphore, semaphore};
  156. const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
  157. .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
  158. .pNext = nullptr,
  159. .waitSemaphoreValueCount = 1,
  160. .pWaitSemaphoreValues = &wait_value,
  161. .signalSemaphoreValueCount = num_signal_semaphores,
  162. .pSignalSemaphoreValues = signal_values.data(),
  163. };
  164. const VkSubmitInfo submit_info{
  165. .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
  166. .pNext = &timeline_si,
  167. .waitSemaphoreCount = 1,
  168. .pWaitSemaphores = &timeline_semaphore,
  169. .pWaitDstStageMask = &wait_stage_mask,
  170. .commandBufferCount = 1,
  171. .pCommandBuffers = current_cmdbuf.address(),
  172. .signalSemaphoreCount = num_signal_semaphores,
  173. .pSignalSemaphores = signal_semaphores.data(),
  174. };
  175. switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
  176. case VK_SUCCESS:
  177. break;
  178. case VK_ERROR_DEVICE_LOST:
  179. device.ReportLoss();
  180. [[fallthrough]];
  181. default:
  182. vk::Check(result);
  183. }
  184. }
  185. void VKScheduler::AllocateNewContext() {
  186. std::unique_lock lock{mutex};
  187. current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
  188. current_cmdbuf.Begin({
  189. .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
  190. .pNext = nullptr,
  191. .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
  192. .pInheritanceInfo = nullptr,
  193. });
  194. // Enable counters once again. These are disabled when a command buffer is finished.
  195. if (query_cache) {
  196. query_cache->UpdateCounters();
  197. }
  198. }
  199. void VKScheduler::InvalidateState() {
  200. state.graphics_pipeline = nullptr;
  201. state_tracker.InvalidateCommandBufferState();
  202. }
  203. void VKScheduler::EndPendingOperations() {
  204. query_cache->DisableStreams();
  205. EndRenderPass();
  206. }
  207. void VKScheduler::EndRenderPass() {
  208. if (!state.renderpass) {
  209. return;
  210. }
  211. Record([num_images = num_renderpass_images, images = renderpass_images,
  212. ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) {
  213. std::array<VkImageMemoryBarrier, 9> barriers;
  214. for (size_t i = 0; i < num_images; ++i) {
  215. barriers[i] = VkImageMemoryBarrier{
  216. .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
  217. .pNext = nullptr,
  218. .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
  219. VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
  220. .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
  221. VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
  222. VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
  223. VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
  224. VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
  225. .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
  226. .newLayout = VK_IMAGE_LAYOUT_GENERAL,
  227. .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
  228. .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
  229. .image = images[i],
  230. .subresourceRange = ranges[i],
  231. };
  232. }
  233. cmdbuf.EndRenderPass();
  234. cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
  235. VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
  236. VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
  237. VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, nullptr, nullptr,
  238. vk::Span(barriers.data(), num_images));
  239. });
  240. state.renderpass = nullptr;
  241. num_renderpass_images = 0;
  242. }
  243. void VKScheduler::AcquireNewChunk() {
  244. if (chunk_reserve.Empty()) {
  245. chunk = std::make_unique<CommandChunk>();
  246. return;
  247. }
  248. chunk = std::move(chunk_reserve.Front());
  249. chunk_reserve.Pop();
  250. }
  251. } // namespace Vulkan