diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 654de16f7..601e2616f 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -604,6 +604,7 @@ void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); ReadGlobalSetting(Settings::values.physical_device); + ReadGlobalSetting(Settings::values.async_command_recording); ReadGlobalSetting(Settings::values.graphics_api); ReadGlobalSetting(Settings::values.use_hw_renderer); ReadGlobalSetting(Settings::values.use_hw_shader); @@ -1085,6 +1086,7 @@ void Config::SaveRendererValues() { WriteGlobalSetting(Settings::values.graphics_api); WriteGlobalSetting(Settings::values.physical_device); + WriteGlobalSetting(Settings::values.async_command_recording); WriteGlobalSetting(Settings::values.use_hw_renderer); WriteGlobalSetting(Settings::values.use_hw_shader); #ifdef __APPLE__ diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index a4ccaf243..c79eb3d09 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -85,6 +85,7 @@ void ConfigureGraphics::SetConfiguration() { ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue()); ui->graphics_api_combo->setCurrentIndex(static_cast(Settings::values.graphics_api.GetValue())); ui->physical_device_combo->setCurrentIndex(static_cast(Settings::values.physical_device.GetValue())); + ui->toggle_async_recording->setChecked(Settings::values.async_command_recording.GetValue()); if (Settings::IsConfiguringGlobal()) { ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue()); @@ -106,6 +107,8 @@ void ConfigureGraphics::ApplyConfiguration() { use_vsync_new); ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api, ui->graphics_api_combo); ConfigurationShared::ApplyPerGameSetting(&Settings::values.physical_device, ui->physical_device_combo); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_command_recording, ui->toggle_async_recording, + async_command_recording); if (Settings::IsConfiguringGlobal()) { Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); diff --git a/src/citra_qt/configuration/configure_graphics.h b/src/citra_qt/configuration/configure_graphics.h index c3f5acb0b..61563dc78 100644 --- a/src/citra_qt/configuration/configure_graphics.h +++ b/src/citra_qt/configuration/configure_graphics.h @@ -40,6 +40,7 @@ private: ConfigurationShared::CheckState shaders_accurate_mul; ConfigurationShared::CheckState use_disk_shader_cache; ConfigurationShared::CheckState use_vsync_new; + ConfigurationShared::CheckState async_command_recording; std::unique_ptr ui; QColor bg_color; }; diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 0dda1f100..81fe84d5e 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -171,6 +171,16 @@ Advanced + + + + <html><head/><body><p>Offloads command buffer recording and fragment shader generation to a worker thread. Can improve performance especially on weaker systems. Disable if you notice better performance. If unsure leave it enabled,</p></body></html> + + + Async Command Recording + + + diff --git a/src/common/settings.cpp b/src/common/settings.cpp index af4db7386..64cc75563 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -106,6 +106,7 @@ void LogSettings() { log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue()); log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); log_setting("Renderer_GraphicsAPI", GetAPIName(values.graphics_api.GetValue())); + log_setting("Renderer_AsyncRecording", values.async_command_recording.GetValue()); log_setting("Renderer_UseHwRenderer", values.use_hw_renderer.GetValue()); log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); log_setting("Renderer_SeparableShader", values.separable_shader.GetValue()); diff --git a/src/common/settings.h b/src/common/settings.h index efd9a1520..7109a7769 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -450,6 +450,7 @@ struct Values { SwitchableSetting physical_device{0, "physical_device"}; Setting renderer_debug{false, "renderer_debug"}; Setting dump_command_buffers{false, "dump_command_buffers"}; + SwitchableSetting async_command_recording{true, "async_command_recording"}; SwitchableSetting use_hw_renderer{true, "use_hw_renderer"}; SwitchableSetting use_hw_shader{true, "use_hw_shader"}; SwitchableSetting separable_shader{false, "use_separable_shader"}; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index ee58e46f9..d40c4c48e 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -4,7 +4,7 @@ #include #include #include "common/microprofile.h" -#include "common/thread.h" +#include "core/settings.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" @@ -16,7 +16,7 @@ void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer render_cmdbuf, vk::Co while (command != nullptr) { auto next = command->GetNext(); command->Execute(render_cmdbuf, upload_cmdbuf); - command->~Command(); + std::destroy_at(command); command = next; } submit = false; @@ -26,10 +26,13 @@ void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer render_cmdbuf, vk::Co } Scheduler::Scheduler(const Instance& instance, RendererVulkan& renderer) - : instance{instance}, renderer{renderer}, master_semaphore{instance}, command_pool{instance, master_semaphore} { - AcquireNewChunk(); + : instance{instance}, renderer{renderer}, master_semaphore{instance}, command_pool{instance, master_semaphore}, + use_worker_thread{Settings::values.async_command_recording} { AllocateWorkerCommandBuffers(); - worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); }); + if (use_worker_thread) { + AcquireNewChunk(); + worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); }); + } } Scheduler::~Scheduler() = default; @@ -47,6 +50,10 @@ void Scheduler::Finish(vk::Semaphore signal, vk::Semaphore wait) { MICROPROFILE_DEFINE(Vulkan_WaitForWorker, "Vulkan", "Wait for worker", MP_RGB(255, 192, 192)); void Scheduler::WaitWorker() { + if (!use_worker_thread) { + return; + } + MICROPROFILE_SCOPE(Vulkan_WaitForWorker); DispatchWork(); @@ -162,8 +169,12 @@ void Scheduler::SubmitExecution(vk::Semaphore signal_semaphore, vk::Semaphore wa } }); - chunk->MarkSubmit(); - DispatchWork(); + if (!use_worker_thread) { + AllocateWorkerCommandBuffers(); + } else { + chunk->MarkSubmit(); + DispatchWork(); + } } void Scheduler::AcquireNewChunk() { diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 090c33068..3b2f9acfa 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -52,6 +52,11 @@ public: /// Records the command to the current chunk. template void Record(T&& command) { + if (!use_worker_thread) { + command(render_cmdbuf, upload_cmdbuf); + return; + } + if (chunk->Record(command)) { return; } @@ -144,7 +149,7 @@ private: return false; } Command* const current_last = last; - last = new (data.data() + command_offset) FuncType(std::move(command)); + last = std::construct_at(reinterpret_cast(data.data() + command_offset), std::move(command)); if (current_last) { current_last->SetNext(last); @@ -202,7 +207,7 @@ private: std::condition_variable_any work_cv; std::condition_variable wait_cv; std::jthread worker_thread; - std::jthread prsent_thread; + bool use_worker_thread; }; } // namespace Vulkan