1 // Copyright 2020 yuzu Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #pragma once
6 
7 #include <condition_variable>
8 #include <memory>
9 #include <shared_mutex>
10 #include <thread>
11 
12 // This header includes both Vulkan and OpenGL headers, this has to be fixed
13 // Unfortunately, including OpenGL will include Windows.h that defines macros that can cause issues.
14 // Forcefully include glad early and undefine macros
15 #include <glad/glad.h>
16 #ifdef CreateEvent
17 #undef CreateEvent
18 #endif
19 #ifdef CreateSemaphore
20 #undef CreateSemaphore
21 #endif
22 
23 #include "common/common_types.h"
24 #include "video_core/renderer_opengl/gl_device.h"
25 #include "video_core/renderer_opengl/gl_resource_manager.h"
26 #include "video_core/renderer_opengl/gl_shader_decompiler.h"
27 #include "video_core/renderer_vulkan/vk_device.h"
28 #include "video_core/renderer_vulkan/vk_pipeline_cache.h"
29 #include "video_core/renderer_vulkan/vk_scheduler.h"
30 
31 namespace Core::Frontend {
32 class EmuWindow;
33 class GraphicsContext;
34 } // namespace Core::Frontend
35 
36 namespace Tegra {
37 class GPU;
38 }
39 
40 namespace Vulkan {
41 class VKPipelineCache;
42 }
43 
44 namespace VideoCommon::Shader {
45 
46 class AsyncShaders {
47 public:
48     enum class Backend {
49         OpenGL,
50         GLASM,
51         Vulkan,
52     };
53 
54     struct ResultPrograms {
55         OpenGL::OGLProgram opengl;
56         OpenGL::OGLAssemblyProgram glasm;
57     };
58 
59     struct Result {
60         u64 uid;
61         VAddr cpu_address;
62         Backend backend;
63         ResultPrograms program;
64         std::vector<u64> code;
65         std::vector<u64> code_b;
66         Tegra::Engines::ShaderType shader_type;
67     };
68 
69     explicit AsyncShaders(Core::Frontend::EmuWindow& emu_window_);
70     ~AsyncShaders();
71 
72     /// Start up shader worker threads
73     void AllocateWorkers();
74 
75     /// Clear the shader queue and kill all worker threads
76     void FreeWorkers();
77 
78     // Force end all threads
79     void KillWorkers();
80 
81     /// Check to see if any shaders have actually been compiled
82     [[nodiscard]] bool HasCompletedWork() const;
83 
84     /// Deduce if a shader can be build on another thread of MUST be built in sync. We cannot build
85     /// every shader async as some shaders are only built and executed once. We try to "guess" which
86     /// shader would be used only once
87     [[nodiscard]] bool IsShaderAsync(const Tegra::GPU& gpu) const;
88 
89     /// Pulls completed compiled shaders
90     [[nodiscard]] std::vector<Result> GetCompletedWork();
91 
92     void QueueOpenGLShader(const OpenGL::Device& device, Tegra::Engines::ShaderType shader_type,
93                            u64 uid, std::vector<u64> code, std::vector<u64> code_b, u32 main_offset,
94                            CompilerSettings compiler_settings, const Registry& registry,
95                            VAddr cpu_addr);
96 
97     void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device,
98                            Vulkan::VKScheduler& scheduler,
99                            Vulkan::VKDescriptorPool& descriptor_pool,
100                            Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
101                            Vulkan::VKRenderPassCache& renderpass_cache,
102                            std::vector<VkDescriptorSetLayoutBinding> bindings,
103                            Vulkan::SPIRVProgram program, Vulkan::GraphicsPipelineCacheKey key);
104 
105 private:
106     void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
107 
108     /// Check our worker queue to see if we have any work queued already
109     [[nodiscard]] bool HasWorkQueued() const;
110 
111     struct WorkerParams {
112         Backend backend;
113         // For OGL
114         const OpenGL::Device* device;
115         Tegra::Engines::ShaderType shader_type;
116         u64 uid;
117         std::vector<u64> code;
118         std::vector<u64> code_b;
119         u32 main_offset;
120         CompilerSettings compiler_settings;
121         std::optional<Registry> registry;
122         VAddr cpu_address;
123 
124         // For Vulkan
125         Vulkan::VKPipelineCache* pp_cache;
126         const Vulkan::VKDevice* vk_device;
127         Vulkan::VKScheduler* scheduler;
128         Vulkan::VKDescriptorPool* descriptor_pool;
129         Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue;
130         Vulkan::VKRenderPassCache* renderpass_cache;
131         std::vector<VkDescriptorSetLayoutBinding> bindings;
132         Vulkan::SPIRVProgram program;
133         Vulkan::GraphicsPipelineCacheKey key;
134     };
135 
136     std::condition_variable cv;
137     mutable std::mutex queue_mutex;
138     mutable std::shared_mutex completed_mutex;
139     std::atomic<bool> is_thread_exiting{};
140     std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
141     std::vector<std::thread> worker_threads;
142     std::queue<WorkerParams> pending_queue;
143     std::vector<Result> finished_work;
144     Core::Frontend::EmuWindow& emu_window;
145 };
146 
147 } // namespace VideoCommon::Shader
148