/ src / video_core / renderer_vulkan / vk_master_semaphore.cpp
vk_master_semaphore.cpp
  1  // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2  // SPDX-License-Identifier: GPL-2.0-or-later
  3  
  4  #include <limits>
  5  #include <mutex>
  6  #include "video_core/renderer_vulkan/vk_instance.h"
  7  #include "video_core/renderer_vulkan/vk_master_semaphore.h"
  8  #include "video_core/renderer_vulkan/vk_scheduler.h"
  9  
 10  namespace Vulkan {
 11  
 12  constexpr u64 WAIT_TIMEOUT = std::numeric_limits<u64>::max();
 13  
 14  MasterSemaphoreTimeline::MasterSemaphoreTimeline(const Instance& instance_) : instance{instance_} {
 15      const vk::StructureChain semaphore_chain = {
 16          vk::SemaphoreCreateInfo{},
 17          vk::SemaphoreTypeCreateInfoKHR{
 18              .semaphoreType = vk::SemaphoreType::eTimeline,
 19              .initialValue = 0,
 20          },
 21      };
 22      semaphore = instance.GetDevice().createSemaphoreUnique(semaphore_chain.get());
 23  }
 24  
 25  MasterSemaphoreTimeline::~MasterSemaphoreTimeline() = default;
 26  
 27  void MasterSemaphoreTimeline::Refresh() {
 28      u64 this_tick{};
 29      u64 counter{};
 30      do {
 31          this_tick = gpu_tick.load(std::memory_order_acquire);
 32          counter = instance.GetDevice().getSemaphoreCounterValueKHR(*semaphore);
 33          if (counter < this_tick) {
 34              return;
 35          }
 36      } while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
 37                                               std::memory_order_relaxed));
 38  }
 39  
 40  void MasterSemaphoreTimeline::Wait(u64 tick) {
 41      // No need to wait if the GPU is ahead of the tick
 42      if (IsFree(tick)) {
 43          return;
 44      }
 45      // Update the GPU tick and try again
 46      Refresh();
 47      if (IsFree(tick)) {
 48          return;
 49      }
 50  
 51      // If none of the above is hit, fallback to a regular wait
 52      const vk::SemaphoreWaitInfoKHR wait_info = {
 53          .semaphoreCount = 1,
 54          .pSemaphores = &semaphore.get(),
 55          .pValues = &tick,
 56      };
 57  
 58      while (instance.GetDevice().waitSemaphoresKHR(&wait_info, WAIT_TIMEOUT) !=
 59             vk::Result::eSuccess) {
 60      }
 61      Refresh();
 62  }
 63  
 64  void MasterSemaphoreTimeline::SubmitWork(vk::CommandBuffer cmdbuf, vk::Semaphore wait,
 65                                           vk::Semaphore signal, u64 signal_value) {
 66      cmdbuf.end();
 67  
 68      const u32 num_signal_semaphores = signal ? 2U : 1U;
 69      const std::array signal_values{signal_value, u64(0)};
 70      const std::array signal_semaphores{Handle(), signal};
 71  
 72      const u32 num_wait_semaphores = wait ? 2U : 1U;
 73      const std::array wait_values{signal_value - 1, u64(1)};
 74      const std::array wait_semaphores{Handle(), wait};
 75  
 76      static constexpr std::array<vk::PipelineStageFlags, 2> wait_stage_masks = {
 77          vk::PipelineStageFlagBits::eAllCommands,
 78          vk::PipelineStageFlagBits::eColorAttachmentOutput,
 79      };
 80  
 81      const vk::TimelineSemaphoreSubmitInfoKHR timeline_si = {
 82          .waitSemaphoreValueCount = num_wait_semaphores,
 83          .pWaitSemaphoreValues = wait_values.data(),
 84          .signalSemaphoreValueCount = num_signal_semaphores,
 85          .pSignalSemaphoreValues = signal_values.data(),
 86      };
 87  
 88      const vk::SubmitInfo submit_info = {
 89          .pNext = &timeline_si,
 90          .waitSemaphoreCount = num_wait_semaphores,
 91          .pWaitSemaphores = wait_semaphores.data(),
 92          .pWaitDstStageMask = wait_stage_masks.data(),
 93          .commandBufferCount = 1u,
 94          .pCommandBuffers = &cmdbuf,
 95          .signalSemaphoreCount = num_signal_semaphores,
 96          .pSignalSemaphores = signal_semaphores.data(),
 97      };
 98  
 99      try {
100          instance.GetGraphicsQueue().submit(submit_info);
101      } catch (vk::DeviceLostError& err) {
102          LOG_CRITICAL(Render_Vulkan, "Device lost during submit: {}", err.what());
103          UNREACHABLE();
104      }
105  }
106  
107  constexpr u64 FENCE_RESERVE = 8;
108  
109  MasterSemaphoreFence::MasterSemaphoreFence(const Instance& instance_) : instance{instance_} {
110      const vk::Device device{instance.GetDevice()};
111      for (u64 i = 0; i < FENCE_RESERVE; i++) {
112          free_queue.push(device.createFenceUnique({}));
113      }
114      wait_thread = std::jthread([this](std::stop_token token) { WaitThread(token); });
115  }
116  
117  MasterSemaphoreFence::~MasterSemaphoreFence() = default;
118  
119  void MasterSemaphoreFence::Refresh() {}
120  
121  void MasterSemaphoreFence::Wait(u64 tick) {
122      while (true) {
123          u64 current_value = gpu_tick.load(std::memory_order_relaxed);
124          if (current_value >= tick) {
125              return;
126          }
127          gpu_tick.wait(current_value);
128      }
129  }
130  
131  void MasterSemaphoreFence::SubmitWork(vk::CommandBuffer cmdbuf, vk::Semaphore wait,
132                                        vk::Semaphore signal, u64 signal_value) {
133      cmdbuf.end();
134  
135      const u32 num_signal_semaphores = signal ? 1U : 0U;
136      const u32 num_wait_semaphores = wait ? 1U : 0U;
137  
138      static constexpr std::array<vk::PipelineStageFlags, 1> wait_stage_masks = {
139          vk::PipelineStageFlagBits::eColorAttachmentOutput,
140      };
141  
142      const vk::SubmitInfo submit_info = {
143          .waitSemaphoreCount = num_wait_semaphores,
144          .pWaitSemaphores = &wait,
145          .pWaitDstStageMask = wait_stage_masks.data(),
146          .commandBufferCount = 1u,
147          .pCommandBuffers = &cmdbuf,
148          .signalSemaphoreCount = num_signal_semaphores,
149          .pSignalSemaphores = &signal,
150      };
151  
152      vk::UniqueFence fence{GetFreeFence()};
153      try {
154          instance.GetGraphicsQueue().submit(submit_info, *fence);
155      } catch (vk::DeviceLostError& err) {
156          LOG_CRITICAL(Render_Vulkan, "Device lost during submit: {}", err.what());
157          UNREACHABLE();
158      }
159  
160      std::scoped_lock lock{wait_mutex};
161      wait_queue.push({
162          .handle = std::move(fence),
163          .signal_value = signal_value,
164      });
165      wait_cv.notify_one();
166  }
167  
168  void MasterSemaphoreFence::WaitThread(std::stop_token token) {
169      const vk::Device device{instance.GetDevice()};
170      while (!token.stop_requested()) {
171          Fence fence;
172          {
173              std::unique_lock lock{wait_mutex};
174              Common::CondvarWait(wait_cv, lock, token, [this] { return !wait_queue.empty(); });
175              if (token.stop_requested()) {
176                  return;
177              }
178              fence = std::move(wait_queue.front());
179              wait_queue.pop();
180          }
181  
182          const vk::Result result = device.waitForFences(*fence.handle, true, WAIT_TIMEOUT);
183          if (result != vk::Result::eSuccess) {
184              LOG_CRITICAL(Render_Vulkan, "Fence wait failed with error {}", vk::to_string(result));
185              UNREACHABLE();
186          }
187          device.resetFences(*fence.handle);
188  
189          gpu_tick.store(fence.signal_value);
190          gpu_tick.notify_all();
191  
192          std::scoped_lock lock{free_mutex};
193          free_queue.push(std::move(fence.handle));
194      }
195  }
196  
197  vk::UniqueFence MasterSemaphoreFence::GetFreeFence() {
198      std::scoped_lock lock{free_mutex};
199      if (free_queue.empty()) {
200          return instance.GetDevice().createFenceUnique({});
201      }
202  
203      vk::UniqueFence fence{std::move(free_queue.front())};
204      free_queue.pop();
205      return fence;
206  }
207  
208  } // namespace Vulkan