renderer_vulkan: Add single-thread record ability to the scheduler
* Async is pretty nice but games that do a lot of flushes might have worse performance due to thread synchronization overhead * I haven't noticed any cases of this yet but it doesn't hurt making this a UI option
This commit is contained in:
@ -604,6 +604,7 @@ void Config::ReadRendererValues() {
|
|||||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||||
|
|
||||||
ReadGlobalSetting(Settings::values.physical_device);
|
ReadGlobalSetting(Settings::values.physical_device);
|
||||||
|
ReadGlobalSetting(Settings::values.async_command_recording);
|
||||||
ReadGlobalSetting(Settings::values.graphics_api);
|
ReadGlobalSetting(Settings::values.graphics_api);
|
||||||
ReadGlobalSetting(Settings::values.use_hw_renderer);
|
ReadGlobalSetting(Settings::values.use_hw_renderer);
|
||||||
ReadGlobalSetting(Settings::values.use_hw_shader);
|
ReadGlobalSetting(Settings::values.use_hw_shader);
|
||||||
@ -1085,6 +1086,7 @@ void Config::SaveRendererValues() {
|
|||||||
|
|
||||||
WriteGlobalSetting(Settings::values.graphics_api);
|
WriteGlobalSetting(Settings::values.graphics_api);
|
||||||
WriteGlobalSetting(Settings::values.physical_device);
|
WriteGlobalSetting(Settings::values.physical_device);
|
||||||
|
WriteGlobalSetting(Settings::values.async_command_recording);
|
||||||
WriteGlobalSetting(Settings::values.use_hw_renderer);
|
WriteGlobalSetting(Settings::values.use_hw_renderer);
|
||||||
WriteGlobalSetting(Settings::values.use_hw_shader);
|
WriteGlobalSetting(Settings::values.use_hw_shader);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
@ -85,6 +85,7 @@ void ConfigureGraphics::SetConfiguration() {
|
|||||||
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue());
|
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue());
|
||||||
ui->graphics_api_combo->setCurrentIndex(static_cast<int>(Settings::values.graphics_api.GetValue()));
|
ui->graphics_api_combo->setCurrentIndex(static_cast<int>(Settings::values.graphics_api.GetValue()));
|
||||||
ui->physical_device_combo->setCurrentIndex(static_cast<int>(Settings::values.physical_device.GetValue()));
|
ui->physical_device_combo->setCurrentIndex(static_cast<int>(Settings::values.physical_device.GetValue()));
|
||||||
|
ui->toggle_async_recording->setChecked(Settings::values.async_command_recording.GetValue());
|
||||||
|
|
||||||
if (Settings::IsConfiguringGlobal()) {
|
if (Settings::IsConfiguringGlobal()) {
|
||||||
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue());
|
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue());
|
||||||
@ -106,6 +107,8 @@ void ConfigureGraphics::ApplyConfiguration() {
|
|||||||
use_vsync_new);
|
use_vsync_new);
|
||||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api, ui->graphics_api_combo);
|
ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api, ui->graphics_api_combo);
|
||||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.physical_device, ui->physical_device_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()) {
|
if (Settings::IsConfiguringGlobal()) {
|
||||||
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
|
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
|
||||||
|
@ -40,6 +40,7 @@ private:
|
|||||||
ConfigurationShared::CheckState shaders_accurate_mul;
|
ConfigurationShared::CheckState shaders_accurate_mul;
|
||||||
ConfigurationShared::CheckState use_disk_shader_cache;
|
ConfigurationShared::CheckState use_disk_shader_cache;
|
||||||
ConfigurationShared::CheckState use_vsync_new;
|
ConfigurationShared::CheckState use_vsync_new;
|
||||||
|
ConfigurationShared::CheckState async_command_recording;
|
||||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||||
QColor bg_color;
|
QColor bg_color;
|
||||||
};
|
};
|
||||||
|
@ -171,6 +171,16 @@
|
|||||||
<string>Advanced</string>
|
<string>Advanced</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="toggle_async_recording">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><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></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Async Command Recording</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="toggle_disk_shader_cache">
|
<widget class="QCheckBox" name="toggle_disk_shader_cache">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
@ -106,6 +106,7 @@ void LogSettings() {
|
|||||||
log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue());
|
log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue());
|
||||||
log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue());
|
log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue());
|
||||||
log_setting("Renderer_GraphicsAPI", GetAPIName(values.graphics_api.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_UseHwRenderer", values.use_hw_renderer.GetValue());
|
||||||
log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue());
|
log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue());
|
||||||
log_setting("Renderer_SeparableShader", values.separable_shader.GetValue());
|
log_setting("Renderer_SeparableShader", values.separable_shader.GetValue());
|
||||||
|
@ -450,6 +450,7 @@ struct Values {
|
|||||||
SwitchableSetting<u16> physical_device{0, "physical_device"};
|
SwitchableSetting<u16> physical_device{0, "physical_device"};
|
||||||
Setting<bool> renderer_debug{false, "renderer_debug"};
|
Setting<bool> renderer_debug{false, "renderer_debug"};
|
||||||
Setting<bool> dump_command_buffers{false, "dump_command_buffers"};
|
Setting<bool> dump_command_buffers{false, "dump_command_buffers"};
|
||||||
|
SwitchableSetting<bool> async_command_recording{true, "async_command_recording"};
|
||||||
SwitchableSetting<bool> use_hw_renderer{true, "use_hw_renderer"};
|
SwitchableSetting<bool> use_hw_renderer{true, "use_hw_renderer"};
|
||||||
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
|
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
|
||||||
SwitchableSetting<bool> separable_shader{false, "use_separable_shader"};
|
SwitchableSetting<bool> separable_shader{false, "use_separable_shader"};
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common/microprofile.h"
|
#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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
#include "video_core/renderer_vulkan/renderer_vulkan.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) {
|
while (command != nullptr) {
|
||||||
auto next = command->GetNext();
|
auto next = command->GetNext();
|
||||||
command->Execute(render_cmdbuf, upload_cmdbuf);
|
command->Execute(render_cmdbuf, upload_cmdbuf);
|
||||||
command->~Command();
|
std::destroy_at(command);
|
||||||
command = next;
|
command = next;
|
||||||
}
|
}
|
||||||
submit = false;
|
submit = false;
|
||||||
@ -26,10 +26,13 @@ void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer render_cmdbuf, vk::Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scheduler::Scheduler(const Instance& instance, RendererVulkan& renderer)
|
Scheduler::Scheduler(const Instance& instance, RendererVulkan& renderer)
|
||||||
: instance{instance}, renderer{renderer}, master_semaphore{instance}, command_pool{instance, master_semaphore} {
|
: instance{instance}, renderer{renderer}, master_semaphore{instance}, command_pool{instance, master_semaphore},
|
||||||
AcquireNewChunk();
|
use_worker_thread{Settings::values.async_command_recording} {
|
||||||
AllocateWorkerCommandBuffers();
|
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;
|
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));
|
MICROPROFILE_DEFINE(Vulkan_WaitForWorker, "Vulkan", "Wait for worker", MP_RGB(255, 192, 192));
|
||||||
void Scheduler::WaitWorker() {
|
void Scheduler::WaitWorker() {
|
||||||
|
if (!use_worker_thread) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
|
MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
|
||||||
DispatchWork();
|
DispatchWork();
|
||||||
|
|
||||||
@ -162,8 +169,12 @@ void Scheduler::SubmitExecution(vk::Semaphore signal_semaphore, vk::Semaphore wa
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
chunk->MarkSubmit();
|
if (!use_worker_thread) {
|
||||||
DispatchWork();
|
AllocateWorkerCommandBuffers();
|
||||||
|
} else {
|
||||||
|
chunk->MarkSubmit();
|
||||||
|
DispatchWork();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::AcquireNewChunk() {
|
void Scheduler::AcquireNewChunk() {
|
||||||
|
@ -52,6 +52,11 @@ public:
|
|||||||
/// Records the command to the current chunk.
|
/// Records the command to the current chunk.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Record(T&& command) {
|
void Record(T&& command) {
|
||||||
|
if (!use_worker_thread) {
|
||||||
|
command(render_cmdbuf, upload_cmdbuf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (chunk->Record(command)) {
|
if (chunk->Record(command)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -144,7 +149,7 @@ private:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Command* const current_last = last;
|
Command* const current_last = last;
|
||||||
last = new (data.data() + command_offset) FuncType(std::move(command));
|
last = std::construct_at(reinterpret_cast<FuncType*>(data.data() + command_offset), std::move(command));
|
||||||
|
|
||||||
if (current_last) {
|
if (current_last) {
|
||||||
current_last->SetNext(last);
|
current_last->SetNext(last);
|
||||||
@ -202,7 +207,7 @@ private:
|
|||||||
std::condition_variable_any work_cv;
|
std::condition_variable_any work_cv;
|
||||||
std::condition_variable wait_cv;
|
std::condition_variable wait_cv;
|
||||||
std::jthread worker_thread;
|
std::jthread worker_thread;
|
||||||
std::jthread prsent_thread;
|
bool use_worker_thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
Reference in New Issue
Block a user