async_shaders.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <condition_variable>
  5. #include <mutex>
  6. #include <thread>
  7. #include <vector>
  8. #include "video_core/engines/maxwell_3d.h"
  9. #include "video_core/renderer_base.h"
  10. #include "video_core/renderer_opengl/gl_shader_cache.h"
  11. #include "video_core/shader/async_shaders.h"
  12. namespace VideoCommon::Shader {
  13. AsyncShaders::AsyncShaders(Core::Frontend::EmuWindow& emu_window_) : emu_window(emu_window_) {}
  14. AsyncShaders::~AsyncShaders() {
  15. KillWorkers();
  16. }
  17. void AsyncShaders::AllocateWorkers() {
  18. // Use at least one thread
  19. u32 num_workers = 1;
  20. // Deduce how many more threads we can use
  21. const u32 thread_count = std::thread::hardware_concurrency();
  22. if (thread_count >= 8) {
  23. // Increase async workers by 1 for every 2 threads >= 8
  24. num_workers += 1 + (thread_count - 8) / 2;
  25. }
  26. // If we already have workers queued, ignore
  27. if (num_workers == worker_threads.size()) {
  28. return;
  29. }
  30. // If workers already exist, clear them
  31. if (!worker_threads.empty()) {
  32. FreeWorkers();
  33. }
  34. // Create workers
  35. for (std::size_t i = 0; i < num_workers; i++) {
  36. context_list.push_back(emu_window.CreateSharedContext());
  37. worker_threads.emplace_back(&AsyncShaders::ShaderCompilerThread, this,
  38. context_list[i].get());
  39. }
  40. }
  41. void AsyncShaders::FreeWorkers() {
  42. // Mark all threads to quit
  43. is_thread_exiting.store(true);
  44. cv.notify_all();
  45. for (auto& thread : worker_threads) {
  46. thread.join();
  47. }
  48. // Clear our shared contexts
  49. context_list.clear();
  50. // Clear our worker threads
  51. worker_threads.clear();
  52. }
  53. void AsyncShaders::KillWorkers() {
  54. is_thread_exiting.store(true);
  55. cv.notify_all();
  56. for (auto& thread : worker_threads) {
  57. thread.detach();
  58. }
  59. // Clear our shared contexts
  60. context_list.clear();
  61. // Clear our worker threads
  62. worker_threads.clear();
  63. }
  64. bool AsyncShaders::HasWorkQueued() const {
  65. return !pending_queue.empty();
  66. }
  67. bool AsyncShaders::HasCompletedWork() const {
  68. std::shared_lock lock{completed_mutex};
  69. return !finished_work.empty();
  70. }
  71. bool AsyncShaders::IsShaderAsync(const Tegra::GPU& gpu) const {
  72. const auto& regs = gpu.Maxwell3D().regs;
  73. // If something is using depth, we can assume that games are not rendering anything which will
  74. // be used one time.
  75. if (regs.zeta_enable) {
  76. return true;
  77. }
  78. // If games are using a small index count, we can assume these are full screen quads. Usually
  79. // these shaders are only used once for building textures so we can assume they can't be built
  80. // async
  81. if (regs.index_array.count <= 6 || regs.vertex_buffer.count <= 6) {
  82. return false;
  83. }
  84. return true;
  85. }
  86. std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
  87. std::vector<Result> results;
  88. {
  89. std::unique_lock lock{completed_mutex};
  90. results = std::move(finished_work);
  91. finished_work.clear();
  92. }
  93. return results;
  94. }
  95. void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
  96. Tegra::Engines::ShaderType shader_type, u64 uid,
  97. std::vector<u64> code, std::vector<u64> code_b,
  98. u32 main_offset, CompilerSettings compiler_settings,
  99. const Registry& registry, VAddr cpu_addr) {
  100. std::unique_lock lock(queue_mutex);
  101. pending_queue.push({
  102. .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
  103. .device = &device,
  104. .shader_type = shader_type,
  105. .uid = uid,
  106. .code = std::move(code),
  107. .code_b = std::move(code_b),
  108. .main_offset = main_offset,
  109. .compiler_settings = compiler_settings,
  110. .registry = registry,
  111. .cpu_address = cpu_addr,
  112. .pp_cache = nullptr,
  113. .vk_device = nullptr,
  114. .scheduler = nullptr,
  115. .descriptor_pool = nullptr,
  116. .update_descriptor_queue = nullptr,
  117. .bindings{},
  118. .program{},
  119. .key{},
  120. .num_color_buffers = 0,
  121. });
  122. cv.notify_one();
  123. }
  124. void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
  125. const Vulkan::Device& device, Vulkan::VKScheduler& scheduler,
  126. Vulkan::VKDescriptorPool& descriptor_pool,
  127. Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
  128. std::vector<VkDescriptorSetLayoutBinding> bindings,
  129. Vulkan::SPIRVProgram program,
  130. Vulkan::GraphicsPipelineCacheKey key, u32 num_color_buffers) {
  131. std::unique_lock lock(queue_mutex);
  132. pending_queue.push({
  133. .backend = Backend::Vulkan,
  134. .device = nullptr,
  135. .shader_type{},
  136. .uid = 0,
  137. .code{},
  138. .code_b{},
  139. .main_offset = 0,
  140. .compiler_settings{},
  141. .registry{},
  142. .cpu_address = 0,
  143. .pp_cache = pp_cache,
  144. .vk_device = &device,
  145. .scheduler = &scheduler,
  146. .descriptor_pool = &descriptor_pool,
  147. .update_descriptor_queue = &update_descriptor_queue,
  148. .bindings = std::move(bindings),
  149. .program = std::move(program),
  150. .key = key,
  151. .num_color_buffers = num_color_buffers,
  152. });
  153. cv.notify_one();
  154. }
  155. void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) {
  156. while (!is_thread_exiting.load(std::memory_order_relaxed)) {
  157. std::unique_lock lock{queue_mutex};
  158. cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; });
  159. if (is_thread_exiting) {
  160. return;
  161. }
  162. // Partial lock to allow all threads to read at the same time
  163. if (!HasWorkQueued()) {
  164. continue;
  165. }
  166. // Another thread beat us, just unlock and wait for the next load
  167. if (pending_queue.empty()) {
  168. continue;
  169. }
  170. // Pull work from queue
  171. WorkerParams work = std::move(pending_queue.front());
  172. pending_queue.pop();
  173. lock.unlock();
  174. if (work.backend == Backend::OpenGL || work.backend == Backend::GLASM) {
  175. const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, *work.registry);
  176. const auto scope = context->Acquire();
  177. auto program =
  178. OpenGL::BuildShader(*work.device, work.shader_type, work.uid, ir, *work.registry);
  179. Result result{};
  180. result.backend = work.backend;
  181. result.cpu_address = work.cpu_address;
  182. result.uid = work.uid;
  183. result.code = std::move(work.code);
  184. result.code_b = std::move(work.code_b);
  185. result.shader_type = work.shader_type;
  186. if (work.backend == Backend::OpenGL) {
  187. result.program.opengl = std::move(program->source_program);
  188. } else if (work.backend == Backend::GLASM) {
  189. result.program.glasm = std::move(program->assembly_program);
  190. }
  191. {
  192. std::unique_lock complete_lock(completed_mutex);
  193. finished_work.push_back(std::move(result));
  194. }
  195. } else if (work.backend == Backend::Vulkan) {
  196. auto pipeline = std::make_unique<Vulkan::VKGraphicsPipeline>(
  197. *work.vk_device, *work.scheduler, *work.descriptor_pool,
  198. *work.update_descriptor_queue, work.key, work.bindings, work.program,
  199. work.num_color_buffers);
  200. work.pp_cache->EmplacePipeline(std::move(pipeline));
  201. }
  202. }
  203. }
  204. } // namespace VideoCommon::Shader