summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2020-07-18 00:48:27 -0400
committerGravatar GitHub2020-07-18 00:48:27 -0400
commit90cbcaa44a3901a832556258b5b97d8d7de34ca9 (patch)
tree570ff95dae035757fb2831804aae4f4ca681d354
parentMerge pull request #4364 from lioncash/desig5 (diff)
parentFix style issues (diff)
downloadyuzu-90cbcaa44a3901a832556258b5b97d8d7de34ca9.tar.gz
yuzu-90cbcaa44a3901a832556258b5b97d8d7de34ca9.tar.xz
yuzu-90cbcaa44a3901a832556258b5b97d8d7de34ca9.zip
Merge pull request #4273 from ogniK5377/async-shaders-prod
video_core: Add asynchronous shader decompilation and compilation
Diffstat (limited to '')
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp2
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/gpu.h11
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_device.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp24
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h10
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp181
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h36
-rw-r--r--src/video_core/shader/async_shaders.cpp181
-rw-r--r--src/video_core/shader/async_shaders.h109
-rw-r--r--src/video_core/shader_notify.cpp42
-rw-r--r--src/video_core/shader_notify.h29
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp22
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui10
-rw-r--r--src/yuzu/main.cpp32
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h4
24 files changed, 660 insertions, 64 deletions
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index e8a6f2a6e..44252dd81 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -115,6 +115,7 @@ void LogSettings() {
115 values.use_asynchronous_gpu_emulation.GetValue()); 115 values.use_asynchronous_gpu_emulation.GetValue());
116 log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); 116 log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
117 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); 117 log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
118 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
118 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 119 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
119 log_setting("Audio_OutputEngine", values.sink_id); 120 log_setting("Audio_OutputEngine", values.sink_id);
120 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); 121 log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
@@ -170,6 +171,7 @@ void RestoreGlobalState() {
170 values.use_asynchronous_gpu_emulation.SetGlobal(true); 171 values.use_asynchronous_gpu_emulation.SetGlobal(true);
171 values.use_vsync.SetGlobal(true); 172 values.use_vsync.SetGlobal(true);
172 values.use_assembly_shaders.SetGlobal(true); 173 values.use_assembly_shaders.SetGlobal(true);
174 values.use_asynchronous_shaders.SetGlobal(true);
173 values.use_fast_gpu_time.SetGlobal(true); 175 values.use_fast_gpu_time.SetGlobal(true);
174 values.force_30fps_mode.SetGlobal(true); 176 values.force_30fps_mode.SetGlobal(true);
175 values.bg_red.SetGlobal(true); 177 values.bg_red.SetGlobal(true);
diff --git a/src/core/settings.h b/src/core/settings.h
index a64debd25..386233fdf 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -434,6 +434,7 @@ struct Values {
434 Setting<bool> use_asynchronous_gpu_emulation; 434 Setting<bool> use_asynchronous_gpu_emulation;
435 Setting<bool> use_vsync; 435 Setting<bool> use_vsync;
436 Setting<bool> use_assembly_shaders; 436 Setting<bool> use_assembly_shaders;
437 Setting<bool> use_asynchronous_shaders;
437 Setting<bool> force_30fps_mode; 438 Setting<bool> force_30fps_mode;
438 Setting<bool> use_fast_gpu_time; 439 Setting<bool> use_fast_gpu_time;
439 440
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 78915e6db..5a30c75da 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -207,6 +207,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
207 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); 207 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
208 AddField(field_type, "Renderer_UseAssemblyShaders", 208 AddField(field_type, "Renderer_UseAssemblyShaders",
209 Settings::values.use_assembly_shaders.GetValue()); 209 Settings::values.use_assembly_shaders.GetValue());
210 AddField(field_type, "Renderer_UseAsynchronousShaders",
211 Settings::values.use_asynchronous_shaders.GetValue());
210 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); 212 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
211} 213}
212 214
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 21c46a567..3cd896a0f 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -98,6 +98,8 @@ add_library(video_core STATIC
98 sampler_cache.cpp 98 sampler_cache.cpp
99 sampler_cache.h 99 sampler_cache.h
100 shader_cache.h 100 shader_cache.h
101 shader_notify.cpp
102 shader_notify.h
101 shader/decode/arithmetic.cpp 103 shader/decode/arithmetic.cpp
102 shader/decode/arithmetic_immediate.cpp 104 shader/decode/arithmetic_immediate.cpp
103 shader/decode/bfe.cpp 105 shader/decode/bfe.cpp
@@ -128,6 +130,8 @@ add_library(video_core STATIC
128 shader/decode/other.cpp 130 shader/decode/other.cpp
129 shader/ast.cpp 131 shader/ast.cpp
130 shader/ast.h 132 shader/ast.h
133 shader/async_shaders.cpp
134 shader/async_shaders.h
131 shader/compiler_settings.cpp 135 shader/compiler_settings.cpp
132 shader/compiler_settings.h 136 shader/compiler_settings.h
133 shader/control_flow.cpp 137 shader/control_flow.cpp
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 758bfe148..8e19c3373 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -20,6 +20,7 @@
20#include "video_core/gpu.h" 20#include "video_core/gpu.h"
21#include "video_core/memory_manager.h" 21#include "video_core/memory_manager.h"
22#include "video_core/renderer_base.h" 22#include "video_core/renderer_base.h"
23#include "video_core/shader_notify.h"
23#include "video_core/video_core.h" 24#include "video_core/video_core.h"
24 25
25namespace Tegra { 26namespace Tegra {
@@ -36,6 +37,7 @@ GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& render
36 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager); 37 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager);
37 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager); 38 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
38 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager); 39 kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
40 shader_notify = std::make_unique<VideoCore::ShaderNotify>();
39} 41}
40 42
41GPU::~GPU() = default; 43GPU::~GPU() = default;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 2c42483bd..8d04d9fd9 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -33,6 +33,7 @@ class System;
33 33
34namespace VideoCore { 34namespace VideoCore {
35class RendererBase; 35class RendererBase;
36class ShaderNotify;
36} // namespace VideoCore 37} // namespace VideoCore
37 38
38namespace Tegra { 39namespace Tegra {
@@ -207,6 +208,14 @@ public:
207 return *renderer; 208 return *renderer;
208 } 209 }
209 210
211 VideoCore::ShaderNotify& ShaderNotify() {
212 return *shader_notify;
213 }
214
215 const VideoCore::ShaderNotify& ShaderNotify() const {
216 return *shader_notify;
217 }
218
210 // Waits for the GPU to finish working 219 // Waits for the GPU to finish working
211 virtual void WaitIdle() const = 0; 220 virtual void WaitIdle() const = 0;
212 221
@@ -347,6 +356,8 @@ private:
347 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; 356 std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
348 /// Inline memory engine 357 /// Inline memory engine
349 std::unique_ptr<Engines::KeplerMemory> kepler_memory; 358 std::unique_ptr<Engines::KeplerMemory> kepler_memory;
359 /// Shader build notifier
360 std::unique_ptr<VideoCore::ShaderNotify> shader_notify;
350 361
351 std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{}; 362 std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};
352 363
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index c1f20f0ab..630acb73b 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -233,6 +233,8 @@ Device::Device()
233 GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 && 233 GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
234 GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2; 234 GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
235 235
236 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
237
236 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); 238 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
237 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); 239 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
238 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); 240 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index e1d811966..94d38d7d1 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -104,6 +104,10 @@ public:
104 return use_assembly_shaders; 104 return use_assembly_shaders;
105 } 105 }
106 106
107 bool UseAsynchronousShaders() const {
108 return use_asynchronous_shaders;
109 }
110
107private: 111private:
108 static bool TestVariableAoffi(); 112 static bool TestVariableAoffi();
109 static bool TestPreciseBug(); 113 static bool TestPreciseBug();
@@ -127,6 +131,7 @@ private:
127 bool has_fast_buffer_sub_data{}; 131 bool has_fast_buffer_sub_data{};
128 bool has_nv_viewport_array2{}; 132 bool has_nv_viewport_array2{};
129 bool use_assembly_shaders{}; 133 bool use_assembly_shaders{};
134 bool use_asynchronous_shaders{};
130}; 135};
131 136
132} // namespace OpenGL 137} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e960a0ef1..c3fad563c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -149,7 +149,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
149 shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, 149 shader_cache{*this, system, emu_window, device}, query_cache{system, *this},
150 buffer_cache{*this, system, device, STREAM_BUFFER_SIZE}, 150 buffer_cache{*this, system, device, STREAM_BUFFER_SIZE},
151 fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system}, 151 fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system},
152 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} { 152 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
153 async_shaders{emu_window} {
153 CheckExtensions(); 154 CheckExtensions();
154 155
155 unified_uniform_buffer.Create(); 156 unified_uniform_buffer.Create();
@@ -162,6 +163,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
162 nullptr, 0); 163 nullptr, 0);
163 } 164 }
164 } 165 }
166
167 if (device.UseAsynchronousShaders()) {
168 // Max worker threads we should allow
169 constexpr auto MAX_THREADS = 2u;
170 // Amount of threads we should reserve for other parts of yuzu
171 constexpr auto RESERVED_THREADS = 6u;
172 // Get the amount of threads we can use(this can return zero)
173 const auto cpu_thread_count =
174 std::max(RESERVED_THREADS, std::thread::hardware_concurrency());
175 // Deduce how many "extra" threads we have to use.
176 const auto max_threads_unused = cpu_thread_count - RESERVED_THREADS;
177 // Always allow at least 1 thread regardless of our settings
178 const auto max_worker_count = std::max(1u, max_threads_unused);
179 // Don't use more than MAX_THREADS
180 const auto worker_count = std::min(max_worker_count, MAX_THREADS);
181 async_shaders.AllocateWorkers(worker_count);
182 }
165} 183}
166 184
167RasterizerOpenGL::~RasterizerOpenGL() { 185RasterizerOpenGL::~RasterizerOpenGL() {
@@ -336,7 +354,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
336 continue; 354 continue;
337 } 355 }
338 356
339 Shader* const shader = shader_cache.GetStageProgram(program); 357 Shader* shader = shader_cache.GetStageProgram(program, async_shaders);
340 358
341 if (device.UseAssemblyShaders()) { 359 if (device.UseAssemblyShaders()) {
342 // Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this 360 // Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this
@@ -353,7 +371,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
353 SetupDrawTextures(stage, shader); 371 SetupDrawTextures(stage, shader);
354 SetupDrawImages(stage, shader); 372 SetupDrawImages(stage, shader);
355 373
356 const GLuint program_handle = shader->GetHandle(); 374 const GLuint program_handle = shader->IsBuilt() ? shader->GetHandle() : 0;
357 switch (program) { 375 switch (program) {
358 case Maxwell::ShaderProgram::VertexA: 376 case Maxwell::ShaderProgram::VertexA:
359 case Maxwell::ShaderProgram::VertexB: 377 case Maxwell::ShaderProgram::VertexB:
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 4f082592f..a95646936 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -33,6 +33,7 @@
33#include "video_core/renderer_opengl/gl_state_tracker.h" 33#include "video_core/renderer_opengl/gl_state_tracker.h"
34#include "video_core/renderer_opengl/gl_texture_cache.h" 34#include "video_core/renderer_opengl/gl_texture_cache.h"
35#include "video_core/renderer_opengl/utils.h" 35#include "video_core/renderer_opengl/utils.h"
36#include "video_core/shader/async_shaders.h"
36#include "video_core/textures/texture.h" 37#include "video_core/textures/texture.h"
37 38
38namespace Core { 39namespace Core {
@@ -91,6 +92,14 @@ public:
91 return num_queued_commands > 0; 92 return num_queued_commands > 0;
92 } 93 }
93 94
95 VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
96 return async_shaders;
97 }
98
99 const VideoCommon::Shader::AsyncShaders& GetAsyncShaders() const {
100 return async_shaders;
101 }
102
94private: 103private:
95 /// Configures the color and depth framebuffer states. 104 /// Configures the color and depth framebuffer states.
96 void ConfigureFramebuffers(); 105 void ConfigureFramebuffers();
@@ -242,6 +251,7 @@ private:
242 ScreenInfo& screen_info; 251 ScreenInfo& screen_info;
243 ProgramManager& program_manager; 252 ProgramManager& program_manager;
244 StateTracker& state_tracker; 253 StateTracker& state_tracker;
254 VideoCommon::Shader::AsyncShaders async_shaders;
245 255
246 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 256 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
247 257
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index f8b322227..b05cb641c 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -177,6 +177,12 @@ public:
177 Release(); 177 Release();
178 } 178 }
179 179
180 OGLAssemblyProgram& operator=(OGLAssemblyProgram&& o) noexcept {
181 Release();
182 handle = std::exchange(o.handle, 0);
183 return *this;
184 }
185
180 /// Deletes the internal OpenGL resource 186 /// Deletes the internal OpenGL resource
181 void Release(); 187 void Release();
182 188
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index c6a3bf3a1..f469ed656 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -31,6 +31,7 @@
31#include "video_core/shader/registry.h" 31#include "video_core/shader/registry.h"
32#include "video_core/shader/shader_ir.h" 32#include "video_core/shader/shader_ir.h"
33#include "video_core/shader_cache.h" 33#include "video_core/shader_cache.h"
34#include "video_core/shader_notify.h"
34 35
35namespace OpenGL { 36namespace OpenGL {
36 37
@@ -140,9 +141,24 @@ std::shared_ptr<Registry> MakeRegistry(const ShaderDiskCacheEntry& entry) {
140 return registry; 141 return registry;
141} 142}
142 143
144std::unordered_set<GLenum> GetSupportedFormats() {
145 GLint num_formats;
146 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
147
148 std::vector<GLint> formats(num_formats);
149 glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
150
151 std::unordered_set<GLenum> supported_formats;
152 for (const GLint format : formats) {
153 supported_formats.insert(static_cast<GLenum>(format));
154 }
155 return supported_formats;
156}
157
158} // Anonymous namespace
159
143ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 unique_identifier, 160ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 unique_identifier,
144 const ShaderIR& ir, const Registry& registry, 161 const ShaderIR& ir, const Registry& registry, bool hint_retrievable) {
145 bool hint_retrievable = false) {
146 const std::string shader_id = MakeShaderID(unique_identifier, shader_type); 162 const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
147 LOG_INFO(Render_OpenGL, "{}", shader_id); 163 LOG_INFO(Render_OpenGL, "{}", shader_id);
148 164
@@ -181,30 +197,17 @@ ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 u
181 return program; 197 return program;
182} 198}
183 199
184std::unordered_set<GLenum> GetSupportedFormats() {
185 GLint num_formats;
186 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
187
188 std::vector<GLint> formats(num_formats);
189 glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
190
191 std::unordered_set<GLenum> supported_formats;
192 for (const GLint format : formats) {
193 supported_formats.insert(static_cast<GLenum>(format));
194 }
195 return supported_formats;
196}
197
198} // Anonymous namespace
199
200Shader::Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry_, ShaderEntries entries_, 200Shader::Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry_, ShaderEntries entries_,
201 ProgramSharedPtr program_) 201 ProgramSharedPtr program_, bool is_built)
202 : registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)} { 202 : registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)},
203 is_built(is_built) {
203 handle = program->assembly_program.handle; 204 handle = program->assembly_program.handle;
204 if (handle == 0) { 205 if (handle == 0) {
205 handle = program->source_program.handle; 206 handle = program->source_program.handle;
206 } 207 }
207 ASSERT(handle != 0); 208 if (is_built) {
209 ASSERT(handle != 0);
210 }
208} 211}
209 212
210Shader::~Shader() = default; 213Shader::~Shader() = default;
@@ -214,42 +217,82 @@ GLuint Shader::GetHandle() const {
214 return handle; 217 return handle;
215} 218}
216 219
217std::unique_ptr<Shader> Shader::CreateStageFromMemory(const ShaderParameters& params, 220bool Shader::IsBuilt() const {
218 Maxwell::ShaderProgram program_type, 221 return is_built;
219 ProgramCode code, ProgramCode code_b) { 222}
223
224void Shader::AsyncOpenGLBuilt(OGLProgram new_program) {
225 program->source_program = std::move(new_program);
226 handle = program->source_program.handle;
227 is_built = true;
228}
229
230void Shader::AsyncGLASMBuilt(OGLAssemblyProgram new_program) {
231 program->assembly_program = std::move(new_program);
232 handle = program->assembly_program.handle;
233 is_built = true;
234}
235
236std::unique_ptr<Shader> Shader::CreateStageFromMemory(
237 const ShaderParameters& params, Maxwell::ShaderProgram program_type, ProgramCode code,
238 ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) {
220 const auto shader_type = GetShaderType(program_type); 239 const auto shader_type = GetShaderType(program_type);
221 const std::size_t size_in_bytes = code.size() * sizeof(u64); 240 const std::size_t size_in_bytes = code.size() * sizeof(u64);
222 241
223 auto registry = std::make_shared<Registry>(shader_type, params.system.GPU().Maxwell3D()); 242 auto& gpu = params.system.GPU();
224 const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry); 243 gpu.ShaderNotify().MarkSharderBuilding();
225 // TODO(Rodrigo): Handle VertexA shaders 244
226 // std::optional<ShaderIR> ir_b; 245 auto registry = std::make_shared<Registry>(shader_type, gpu.Maxwell3D());
227 // if (!code_b.empty()) { 246 if (!async_shaders.IsShaderAsync(params.system.GPU()) ||
228 // ir_b.emplace(code_b, STAGE_MAIN_OFFSET); 247 !params.device.UseAsynchronousShaders()) {
229 // } 248 const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
230 auto program = BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry); 249 // TODO(Rodrigo): Handle VertexA shaders
250 // std::optional<ShaderIR> ir_b;
251 // if (!code_b.empty()) {
252 // ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
253 // }
254 auto program =
255 BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
256 ShaderDiskCacheEntry entry;
257 entry.type = shader_type;
258 entry.code = std::move(code);
259 entry.code_b = std::move(code_b);
260 entry.unique_identifier = params.unique_identifier;
261 entry.bound_buffer = registry->GetBoundBuffer();
262 entry.graphics_info = registry->GetGraphicsInfo();
263 entry.keys = registry->GetKeys();
264 entry.bound_samplers = registry->GetBoundSamplers();
265 entry.bindless_samplers = registry->GetBindlessSamplers();
266 params.disk_cache.SaveEntry(std::move(entry));
267
268 gpu.ShaderNotify().MarkShaderComplete();
269
270 return std::unique_ptr<Shader>(new Shader(std::move(registry),
271 MakeEntries(params.device, ir, shader_type),
272 std::move(program), true));
273 } else {
274 // Required for entries
275 const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
276 auto entries = MakeEntries(params.device, ir, shader_type);
231 277
232 ShaderDiskCacheEntry entry; 278 async_shaders.QueueOpenGLShader(params.device, shader_type, params.unique_identifier,
233 entry.type = shader_type; 279 std::move(code), std::move(code_b), STAGE_MAIN_OFFSET,
234 entry.code = std::move(code); 280 COMPILER_SETTINGS, *registry, cpu_addr);
235 entry.code_b = std::move(code_b);
236 entry.unique_identifier = params.unique_identifier;
237 entry.bound_buffer = registry->GetBoundBuffer();
238 entry.graphics_info = registry->GetGraphicsInfo();
239 entry.keys = registry->GetKeys();
240 entry.bound_samplers = registry->GetBoundSamplers();
241 entry.bindless_samplers = registry->GetBindlessSamplers();
242 params.disk_cache.SaveEntry(std::move(entry));
243 281
244 return std::unique_ptr<Shader>(new Shader( 282 auto program = std::make_shared<ProgramHandle>();
245 std::move(registry), MakeEntries(params.device, ir, shader_type), std::move(program))); 283 return std::unique_ptr<Shader>(
284 new Shader(std::move(registry), std::move(entries), std::move(program), false));
285 }
246} 286}
247 287
248std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params, 288std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params,
249 ProgramCode code) { 289 ProgramCode code) {
250 const std::size_t size_in_bytes = code.size() * sizeof(u64); 290 const std::size_t size_in_bytes = code.size() * sizeof(u64);
251 291
252 auto& engine = params.system.GPU().KeplerCompute(); 292 auto& gpu = params.system.GPU();
293 gpu.ShaderNotify().MarkSharderBuilding();
294
295 auto& engine = gpu.KeplerCompute();
253 auto registry = std::make_shared<Registry>(ShaderType::Compute, engine); 296 auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
254 const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry); 297 const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
255 const u64 uid = params.unique_identifier; 298 const u64 uid = params.unique_identifier;
@@ -266,6 +309,8 @@ std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& p
266 entry.bindless_samplers = registry->GetBindlessSamplers(); 309 entry.bindless_samplers = registry->GetBindlessSamplers();
267 params.disk_cache.SaveEntry(std::move(entry)); 310 params.disk_cache.SaveEntry(std::move(entry));
268 311
312 gpu.ShaderNotify().MarkShaderComplete();
313
269 return std::unique_ptr<Shader>(new Shader(std::move(registry), 314 return std::unique_ptr<Shader>(new Shader(std::move(registry),
270 MakeEntries(params.device, ir, ShaderType::Compute), 315 MakeEntries(params.device, ir, ShaderType::Compute),
271 std::move(program))); 316 std::move(program)));
@@ -436,14 +481,51 @@ ProgramSharedPtr ShaderCacheOpenGL::GeneratePrecompiledProgram(
436 return program; 481 return program;
437} 482}
438 483
439Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 484Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
485 VideoCommon::Shader::AsyncShaders& async_shaders) {
440 if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) { 486 if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
441 return last_shaders[static_cast<std::size_t>(program)]; 487 auto* last_shader = last_shaders[static_cast<std::size_t>(program)];
488 if (last_shader->IsBuilt()) {
489 return last_shader;
490 }
442 } 491 }
443 492
444 auto& memory_manager{system.GPU().MemoryManager()}; 493 auto& memory_manager{system.GPU().MemoryManager()};
445 const GPUVAddr address{GetShaderAddress(system, program)}; 494 const GPUVAddr address{GetShaderAddress(system, program)};
446 495
496 if (device.UseAsynchronousShaders() && async_shaders.HasCompletedWork()) {
497 auto completed_work = async_shaders.GetCompletedWork();
498 for (auto& work : completed_work) {
499 Shader* shader = TryGet(work.cpu_address);
500 auto& gpu = system.GPU();
501 gpu.ShaderNotify().MarkShaderComplete();
502 if (shader == nullptr) {
503 continue;
504 }
505 using namespace VideoCommon::Shader;
506 if (work.backend == AsyncShaders::Backend::OpenGL) {
507 shader->AsyncOpenGLBuilt(std::move(work.program.opengl));
508 } else if (work.backend == AsyncShaders::Backend::GLASM) {
509 shader->AsyncGLASMBuilt(std::move(work.program.glasm));
510 }
511
512 ShaderDiskCacheEntry entry;
513 entry.type = work.shader_type;
514 entry.code = std::move(work.code);
515 entry.code_b = std::move(work.code_b);
516 entry.unique_identifier = work.uid;
517
518 auto& registry = shader->GetRegistry();
519
520 entry.bound_buffer = registry.GetBoundBuffer();
521 entry.graphics_info = registry.GetGraphicsInfo();
522 entry.keys = registry.GetKeys();
523 entry.bound_samplers = registry.GetBoundSamplers();
524 entry.bindless_samplers = registry.GetBindlessSamplers();
525 disk_cache.SaveEntry(std::move(entry));
526 }
527 }
528
447 // Look up shader in the cache based on address 529 // Look up shader in the cache based on address
448 const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; 530 const auto cpu_addr{memory_manager.GpuToCpuAddress(address)};
449 if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) { 531 if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) {
@@ -471,7 +553,8 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
471 std::unique_ptr<Shader> shader; 553 std::unique_ptr<Shader> shader;
472 const auto found = runtime_cache.find(unique_identifier); 554 const auto found = runtime_cache.find(unique_identifier);
473 if (found == runtime_cache.end()) { 555 if (found == runtime_cache.end()) {
474 shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b)); 556 shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b),
557 async_shaders, cpu_addr.value_or(0));
475 } else { 558 } else {
476 shader = Shader::CreateFromCache(params, found->second); 559 shader = Shader::CreateFromCache(params, found->second);
477 } 560 }
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 994aaeaf2..7528ac686 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -33,6 +33,10 @@ namespace Core::Frontend {
33class EmuWindow; 33class EmuWindow;
34} 34}
35 35
36namespace VideoCommon::Shader {
37class AsyncShaders;
38}
39
36namespace OpenGL { 40namespace OpenGL {
37 41
38class Device; 42class Device;
@@ -61,6 +65,11 @@ struct ShaderParameters {
61 u64 unique_identifier; 65 u64 unique_identifier;
62}; 66};
63 67
68ProgramSharedPtr BuildShader(const Device& device, Tegra::Engines::ShaderType shader_type,
69 u64 unique_identifier, const VideoCommon::Shader::ShaderIR& ir,
70 const VideoCommon::Shader::Registry& registry,
71 bool hint_retrievable = false);
72
64class Shader final { 73class Shader final {
65public: 74public:
66 ~Shader(); 75 ~Shader();
@@ -68,15 +77,28 @@ public:
68 /// Gets the GL program handle for the shader 77 /// Gets the GL program handle for the shader
69 GLuint GetHandle() const; 78 GLuint GetHandle() const;
70 79
80 bool IsBuilt() const;
81
71 /// Gets the shader entries for the shader 82 /// Gets the shader entries for the shader
72 const ShaderEntries& GetEntries() const { 83 const ShaderEntries& GetEntries() const {
73 return entries; 84 return entries;
74 } 85 }
75 86
76 static std::unique_ptr<Shader> CreateStageFromMemory(const ShaderParameters& params, 87 const VideoCommon::Shader::Registry& GetRegistry() const {
77 Maxwell::ShaderProgram program_type, 88 return *registry;
78 ProgramCode program_code, 89 }
79 ProgramCode program_code_b); 90
91 /// Mark a OpenGL shader as built
92 void AsyncOpenGLBuilt(OGLProgram new_program);
93
94 /// Mark a GLASM shader as built
95 void AsyncGLASMBuilt(OGLAssemblyProgram new_program);
96
97 static std::unique_ptr<Shader> CreateStageFromMemory(
98 const ShaderParameters& params, Maxwell::ShaderProgram program_type,
99 ProgramCode program_code, ProgramCode program_code_b,
100 VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr);
101
80 static std::unique_ptr<Shader> CreateKernelFromMemory(const ShaderParameters& params, 102 static std::unique_ptr<Shader> CreateKernelFromMemory(const ShaderParameters& params,
81 ProgramCode code); 103 ProgramCode code);
82 104
@@ -85,12 +107,13 @@ public:
85 107
86private: 108private:
87 explicit Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry, ShaderEntries entries, 109 explicit Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry, ShaderEntries entries,
88 ProgramSharedPtr program); 110 ProgramSharedPtr program, bool is_built = true);
89 111
90 std::shared_ptr<VideoCommon::Shader::Registry> registry; 112 std::shared_ptr<VideoCommon::Shader::Registry> registry;
91 ShaderEntries entries; 113 ShaderEntries entries;
92 ProgramSharedPtr program; 114 ProgramSharedPtr program;
93 GLuint handle = 0; 115 GLuint handle = 0;
116 bool is_built{};
94}; 117};
95 118
96class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> { 119class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> {
@@ -104,7 +127,8 @@ public:
104 const VideoCore::DiskResourceLoadCallback& callback); 127 const VideoCore::DiskResourceLoadCallback& callback);
105 128
106 /// Gets the current specified shader stage program 129 /// Gets the current specified shader stage program
107 Shader* GetStageProgram(Maxwell::ShaderProgram program); 130 Shader* GetStageProgram(Maxwell::ShaderProgram program,
131 VideoCommon::Shader::AsyncShaders& async_shaders);
108 132
109 /// Gets a compute kernel in the passed address 133 /// Gets a compute kernel in the passed address
110 Shader* GetComputeKernel(GPUVAddr code_addr); 134 Shader* GetComputeKernel(GPUVAddr code_addr);
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
new file mode 100644
index 000000000..b7f66d7ee
--- /dev/null
+++ b/src/video_core/shader/async_shaders.cpp
@@ -0,0 +1,181 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <condition_variable>
7#include <mutex>
8#include <thread>
9#include <vector>
10#include "video_core/engines/maxwell_3d.h"
11#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_shader_cache.h"
13#include "video_core/shader/async_shaders.h"
14
15namespace VideoCommon::Shader {
16
17AsyncShaders::AsyncShaders(Core::Frontend::EmuWindow& emu_window) : emu_window(emu_window) {}
18
19AsyncShaders::~AsyncShaders() {
20 KillWorkers();
21}
22
23void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
24 // If we're already have workers queued or don't want to queue workers, ignore
25 if (num_workers == worker_threads.size() || num_workers == 0) {
26 return;
27 }
28
29 // If workers already exist, clear them
30 if (!worker_threads.empty()) {
31 FreeWorkers();
32 }
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.push_back(std::move(
38 std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get())));
39 }
40}
41
42void AsyncShaders::FreeWorkers() {
43 // Mark all threads to quit
44 is_thread_exiting.store(true);
45 cv.notify_all();
46 for (auto& thread : worker_threads) {
47 thread.join();
48 }
49 // Clear our shared contexts
50 context_list.clear();
51
52 // Clear our worker threads
53 worker_threads.clear();
54}
55
56void AsyncShaders::KillWorkers() {
57 is_thread_exiting.store(true);
58 for (auto& thread : worker_threads) {
59 thread.detach();
60 }
61 // Clear our shared contexts
62 context_list.clear();
63
64 // Clear our worker threads
65 worker_threads.clear();
66}
67
68bool AsyncShaders::HasWorkQueued() {
69 return !pending_queue.empty();
70}
71
72bool AsyncShaders::HasCompletedWork() {
73 std::shared_lock lock{completed_mutex};
74 return !finished_work.empty();
75}
76
77bool AsyncShaders::IsShaderAsync(const Tegra::GPU& gpu) const {
78 const auto& regs = gpu.Maxwell3D().regs;
79
80 // If something is using depth, we can assume that games are not rendering anything which will
81 // be used one time.
82 if (regs.zeta_enable) {
83 return true;
84 }
85
86 // If games are using a small index count, we can assume these are full screen quads. Usually
87 // these shaders are only used once for building textures so we can assume they can't be built
88 // async
89 if (regs.index_array.count <= 6 || regs.vertex_buffer.count <= 6) {
90 return false;
91 }
92
93 return true;
94}
95
96std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
97 std::vector<AsyncShaders::Result> results;
98 {
99 std::unique_lock lock{completed_mutex};
100 results.assign(std::make_move_iterator(finished_work.begin()),
101 std::make_move_iterator(finished_work.end()));
102 finished_work.clear();
103 }
104 return results;
105}
106
107void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
108 Tegra::Engines::ShaderType shader_type, u64 uid,
109 std::vector<u64> code, std::vector<u64> code_b,
110 u32 main_offset,
111 VideoCommon::Shader::CompilerSettings compiler_settings,
112 const VideoCommon::Shader::Registry& registry,
113 VAddr cpu_addr) {
114 WorkerParams params{device.UseAssemblyShaders() ? AsyncShaders::Backend::GLASM
115 : AsyncShaders::Backend::OpenGL,
116 device,
117 shader_type,
118 uid,
119 std::move(code),
120 std::move(code_b),
121 main_offset,
122 compiler_settings,
123 registry,
124 cpu_addr};
125 std::unique_lock lock(queue_mutex);
126 pending_queue.push_back(std::move(params));
127 cv.notify_one();
128}
129
130void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) {
131 using namespace std::chrono_literals;
132 while (!is_thread_exiting.load(std::memory_order_relaxed)) {
133 std::unique_lock lock{queue_mutex};
134 cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; });
135 if (is_thread_exiting) {
136 return;
137 }
138
139 // Partial lock to allow all threads to read at the same time
140 if (!HasWorkQueued()) {
141 continue;
142 }
143 // Another thread beat us, just unlock and wait for the next load
144 if (pending_queue.empty()) {
145 continue;
146 }
147 // Pull work from queue
148 WorkerParams work = std::move(pending_queue.front());
149 pending_queue.pop_front();
150
151 lock.unlock();
152
153 if (work.backend == AsyncShaders::Backend::OpenGL ||
154 work.backend == AsyncShaders::Backend::GLASM) {
155 const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, work.registry);
156 const auto scope = context->Acquire();
157 auto program =
158 OpenGL::BuildShader(work.device, work.shader_type, work.uid, ir, work.registry);
159 Result result{};
160 result.backend = work.backend;
161 result.cpu_address = work.cpu_address;
162 result.uid = work.uid;
163 result.code = std::move(work.code);
164 result.code_b = std::move(work.code_b);
165 result.shader_type = work.shader_type;
166
167 if (work.backend == AsyncShaders::Backend::OpenGL) {
168 result.program.opengl = std::move(program->source_program);
169 } else if (work.backend == AsyncShaders::Backend::GLASM) {
170 result.program.glasm = std::move(program->assembly_program);
171 }
172
173 {
174 std::unique_lock complete_lock(completed_mutex);
175 finished_work.push_back(std::move(result));
176 }
177 }
178 }
179}
180
181} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/async_shaders.h b/src/video_core/shader/async_shaders.h
new file mode 100644
index 000000000..2f5ee94ad
--- /dev/null
+++ b/src/video_core/shader/async_shaders.h
@@ -0,0 +1,109 @@
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 <deque>
9#include <memory>
10#include <shared_mutex>
11#include <thread>
12#include "common/bit_field.h"
13#include "common/common_types.h"
14#include "video_core/renderer_opengl/gl_device.h"
15#include "video_core/renderer_opengl/gl_resource_manager.h"
16#include "video_core/renderer_opengl/gl_shader_decompiler.h"
17
18namespace Core::Frontend {
19class EmuWindow;
20class GraphicsContext;
21} // namespace Core::Frontend
22
23namespace Tegra {
24class GPU;
25}
26
27namespace VideoCommon::Shader {
28
29class AsyncShaders {
30public:
31 enum class Backend {
32 OpenGL,
33 GLASM,
34 };
35
36 struct ResultPrograms {
37 OpenGL::OGLProgram opengl;
38 OpenGL::OGLAssemblyProgram glasm;
39 };
40
41 struct Result {
42 u64 uid;
43 VAddr cpu_address;
44 Backend backend;
45 ResultPrograms program;
46 std::vector<u64> code;
47 std::vector<u64> code_b;
48 Tegra::Engines::ShaderType shader_type;
49 };
50
51 explicit AsyncShaders(Core::Frontend::EmuWindow& emu_window);
52 ~AsyncShaders();
53
54 /// Start up shader worker threads
55 void AllocateWorkers(std::size_t num_workers);
56
57 /// Clear the shader queue and kill all worker threads
58 void FreeWorkers();
59
60 // Force end all threads
61 void KillWorkers();
62
63 /// Check to see if any shaders have actually been compiled
64 bool HasCompletedWork();
65
66 /// Deduce if a shader can be build on another thread of MUST be built in sync. We cannot build
67 /// every shader async as some shaders are only built and executed once. We try to "guess" which
68 /// shader would be used only once
69 bool IsShaderAsync(const Tegra::GPU& gpu) const;
70
71 /// Pulls completed compiled shaders
72 std::vector<Result> GetCompletedWork();
73
74 void QueueOpenGLShader(const OpenGL::Device& device, Tegra::Engines::ShaderType shader_type,
75 u64 uid, std::vector<u64> code, std::vector<u64> code_b, u32 main_offset,
76 VideoCommon::Shader::CompilerSettings compiler_settings,
77 const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
78
79private:
80 void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
81
82 /// Check our worker queue to see if we have any work queued already
83 bool HasWorkQueued();
84
85 struct WorkerParams {
86 AsyncShaders::Backend backend;
87 OpenGL::Device device;
88 Tegra::Engines::ShaderType shader_type;
89 u64 uid;
90 std::vector<u64> code;
91 std::vector<u64> code_b;
92 u32 main_offset;
93 VideoCommon::Shader::CompilerSettings compiler_settings;
94 VideoCommon::Shader::Registry registry;
95 VAddr cpu_address;
96 };
97
98 std::condition_variable cv;
99 std::mutex queue_mutex;
100 std::shared_mutex completed_mutex;
101 std::atomic<bool> is_thread_exiting{};
102 std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
103 std::vector<std::thread> worker_threads;
104 std::deque<WorkerParams> pending_queue;
105 std::vector<AsyncShaders::Result> finished_work;
106 Core::Frontend::EmuWindow& emu_window;
107};
108
109} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader_notify.cpp b/src/video_core/shader_notify.cpp
new file mode 100644
index 000000000..c3c71657d
--- /dev/null
+++ b/src/video_core/shader_notify.cpp
@@ -0,0 +1,42 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/shader_notify.h"
6
7using namespace std::chrono_literals;
8
9namespace VideoCore {
10namespace {
11constexpr auto UPDATE_TICK = 32ms;
12}
13
14ShaderNotify::ShaderNotify() = default;
15ShaderNotify::~ShaderNotify() = default;
16
17std::size_t ShaderNotify::GetShadersBuilding() {
18 const auto now = std::chrono::high_resolution_clock::now();
19 const auto diff = now - last_update;
20 if (diff > UPDATE_TICK) {
21 std::shared_lock lock(mutex);
22 last_updated_count = accurate_count;
23 }
24 return last_updated_count;
25}
26
27std::size_t ShaderNotify::GetShadersBuildingAccurate() {
28 std::shared_lock lock{mutex};
29 return accurate_count;
30}
31
32void ShaderNotify::MarkShaderComplete() {
33 std::unique_lock lock{mutex};
34 accurate_count--;
35}
36
37void ShaderNotify::MarkSharderBuilding() {
38 std::unique_lock lock{mutex};
39 accurate_count++;
40}
41
42} // namespace VideoCore
diff --git a/src/video_core/shader_notify.h b/src/video_core/shader_notify.h
new file mode 100644
index 000000000..a9c92d179
--- /dev/null
+++ b/src/video_core/shader_notify.h
@@ -0,0 +1,29 @@
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 <chrono>
8#include <shared_mutex>
9#include "common/common_types.h"
10
11namespace VideoCore {
12class ShaderNotify {
13public:
14 ShaderNotify();
15 ~ShaderNotify();
16
17 std::size_t GetShadersBuilding();
18 std::size_t GetShadersBuildingAccurate();
19
20 void MarkShaderComplete();
21 void MarkSharderBuilding();
22
23private:
24 std::size_t last_updated_count{};
25 std::size_t accurate_count{};
26 std::shared_mutex mutex;
27 std::chrono::high_resolution_clock::time_point last_update{};
28};
29} // namespace VideoCore
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d25b99a32..805bb954b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -661,6 +661,8 @@ void Config::ReadRendererValues() {
661 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); 661 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
662 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), 662 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
663 false); 663 false);
664 ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
665 QStringLiteral("use_asynchronous_shaders"), false);
664 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), 666 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
665 true); 667 true);
666 ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false); 668 ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
@@ -1145,6 +1147,8 @@ void Config::SaveRendererValues() {
1145 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); 1147 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1146 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), 1148 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
1147 Settings::values.use_assembly_shaders, false); 1149 Settings::values.use_assembly_shaders, false);
1150 WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
1151 Settings::values.use_asynchronous_shaders, false);
1148 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, 1152 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
1149 true); 1153 true);
1150 WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, 1154 WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 7c0fa7ec5..ce30188cd 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -24,6 +24,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
24 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 24 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
25 ui->use_vsync->setEnabled(runtime_lock); 25 ui->use_vsync->setEnabled(runtime_lock);
26 ui->use_assembly_shaders->setEnabled(runtime_lock); 26 ui->use_assembly_shaders->setEnabled(runtime_lock);
27 ui->use_asynchronous_shaders->setEnabled(runtime_lock);
27 ui->force_30fps_mode->setEnabled(runtime_lock); 28 ui->force_30fps_mode->setEnabled(runtime_lock);
28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 29 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
29 30
@@ -32,6 +33,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
32 static_cast<int>(Settings::values.gpu_accuracy.GetValue())); 33 static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
33 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 34 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
34 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); 35 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
36 ui->use_asynchronous_shaders->setChecked(
37 Settings::values.use_asynchronous_shaders.GetValue());
35 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 38 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
36 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue()); 39 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
37 ui->anisotropic_filtering_combobox->setCurrentIndex( 40 ui->anisotropic_filtering_combobox->setCurrentIndex(
@@ -41,6 +44,10 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
41 ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync); 44 ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
42 ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders, 45 ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
43 &Settings::values.use_assembly_shaders); 46 &Settings::values.use_assembly_shaders);
47 ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_shaders,
48 &Settings::values.use_asynchronous_shaders);
49 ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_shaders,
50 &Settings::values.use_asynchronous_shaders);
44 ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time, 51 ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
45 &Settings::values.use_fast_gpu_time); 52 &Settings::values.use_fast_gpu_time);
46 ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode, 53 ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
@@ -67,6 +74,14 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
67 if (Settings::values.use_assembly_shaders.UsingGlobal()) { 74 if (Settings::values.use_assembly_shaders.UsingGlobal()) {
68 Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked()); 75 Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
69 } 76 }
77 if (Settings::values.use_asynchronous_shaders.UsingGlobal()) {
78 Settings::values.use_asynchronous_shaders.SetValue(
79 ui->use_asynchronous_shaders->isChecked());
80 }
81 if (Settings::values.use_asynchronous_shaders.UsingGlobal()) {
82 Settings::values.use_asynchronous_shaders.SetValue(
83 ui->use_asynchronous_shaders->isChecked());
84 }
70 if (Settings::values.use_fast_gpu_time.UsingGlobal()) { 85 if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
71 Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked()); 86 Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
72 } 87 }
@@ -83,6 +98,10 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
83 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync); 98 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync);
84 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, 99 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
85 ui->use_assembly_shaders); 100 ui->use_assembly_shaders);
101 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
102 ui->use_asynchronous_shaders);
103 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
104 ui->use_asynchronous_shaders);
86 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 105 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
87 ui->use_fast_gpu_time); 106 ui->use_fast_gpu_time);
88 ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode, 107 ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
@@ -117,6 +136,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
117 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 136 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
118 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 137 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
119 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); 138 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
139 ui->use_asynchronous_shaders->setEnabled(
140 Settings::values.use_asynchronous_shaders.UsingGlobal());
120 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 141 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
121 ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal()); 142 ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
122 ui->anisotropic_filtering_combobox->setEnabled( 143 ui->anisotropic_filtering_combobox->setEnabled(
@@ -128,6 +149,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
128 ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy); 149 ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
129 ui->use_vsync->setTristate(true); 150 ui->use_vsync->setTristate(true);
130 ui->use_assembly_shaders->setTristate(true); 151 ui->use_assembly_shaders->setTristate(true);
152 ui->use_asynchronous_shaders->setTristate(true);
131 ui->use_fast_gpu_time->setTristate(true); 153 ui->use_fast_gpu_time->setTristate(true);
132 ui->force_30fps_mode->setTristate(true); 154 ui->force_30fps_mode->setTristate(true);
133 ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox); 155 ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 0021607ac..71e7dfe5e 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -73,6 +73,16 @@
73 </widget> 73 </widget>
74 </item> 74 </item>
75 <item> 75 <item>
76 <widget class="QCheckBox" name="use_asynchronous_shaders">
77 <property name="toolTip">
78 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
79 </property>
80 <property name="text">
81 <string>Use asynchronous shader building (experimental, OpenGL or Assembly shaders only)</string>
82 </property>
83 </widget>
84 </item>
85 <item>
76 <widget class="QCheckBox" name="force_30fps_mode"> 86 <widget class="QCheckBox" name="force_30fps_mode">
77 <property name="text"> 87 <property name="text">
78 <string>Force 30 FPS mode</string> 88 <string>Force 30 FPS mode</string>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9f758605a..6909d65d0 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -94,6 +94,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
94#include "core/perf_stats.h" 94#include "core/perf_stats.h"
95#include "core/settings.h" 95#include "core/settings.h"
96#include "core/telemetry_session.h" 96#include "core/telemetry_session.h"
97#include "video_core/gpu.h"
98#include "video_core/shader_notify.h"
97#include "yuzu/about_dialog.h" 99#include "yuzu/about_dialog.h"
98#include "yuzu/bootmanager.h" 100#include "yuzu/bootmanager.h"
99#include "yuzu/compatdb.h" 101#include "yuzu/compatdb.h"
@@ -498,6 +500,8 @@ void GMainWindow::InitializeWidgets() {
498 message_label->setAlignment(Qt::AlignLeft); 500 message_label->setAlignment(Qt::AlignLeft);
499 statusBar()->addPermanentWidget(message_label, 1); 501 statusBar()->addPermanentWidget(message_label, 1);
500 502
503 shader_building_label = new QLabel();
504 shader_building_label->setToolTip(tr("The amount of shaders currently being built"));
501 emu_speed_label = new QLabel(); 505 emu_speed_label = new QLabel();
502 emu_speed_label->setToolTip( 506 emu_speed_label->setToolTip(
503 tr("Current emulation speed. Values higher or lower than 100% " 507 tr("Current emulation speed. Values higher or lower than 100% "
@@ -510,7 +514,8 @@ void GMainWindow::InitializeWidgets() {
510 tr("Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For " 514 tr("Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For "
511 "full-speed emulation this should be at most 16.67 ms.")); 515 "full-speed emulation this should be at most 16.67 ms."));
512 516
513 for (auto& label : {emu_speed_label, game_fps_label, emu_frametime_label}) { 517 for (auto& label :
518 {shader_building_label, emu_speed_label, game_fps_label, emu_frametime_label}) {
514 label->setVisible(false); 519 label->setVisible(false);
515 label->setFrameStyle(QFrame::NoFrame); 520 label->setFrameStyle(QFrame::NoFrame);
516 label->setContentsMargins(4, 0, 4, 0); 521 label->setContentsMargins(4, 0, 4, 0);
@@ -1176,6 +1181,7 @@ void GMainWindow::ShutdownGame() {
1176 1181
1177 // Disable status bar updates 1182 // Disable status bar updates
1178 status_bar_update_timer.stop(); 1183 status_bar_update_timer.stop();
1184 shader_building_label->setVisible(false);
1179 emu_speed_label->setVisible(false); 1185 emu_speed_label->setVisible(false);
1180 game_fps_label->setVisible(false); 1186 game_fps_label->setVisible(false);
1181 emu_frametime_label->setVisible(false); 1187 emu_frametime_label->setVisible(false);
@@ -2186,6 +2192,17 @@ void GMainWindow::UpdateStatusBar() {
2186 } 2192 }
2187 2193
2188 auto results = Core::System::GetInstance().GetAndResetPerfStats(); 2194 auto results = Core::System::GetInstance().GetAndResetPerfStats();
2195 auto& shader_notify = Core::System::GetInstance().GPU().ShaderNotify();
2196 const auto shaders_building = shader_notify.GetShadersBuilding();
2197
2198 if (shaders_building != 0) {
2199 shader_building_label->setText(
2200 tr("Building: %1 shader").arg(shaders_building) +
2201 (shaders_building != 1 ? QString::fromStdString("s") : QString::fromStdString("")));
2202 shader_building_label->setVisible(true);
2203 } else {
2204 shader_building_label->setVisible(false);
2205 }
2189 2206
2190 if (Settings::values.use_frame_limit.GetValue()) { 2207 if (Settings::values.use_frame_limit.GetValue()) {
2191 emu_speed_label->setText(tr("Speed: %1% / %2%") 2208 emu_speed_label->setText(tr("Speed: %1% / %2%")
@@ -2315,9 +2332,12 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2315 if (behavior == ReinitializeKeyBehavior::Warning) { 2332 if (behavior == ReinitializeKeyBehavior::Warning) {
2316 const auto res = QMessageBox::information( 2333 const auto res = QMessageBox::information(
2317 this, tr("Confirm Key Rederivation"), 2334 this, tr("Confirm Key Rederivation"),
2318 tr("You are about to force rederive all of your keys. \nIf you do not know what this " 2335 tr("You are about to force rederive all of your keys. \nIf you do not know what "
2319 "means or what you are doing, \nthis is a potentially destructive action. \nPlease " 2336 "this "
2320 "make sure this is what you want \nand optionally make backups.\n\nThis will delete " 2337 "means or what you are doing, \nthis is a potentially destructive action. "
2338 "\nPlease "
2339 "make sure this is what you want \nand optionally make backups.\n\nThis will "
2340 "delete "
2321 "your autogenerated key files and re-run the key derivation module."), 2341 "your autogenerated key files and re-run the key derivation module."),
2322 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel}); 2342 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
2323 2343
@@ -2628,8 +2648,8 @@ int main(int argc, char* argv[]) {
2628 2648
2629#ifdef __APPLE__ 2649#ifdef __APPLE__
2630 // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". 2650 // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
2631 // But since we require the working directory to be the executable path for the location of the 2651 // But since we require the working directory to be the executable path for the location of
2632 // user folder in the Qt Frontend, we need to cd into that working directory 2652 // the user folder in the Qt Frontend, we need to cd into that working directory
2633 const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; 2653 const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
2634 chdir(bin_path.c_str()); 2654 chdir(bin_path.c_str());
2635#endif 2655#endif
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index adff65fb5..59d9073ae 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -248,6 +248,7 @@ private:
248 248
249 // Status bar elements 249 // Status bar elements
250 QLabel* message_label = nullptr; 250 QLabel* message_label = nullptr;
251 QLabel* shader_building_label = nullptr;
251 QLabel* emu_speed_label = nullptr; 252 QLabel* emu_speed_label = nullptr;
252 QLabel* game_fps_label = nullptr; 253 QLabel* game_fps_label = nullptr;
253 QLabel* emu_frametime_label = nullptr; 254 QLabel* emu_frametime_label = nullptr;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 7773228c8..c2a2982fb 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -394,6 +394,10 @@ void Config::ReadValues() {
394 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1))); 394 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
395 Settings::values.use_assembly_shaders.SetValue( 395 Settings::values.use_assembly_shaders.SetValue(
396 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false)); 396 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false));
397 Settings::values.use_asynchronous_shaders.SetValue(
398 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
399 Settings::values.use_asynchronous_shaders.SetValue(
400 sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
397 Settings::values.use_fast_gpu_time.SetValue( 401 Settings::values.use_fast_gpu_time.SetValue(
398 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true)); 402 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
399 403
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 5bed47fd7..aa9e40380 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -166,6 +166,10 @@ use_vsync =
166# 0 (default): Off, 1: On 166# 0 (default): Off, 1: On
167use_assembly_shaders = 167use_assembly_shaders =
168 168
169# Whether to allow asynchronous shader building.
170# 0 (default): Off, 1: On
171use_asynchronous_shaders =
172
169# Turns on the frame limiter, which will limit frames output to the target game speed 173# Turns on the frame limiter, which will limit frames output to the target game speed
170# 0: Off, 1: On (default) 174# 0: Off, 1: On (default)
171use_frame_limit = 175use_frame_limit =