async_shaders.cpp 7.6 KB

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