renderer_vulkan: Catch and log more runtime errors
* Also add the ability to enable command buffer dumping which is very useful
This commit is contained in:
@ -455,6 +455,8 @@ void Config::ReadDebuggingValues() {
|
||||
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
|
||||
ReadBasicSetting(Settings::values.use_gdbstub);
|
||||
ReadBasicSetting(Settings::values.gdbstub_port);
|
||||
ReadBasicSetting(Settings::values.renderer_debug);
|
||||
ReadBasicSetting(Settings::values.dump_command_buffers);
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Service::service_module_map) {
|
||||
@ -624,7 +626,6 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.texture_filter_name);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.renderer_debug);
|
||||
ReadBasicSetting(Settings::values.use_shader_jit);
|
||||
}
|
||||
|
||||
@ -968,6 +969,8 @@ void Config::SaveDebuggingValues() {
|
||||
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
|
||||
WriteBasicSetting(Settings::values.use_gdbstub);
|
||||
WriteBasicSetting(Settings::values.gdbstub_port);
|
||||
WriteBasicSetting(Settings::values.renderer_debug);
|
||||
WriteBasicSetting(Settings::values.dump_command_buffers);
|
||||
|
||||
qt_config->beginGroup(QStringLiteral("LLE"));
|
||||
for (const auto& service_module : Settings::values.lle_modules) {
|
||||
@ -1102,7 +1105,6 @@ void Config::SaveRendererValues() {
|
||||
WriteGlobalSetting(Settings::values.texture_filter_name);
|
||||
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.renderer_debug);
|
||||
WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(),
|
||||
true);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
||||
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
||||
ui->toggle_dump_command_buffers->setEnabled(!is_powered_on);
|
||||
}
|
||||
|
||||
ConfigureDebug::~ConfigureDebug() = default;
|
||||
@ -39,6 +40,7 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
|
||||
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
|
||||
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
|
||||
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue());
|
||||
}
|
||||
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
@ -52,6 +54,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Log::SetGlobalFilter(filter);
|
||||
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
|
||||
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
|
||||
Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureDebug::RetranslateUI() {
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>443</width>
|
||||
<height>300</height>
|
||||
<width>454</width>
|
||||
<height>356</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -132,6 +132,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_dump_command_buffers">
|
||||
<property name="text">
|
||||
<string>Dump command buffers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -449,6 +449,7 @@ struct Values {
|
||||
SwitchableSetting<GraphicsAPI> graphics_api{GraphicsAPI::OpenGL, "graphics_api"};
|
||||
SwitchableSetting<u16> physical_device{0, "physical_device"};
|
||||
Setting<bool> renderer_debug{false, "renderer_debug"};
|
||||
Setting<bool> dump_command_buffers{false, "dump_command_buffers"};
|
||||
SwitchableSetting<bool> use_hw_renderer{true, "use_hw_renderer"};
|
||||
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
|
||||
SwitchableSetting<bool> separable_shader{false, "use_separable_shader"};
|
||||
|
@ -183,8 +183,7 @@ static std::array<float, 3 * 2> MakeOrthographicMatrix(float width, float height
|
||||
}
|
||||
|
||||
RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
|
||||
: RendererBase{window}, instance{window, Settings::values.physical_device,
|
||||
Settings::values.renderer_debug},
|
||||
: RendererBase{window}, instance{window, Settings::values.physical_device},
|
||||
scheduler{instance, *this}, renderpass_cache{instance, scheduler},
|
||||
runtime{instance, scheduler, renderpass_cache}, swapchain{instance, renderpass_cache},
|
||||
vertex_buffer{
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <span>
|
||||
#include "common/assert.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||
@ -66,7 +67,7 @@ Instance::Instance() {
|
||||
physical_devices = instance.enumeratePhysicalDevices();
|
||||
}
|
||||
|
||||
Instance::Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool enable_validation) {
|
||||
Instance::Instance(Frontend::EmuWindow& window, u32 physical_device_index) {
|
||||
auto window_info = window.GetWindowInfo();
|
||||
|
||||
// Fetch instance independant function pointers
|
||||
@ -90,8 +91,16 @@ Instance::Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool
|
||||
.engineVersion = VK_MAKE_VERSION(1, 0, 0),
|
||||
.apiVersion = available_version};
|
||||
|
||||
const std::array layers = {"VK_LAYER_KHRONOS_validation"};
|
||||
const u32 layer_count = enable_validation ? 1u : 0u;
|
||||
u32 layer_count = 0;
|
||||
std::array<const char*, 2> layers;
|
||||
|
||||
if (Settings::values.renderer_debug) {
|
||||
layers[layer_count++] = "VK_LAYER_KHRONOS_validation";
|
||||
}
|
||||
if (Settings::values.dump_command_buffers) {
|
||||
layers[layer_count++] = "VK_LAYER_LUNARG_api_dump";
|
||||
}
|
||||
|
||||
const vk::InstanceCreateInfo instance_info = {.pApplicationInfo = &application_info,
|
||||
.enabledLayerCount = layer_count,
|
||||
.ppEnabledLayerNames = layers.data(),
|
||||
|
@ -32,7 +32,7 @@ struct FormatTraits {
|
||||
class Instance {
|
||||
public:
|
||||
Instance(); ///< Portable constructor used to query physical devices
|
||||
Instance(Frontend::EmuWindow& window, u32 physical_device_index, bool enable_validation);
|
||||
Instance(Frontend::EmuWindow& window, u32 physical_device_index);
|
||||
~Instance();
|
||||
|
||||
/// Returns the FormatTraits struct for the provided pixel format
|
||||
@ -87,6 +87,7 @@ public:
|
||||
|
||||
/// Returns true when VK_KHR_timeline_semaphore is supported
|
||||
bool IsTimelineSemaphoreSupported() const {
|
||||
return false;
|
||||
return timeline_semaphores;
|
||||
}
|
||||
|
||||
|
@ -601,7 +601,13 @@ void PipelineCache::BindDescriptorSets() {
|
||||
.descriptorSetCount = DESCRIPTOR_BATCH_SIZE,
|
||||
.pSetLayouts = layouts.data()};
|
||||
|
||||
try {
|
||||
batch = device.allocateDescriptorSets(alloc_info);
|
||||
} catch (vk::OutOfPoolMemoryError& err) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Run out of pool memory for layout {}: {}",
|
||||
i, err.what());
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorSet set = batch.back();
|
||||
|
@ -95,7 +95,7 @@ constexpr VertexLayout RasterizerVulkan::HardwareVertex::GetVertexLayout() {
|
||||
return layout;
|
||||
}
|
||||
|
||||
constexpr u32 VERTEX_BUFFER_SIZE = 256 * 1024 * 1024;
|
||||
constexpr u32 VERTEX_BUFFER_SIZE = 64 * 1024 * 1024;
|
||||
constexpr u32 INDEX_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
constexpr u32 UNIFORM_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
constexpr u32 TEXTURE_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
|
@ -69,7 +69,7 @@ StreamBuffer::StreamBuffer(const Instance& instance, TaskScheduler& scheduler, u
|
||||
vk::BufferUsageFlagBits usage, std::span<const vk::Format> view_formats)
|
||||
: instance{instance}, scheduler{scheduler}, staging{instance, size,
|
||||
vk::BufferUsageFlagBits::eTransferSrc},
|
||||
usage{usage}, total_size{size} {
|
||||
usage{usage}, total_size{size * SCHEDULER_COMMAND_COUNT} {
|
||||
|
||||
const vk::BufferCreateInfo buffer_info = {
|
||||
.size = total_size, .usage = usage | vk::BufferUsageFlagBits::eTransferDst};
|
||||
@ -98,7 +98,7 @@ StreamBuffer::StreamBuffer(const Instance& instance, TaskScheduler& scheduler, u
|
||||
}
|
||||
|
||||
view_count = view_formats.size();
|
||||
bucket_size = size / SCHEDULER_COMMAND_COUNT;
|
||||
bucket_size = size;
|
||||
}
|
||||
|
||||
StreamBuffer::~StreamBuffer() {
|
||||
@ -177,9 +177,4 @@ void StreamBuffer::Flush() {
|
||||
buckets[next_bucket].invalid = true;
|
||||
}
|
||||
|
||||
u32 StreamBuffer::GetBufferOffset() const {
|
||||
const u32 current_bucket = scheduler.GetCurrentSlotIndex();
|
||||
return current_bucket * bucket_size + buckets[current_bucket].offset;
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -48,9 +48,6 @@ public:
|
||||
/// Flushes staging memory to the GPU buffer
|
||||
void Flush();
|
||||
|
||||
/// Returns the current buffer offset
|
||||
u32 GetBufferOffset() const;
|
||||
|
||||
/// Returns the Vulkan buffer handle
|
||||
vk::Buffer GetHandle() const {
|
||||
return buffer;
|
||||
|
@ -33,7 +33,6 @@ TaskScheduler::TaskScheduler(const Instance& instance, RendererVulkan& renderer)
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer, 1024},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eUniformBufferDynamic, 1024},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eSampledImage, 2048},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eCombinedImageSampler, 512},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eSampler, 2048},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eUniformTexelBuffer, 1024},
|
||||
vk::DescriptorPoolSize{vk::DescriptorType::eStorageImage, 1024}};
|
||||
@ -91,7 +90,7 @@ void TaskScheduler::Synchronize(u32 slot) {
|
||||
const auto& command = commands[slot];
|
||||
vk::Device device = instance.GetDevice();
|
||||
|
||||
u32 completed_counter = GetFenceCounter();
|
||||
const u64 completed_counter = GetFenceCounter();
|
||||
if (command.fence_counter > completed_counter) {
|
||||
if (instance.IsTimelineSemaphoreSupported()) {
|
||||
const vk::SemaphoreWaitInfo wait_info = {
|
||||
@ -134,6 +133,16 @@ void TaskScheduler::Submit(SubmitMode mode) {
|
||||
|
||||
command_buffers[command_buffer_count++] = command.render_command_buffer;
|
||||
|
||||
const auto QueueSubmit = [this](const vk::SubmitInfo& info, vk::Fence fence) {
|
||||
try {
|
||||
vk::Queue queue = instance.GetGraphicsQueue();
|
||||
queue.submit(info, fence);
|
||||
} catch (vk::DeviceLostError& err) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Device lost during submit: {}", err.what());
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
|
||||
const bool swapchain_sync = True(mode & SubmitMode::SwapchainSynced);
|
||||
if (instance.IsTimelineSemaphoreSupported()) {
|
||||
const u32 wait_semaphore_count = swapchain_sync ? 2u : 1u;
|
||||
@ -166,9 +175,7 @@ void TaskScheduler::Submit(SubmitMode mode) {
|
||||
.pSignalSemaphores = signal_semaphores.data(),
|
||||
};
|
||||
|
||||
vk::Queue queue = instance.GetGraphicsQueue();
|
||||
queue.submit(submit_info);
|
||||
|
||||
QueueSubmit(submit_info, command.fence);
|
||||
} else {
|
||||
const u32 signal_semaphore_count = swapchain_sync ? 1u : 0u;
|
||||
const u32 wait_semaphore_count = swapchain_sync ? 1u : 0u;
|
||||
@ -185,8 +192,7 @@ void TaskScheduler::Submit(SubmitMode mode) {
|
||||
.pSignalSemaphores = &command.present_ready,
|
||||
};
|
||||
|
||||
vk::Queue queue = instance.GetGraphicsQueue();
|
||||
queue.submit(submit_info, command.fence);
|
||||
QueueSubmit(submit_info, command.fence);
|
||||
}
|
||||
|
||||
// Block host until the GPU catches up
|
||||
|
Reference in New Issue
Block a user