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:
GPUCode
2022-10-27 14:22:03 +03:00
parent 58573dd7b3
commit 6f35a3bf37
7 changed files with 41 additions and 9 deletions

View File

@ -485,6 +485,7 @@ void Config::ReadRendererValues() {
ReadSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::GraphicsAPI::OpenGL))
.toUInt());
Settings::values.physical_device = ReadSetting(QStringLiteral("physical_device"), 0).toUInt();
Settings::values.async_command_recording = ReadSetting(QStringLiteral("async_command_recording"), true).toBool();
Settings::values.use_hw_renderer =
ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool();
Settings::values.use_hw_shader = ReadSetting(QStringLiteral("use_hw_shader"), true).toBool();
@ -1004,6 +1005,7 @@ void Config::SaveRendererValues() {
WriteSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::values.graphics_api),
static_cast<u32>(Settings::GraphicsAPI::OpenGL));
WriteSetting(QStringLiteral("physical_device"), Settings::values.physical_device, 0);
WriteSetting(QStringLiteral("async_command_recording"), Settings::values.async_command_recording, true);
WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);
#ifdef __APPLE__

View File

@ -83,6 +83,7 @@ void ConfigureGraphics::SetConfiguration() {
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
ui->graphics_api_combo->setCurrentIndex(static_cast<int>(Settings::values.graphics_api));
ui->physical_device_combo->setCurrentIndex(static_cast<int>(Settings::values.physical_device));
ui->toggle_async_recording->setChecked(Settings::values.async_command_recording);
}
void ConfigureGraphics::ApplyConfiguration() {
@ -96,6 +97,7 @@ void ConfigureGraphics::ApplyConfiguration() {
Settings::values.graphics_api =
static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
Settings::values.physical_device = static_cast<u16>(ui->physical_device_combo->currentIndex());
Settings::values.async_command_recording = ui->toggle_async_recording->isChecked();
}
void ConfigureGraphics::RetranslateUI() {

View File

@ -171,6 +171,16 @@
<string>Advanced</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="toggle_async_recording">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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,&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Async Command Recording</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_disk_shader_cache">
<property name="toolTip">

View File

@ -90,6 +90,7 @@ void LogSettings() {
LogSetting("Core_UseCpuJit", values.use_cpu_jit);
LogSetting("Core_CPUClockPercentage", values.cpu_clock_percentage);
LogSetting("Renderer_GraphicsAPI", GetAPIName(values.graphics_api));
LogSetting("Renderer_AsyncRecording", values.async_command_recording);
LogSetting("Renderer_UseHwRenderer", values.use_hw_renderer);
LogSetting("Renderer_UseHwShader", values.use_hw_shader);
LogSetting("Renderer_SeparableShader", values.separable_shader);

View File

@ -169,6 +169,7 @@ struct Values {
u16 physical_device;
bool renderer_debug;
bool dump_command_buffers;
bool async_command_recording;
bool use_hw_renderer;
bool use_hw_shader;
bool separable_shader;

View File

@ -4,7 +4,7 @@
#include <mutex>
#include <utility>
#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() {

View File

@ -52,6 +52,11 @@ public:
/// Records the command to the current chunk.
template <typename T>
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<FuncType*>(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