mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Implement game render thread delay
More details: https://www.reddit.com/r/Citra/comments/1e1v4e1/fixing_luigis_mansion_2_performance_issues_once/
This commit is contained in:
		
							parent
							
								
									cc220928bd
								
							
						
					
					
						commit
						0aa28b3785
					
				
					 13 changed files with 168 additions and 3 deletions
				
			
		|  | @ -40,7 +40,8 @@ enum class IntSetting( | |||
|     VSYNC("use_vsync_new", Settings.SECTION_RENDERER, 1), | ||||
|     DEBUG_RENDERER("renderer_debug", Settings.SECTION_DEBUG, 0), | ||||
|     TEXTURE_FILTER("texture_filter", Settings.SECTION_RENDERER, 0), | ||||
|     USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1); | ||||
|     USE_FRAME_LIMIT("use_frame_limit", Settings.SECTION_RENDERER, 1), | ||||
|     DELAY_RENDER_THREAD_US("delay_game_render_thread_us", Settings.SECTION_RENDERER, 0); | ||||
| 
 | ||||
|     override var int: Int = defaultValue | ||||
| 
 | ||||
|  |  | |||
|  | @ -729,6 +729,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
|                     IntSetting.TEXTURE_FILTER.defaultValue | ||||
|                 ) | ||||
|             ) | ||||
|             add( | ||||
|                 SliderSetting( | ||||
|                     IntSetting.DELAY_RENDER_THREAD_US, | ||||
|                     R.string.delay_render_thread, | ||||
|                     R.string.delay_render_thread_description, | ||||
|                     0, | ||||
|                     16000, | ||||
|                     " μs", | ||||
|                     IntSetting.DELAY_RENDER_THREAD_US.key, | ||||
|                     IntSetting.DELAY_RENDER_THREAD_US.defaultValue.toFloat() | ||||
|                 ) | ||||
|             ) | ||||
| 
 | ||||
|             add(HeaderSetting(R.string.stereoscopy)) | ||||
|             add( | ||||
|  |  | |||
|  | @ -169,6 +169,7 @@ void Config::ReadValues() { | |||
|     ReadSetting("Renderer", Settings::values.bg_red); | ||||
|     ReadSetting("Renderer", Settings::values.bg_green); | ||||
|     ReadSetting("Renderer", Settings::values.bg_blue); | ||||
|     ReadSetting("Renderer", Settings::values.delay_game_render_thread_us); | ||||
| 
 | ||||
|     // Layout
 | ||||
|     Settings::values.layout_option = static_cast<Settings::LayoutOption>(sdl2_config->GetInteger( | ||||
|  |  | |||
|  | @ -175,6 +175,10 @@ anaglyph_shader_name = | |||
| # 0: Nearest, 1 (default): Linear | ||||
| filter_mode = | ||||
| 
 | ||||
| # Delays the game render thread by the specified amount of microseconds | ||||
| # Set to 0 for no delay, only useful in dynamic-fps games to simulate GPU delay. | ||||
| delay_game_render_thread_us = | ||||
| 
 | ||||
| [Layout] | ||||
| # Layout for the screen inside the render window. | ||||
| # 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen, 3: Side by Side | ||||
|  |  | |||
|  | @ -663,5 +663,7 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string> | |||
|     <string name="artic_base_connect">Conectar con Artic Base</string> | ||||
|     <string name="artic_base_connect_description">Conectar con una consola real que esté ejecutando un servidor Artic Base</string> | ||||
|     <string name="artic_base_enter_address">Introduce la dirección del servidor Artic Base</string> | ||||
|     <string name="delay_render_thread">Retrasa el hilo de dibujado del juego</string> | ||||
|     <string name="delay_render_thread_description">Retrasa el hilo de dibujado del juego cuando envía datos a la GPU. Ayuda con problemas de rendimiento en los (muy pocos) juegos de fps dinámicos.</string> | ||||
| 
 | ||||
| </resources> | ||||
|  |  | |||
|  | @ -689,5 +689,7 @@ | |||
|     <string name="artic_base_connect_description">Connect to a real console that is running an Artic Base server</string> | ||||
|     <string name="artic_base_connect">Connect to Artic Base</string> | ||||
|     <string name="artic_base_enter_address">Enter Artic Base server address</string> | ||||
|     <string name="delay_render_thread">Delay game render thread</string> | ||||
|     <string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) dynamic-fps games.</string> | ||||
| 
 | ||||
| </resources> | ||||
|  |  | |||
|  | @ -147,6 +147,7 @@ void Config::ReadValues() { | |||
|     ReadSetting("Renderer", Settings::values.use_vsync_new); | ||||
|     ReadSetting("Renderer", Settings::values.texture_filter); | ||||
|     ReadSetting("Renderer", Settings::values.texture_sampling); | ||||
|     ReadSetting("Renderer", Settings::values.delay_game_render_thread_us); | ||||
| 
 | ||||
|     ReadSetting("Renderer", Settings::values.mono_render_option); | ||||
|     ReadSetting("Renderer", Settings::values.render_3d); | ||||
|  |  | |||
|  | @ -667,6 +667,8 @@ void Config::ReadRendererValues() { | |||
|     ReadGlobalSetting(Settings::values.texture_filter); | ||||
|     ReadGlobalSetting(Settings::values.texture_sampling); | ||||
| 
 | ||||
|     ReadGlobalSetting(Settings::values.delay_game_render_thread_us); | ||||
| 
 | ||||
|     if (global) { | ||||
|         ReadBasicSetting(Settings::values.use_shader_jit); | ||||
|     } | ||||
|  | @ -1168,6 +1170,8 @@ void Config::SaveRendererValues() { | |||
|     WriteGlobalSetting(Settings::values.texture_filter); | ||||
|     WriteGlobalSetting(Settings::values.texture_sampling); | ||||
| 
 | ||||
|     WriteGlobalSetting(Settings::values.delay_game_render_thread_us); | ||||
| 
 | ||||
|     if (global) { | ||||
|         WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(), | ||||
|                      true); | ||||
|  |  | |||
|  | @ -26,6 +26,10 @@ ConfigureGraphics::ConfigureGraphics(QString gl_renderer, std::span<const QStrin | |||
|     // Set the index to -1 to ensure the below lambda is called with setCurrentIndex
 | ||||
|     ui->graphics_api_combo->setCurrentIndex(-1); | ||||
| 
 | ||||
|     const auto width = static_cast<int>(QString::fromStdString("000000000").size() * 6); | ||||
|     ui->delay_render_display_label->setMinimumWidth(width); | ||||
|     ui->delay_render_combo->setVisible(!Settings::IsConfiguringGlobal()); | ||||
| 
 | ||||
|     auto graphics_api_combo_model = | ||||
|         qobject_cast<QStandardItemModel*>(ui->graphics_api_combo->model()); | ||||
| #ifndef ENABLE_SOFTWARE_RENDERER | ||||
|  | @ -82,12 +86,25 @@ ConfigureGraphics::ConfigureGraphics(QString gl_renderer, std::span<const QStrin | |||
|     connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||||
|             &ConfigureGraphics::SetPhysicalDeviceComboVisibility); | ||||
| 
 | ||||
|     connect(ui->delay_render_slider, &QSlider::valueChanged, this, [&](int value) { | ||||
|         ui->delay_render_display_label->setText( | ||||
|             QStringLiteral("%1 ms") | ||||
|                 .arg(((double)value) / 1000.f, 0, 'f', 3) | ||||
|                 .rightJustified(QString::fromStdString("000000000").size())); | ||||
|     }); | ||||
| 
 | ||||
|     SetConfiguration(); | ||||
| } | ||||
| 
 | ||||
| ConfigureGraphics::~ConfigureGraphics() = default; | ||||
| 
 | ||||
| void ConfigureGraphics::SetConfiguration() { | ||||
|     ui->delay_render_slider->setValue(Settings::values.delay_game_render_thread_us.GetValue()); | ||||
|     ui->delay_render_display_label->setText( | ||||
|         QStringLiteral("%1 ms") | ||||
|             .arg(((double)ui->delay_render_slider->value()) / 1000, 0, 'f', 3) | ||||
|             .rightJustified(QString::fromStdString("000000000").size())); | ||||
| 
 | ||||
|     if (!Settings::IsConfiguringGlobal()) { | ||||
|         ConfigurationShared::SetHighlight(ui->graphics_api_group, | ||||
|                                           !Settings::values.graphics_api.UsingGlobal()); | ||||
|  | @ -101,6 +118,16 @@ void ConfigureGraphics::SetConfiguration() { | |||
|                                                &Settings::values.texture_sampling); | ||||
|         ConfigurationShared::SetHighlight(ui->widget_texture_sampling, | ||||
|                                           !Settings::values.texture_sampling.UsingGlobal()); | ||||
|         ConfigurationShared::SetHighlight( | ||||
|             ui->delay_render_layout, !Settings::values.delay_game_render_thread_us.UsingGlobal()); | ||||
| 
 | ||||
|         if (Settings::values.delay_game_render_thread_us.UsingGlobal()) { | ||||
|             ui->delay_render_combo->setCurrentIndex(0); | ||||
|             ui->delay_render_slider->setEnabled(false); | ||||
|         } else { | ||||
|             ui->delay_render_combo->setCurrentIndex(1); | ||||
|             ui->delay_render_slider->setEnabled(true); | ||||
|         } | ||||
|     } else { | ||||
|         ui->graphics_api_combo->setCurrentIndex( | ||||
|             static_cast<int>(Settings::values.graphics_api.GetValue())); | ||||
|  | @ -144,6 +171,9 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
|                                              ui->toggle_disk_shader_cache, use_disk_shader_cache); | ||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync_new, ui->toggle_vsync_new, | ||||
|                                              use_vsync_new); | ||||
|     ConfigurationShared::ApplyPerGameSetting( | ||||
|         &Settings::values.delay_game_render_thread_us, ui->delay_render_combo, | ||||
|         [this](s32) { return ui->delay_render_slider->value(); }); | ||||
| 
 | ||||
|     if (Settings::IsConfiguringGlobal()) { | ||||
|         Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); | ||||
|  | @ -170,9 +200,16 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
|         ui->toggle_async_present->setEnabled(Settings::values.async_presentation.UsingGlobal()); | ||||
|         ui->graphics_api_combo->setEnabled(Settings::values.graphics_api.UsingGlobal()); | ||||
|         ui->physical_device_combo->setEnabled(Settings::values.physical_device.UsingGlobal()); | ||||
|         ui->delay_render_combo->setEnabled( | ||||
|             Settings::values.delay_game_render_thread_us.UsingGlobal()); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     connect(ui->delay_render_combo, qOverload<int>(&QComboBox::activated), this, [this](int index) { | ||||
|         ui->delay_render_slider->setEnabled(index == 1); | ||||
|         ConfigurationShared::SetHighlight(ui->delay_render_layout, index == 1); | ||||
|     }); | ||||
| 
 | ||||
|     ui->toggle_shader_jit->setVisible(false); | ||||
| 
 | ||||
|     ConfigurationShared::SetColoredComboBox( | ||||
|  |  | |||
|  | @ -307,6 +307,83 @@ | |||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|         <widget class="QWidget" name="delay_render_layout" native="true"> | ||||
|           <layout class="QHBoxLayout" name="delay_render_layout_inner"> | ||||
|             <property name="leftMargin"> | ||||
|               <number>0</number> | ||||
|             </property> | ||||
|             <property name="topMargin"> | ||||
|               <number>0</number> | ||||
|             </property> | ||||
|             <property name="rightMargin"> | ||||
|               <number>0</number> | ||||
|             </property> | ||||
|             <property name="bottomMargin"> | ||||
|               <number>0</number> | ||||
|             </property> | ||||
|             <item> | ||||
|               <widget class="QComboBox" name="delay_render_combo"> | ||||
|                 <item> | ||||
|                   <property name="text"> | ||||
|                     <string>Use global</string> | ||||
|                   </property> | ||||
|                 </item> | ||||
|                 <item> | ||||
|                   <property name="text"> | ||||
|                     <string>Use per-game</string> | ||||
|                   </property> | ||||
|                 </item> | ||||
|               </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|               <widget class="QLabel" name="label_delay_render"> | ||||
|                 <property name="text"> | ||||
|                   <string>Delay game render thread:</string> | ||||
|                 </property> | ||||
|                 <property name="toolTip"> | ||||
|                   <string><html><head/><body><p>Delays the emulated game render thread the specified amount of milliseconds every time it submits render commands to the GPU.</p><p>Adjust this feature in the (very few) dynamic-fps games to fix performance issues.</p></body></html></string> | ||||
|                 </property> | ||||
|               </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|               <widget class="QSlider" name="delay_render_slider"> | ||||
|                 <property name="minimum"> | ||||
|                   <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="maximum"> | ||||
|                   <number>16000</number> | ||||
|                 </property> | ||||
|                 <property name="singleStep"> | ||||
|                   <number>100</number> | ||||
|                 </property> | ||||
|                 <property name="pageStep"> | ||||
|                   <number>250</number> | ||||
|                 </property> | ||||
|                 <property name="value"> | ||||
|                   <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="orientation"> | ||||
|                   <enum>Qt::Horizontal</enum> | ||||
|                 </property> | ||||
|                 <property name="tickPosition"> | ||||
|                   <enum>QSlider::TicksBelow</enum> | ||||
|                 </property> | ||||
|               </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|               <widget class="QLabel" name="delay_render_display_label"> | ||||
|                 <property name="text"> | ||||
|                   <string/> | ||||
|                 </property> | ||||
|                 <property name="alignment"> | ||||
|                   <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|                 </property> | ||||
|               </widget> | ||||
|             </item> | ||||
|           </layout> | ||||
|         </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|  |  | |||
|  | @ -100,6 +100,7 @@ void LogSettings() { | |||
|     log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue())); | ||||
|     log_setting("Renderer_TextureSampling", | ||||
|                 GetTextureSamplingName(values.texture_sampling.GetValue())); | ||||
|     log_setting("Renderer_DelayGameRenderThreasUs", values.delay_game_render_thread_us.GetValue()); | ||||
|     log_setting("Stereoscopy_Render3d", values.render_3d.GetValue()); | ||||
|     log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue()); | ||||
|     log_setting("Stereoscopy_MonoRenderOption", values.mono_render_option.GetValue()); | ||||
|  | @ -192,6 +193,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
|     values.frame_limit.SetGlobal(true); | ||||
|     values.texture_filter.SetGlobal(true); | ||||
|     values.texture_sampling.SetGlobal(true); | ||||
|     values.delay_game_render_thread_us.SetGlobal(true); | ||||
|     values.layout_option.SetGlobal(true); | ||||
|     values.swap_screen.SetGlobal(true); | ||||
|     values.upright_screen.SetGlobal(true); | ||||
|  |  | |||
|  | @ -479,6 +479,8 @@ struct Values { | |||
|     SwitchableSetting<TextureFilter> texture_filter{TextureFilter::None, "texture_filter"}; | ||||
|     SwitchableSetting<TextureSampling> texture_sampling{TextureSampling::GameControlled, | ||||
|                                                         "texture_sampling"}; | ||||
|     SwitchableSetting<u16, true> delay_game_render_thread_us{0, 0, 16000, | ||||
|                                                              "delay_game_render_thread_us"}; | ||||
| 
 | ||||
|     SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"}; | ||||
|     SwitchableSetting<bool> swap_screen{false, "swap_screen"}; | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include <boost/serialization/shared_ptr.hpp> | ||||
| #include "common/archives.h" | ||||
| #include "common/bit_field.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
|  | @ -410,6 +411,9 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     auto* command_buffer = GetCommandBuffer(active_thread_id); | ||||
|     auto& gpu = system.GPU(); | ||||
| 
 | ||||
|     bool requires_delay = false; | ||||
| 
 | ||||
|     while (command_buffer->number_commands) { | ||||
|         if (command_buffer->should_stop) { | ||||
|             command_buffer->status.Assign(CommandBuffer::STATUS_STOPPED); | ||||
|  | @ -420,6 +424,10 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) { | |||
|         } | ||||
| 
 | ||||
|         Command command = command_buffer->commands[command_buffer->index]; | ||||
|         if (command.id == CommandId::SubmitCmdList && !requires_delay && | ||||
|             Settings::values.delay_game_render_thread_us.GetValue() != 0) { | ||||
|             requires_delay = true; | ||||
|         } | ||||
| 
 | ||||
|         // Decrease the number of commands remaining and increase the current index
 | ||||
|         command_buffer->number_commands.Assign(command_buffer->number_commands - 1); | ||||
|  | @ -435,8 +443,20 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(ResultSuccess); | ||||
|     if (requires_delay) { | ||||
|         ctx.RunAsync( | ||||
|             [](Kernel::HLERequestContext& ctx) { | ||||
|                 return Settings::values.delay_game_render_thread_us.GetValue() * 1000; | ||||
|             }, | ||||
|             [](Kernel::HLERequestContext& ctx) { | ||||
|                 IPC::RequestBuilder rb(ctx, 1, 0); | ||||
|                 rb.Push(ResultSuccess); | ||||
|             }, | ||||
|             false); | ||||
|     } else { | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(ResultSuccess); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue