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