Video Core: debug tools (#412)

* video_core: better use of rdoc markers

* renderer_vulkan: added gpu assisted validation

* renderer_vulkan: make nv_checkpoints operational

* video_core: unified Vulkan objects names
This commit is contained in:
psucien 2024-08-12 13:46:45 +02:00 committed by GitHub
parent 3e2d4d6b79
commit ace39957ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 161 additions and 29 deletions

View File

@ -25,7 +25,9 @@ static bool shouldDumpPM4 = false;
static u32 vblankDivider = 1; static u32 vblankDivider = 1;
static bool vkValidation = false; static bool vkValidation = false;
static bool vkValidationSync = false; static bool vkValidationSync = false;
static bool vkValidationGpu = false;
static bool rdocEnable = false; static bool rdocEnable = false;
static bool rdocMarkersEnable = false;
// Gui // Gui
std::string settings_install_dir = ""; std::string settings_install_dir = "";
u32 main_window_geometry_x = 400; u32 main_window_geometry_x = 400;
@ -102,6 +104,10 @@ bool isRdocEnabled() {
return rdocEnable; return rdocEnable;
} }
bool isMarkersEnabled() {
return rdocMarkersEnable;
}
u32 vblankDiv() { u32 vblankDiv() {
return vblankDivider; return vblankDivider;
} }
@ -114,6 +120,10 @@ bool vkValidationSyncEnabled() {
return vkValidationSync; return vkValidationSync;
} }
bool vkValidationGpuEnabled() {
return vkValidationGpu;
}
void setScreenWidth(u32 width) { void setScreenWidth(u32 width) {
screenWidth = width; screenWidth = width;
} }
@ -319,7 +329,9 @@ void load(const std::filesystem::path& path) {
gpuId = toml::find_or<int>(vk, "gpuId", -1); gpuId = toml::find_or<int>(vk, "gpuId", -1);
vkValidation = toml::find_or<bool>(vk, "validation", false); vkValidation = toml::find_or<bool>(vk, "validation", false);
vkValidationSync = toml::find_or<bool>(vk, "validation_sync", false); vkValidationSync = toml::find_or<bool>(vk, "validation_sync", false);
vkValidationGpu = toml::find_or<bool>(vk, "validation_gpu", true);
rdocEnable = toml::find_or<bool>(vk, "rdocEnable", false); rdocEnable = toml::find_or<bool>(vk, "rdocEnable", false);
rdocMarkersEnable = toml::find_or<bool>(vk, "rdocMarkersEnable", false);
} }
if (data.contains("Debug")) { if (data.contains("Debug")) {
@ -394,7 +406,9 @@ void save(const std::filesystem::path& path) {
data["Vulkan"]["gpuId"] = gpuId; data["Vulkan"]["gpuId"] = gpuId;
data["Vulkan"]["validation"] = vkValidation; data["Vulkan"]["validation"] = vkValidation;
data["Vulkan"]["validation_sync"] = vkValidationSync; data["Vulkan"]["validation_sync"] = vkValidationSync;
data["Vulkan"]["validation_gpu"] = vkValidationGpu;
data["Vulkan"]["rdocEnable"] = rdocEnable; data["Vulkan"]["rdocEnable"] = rdocEnable;
data["Vulkan"]["rdocMarkersEnable"] = rdocMarkersEnable;
data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["DebugDump"] = isDebugDump;
data["LLE"]["libc"] = isLibc; data["LLE"]["libc"] = isLibc;
data["GUI"]["theme"] = mw_themes; data["GUI"]["theme"] = mw_themes;

View File

@ -27,6 +27,7 @@ bool nullGpu();
bool dumpShaders(); bool dumpShaders();
bool dumpPM4(); bool dumpPM4();
bool isRdocEnabled(); bool isRdocEnabled();
bool isMarkersEnabled();
u32 vblankDiv(); u32 vblankDiv();
void setDebugDump(bool enable); void setDebugDump(bool enable);
@ -50,6 +51,7 @@ void setRdocEnabled(bool enable);
bool vkValidationEnabled(); bool vkValidationEnabled();
bool vkValidationSyncEnabled(); bool vkValidationSyncEnabled();
bool vkValidationGpuEnabled();
// Gui // Gui
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);

View File

@ -180,6 +180,17 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
Platform::IrqC::Instance()->Signal(Platform::InterruptId::GfxFlip); Platform::IrqC::Instance()->Signal(Platform::InterruptId::GfxFlip);
break; break;
} }
case PM4CmdNop::PayloadType::DebugMarkerPush: {
const auto marker_sz = nop->header.count.Value() * 2;
const std::string_view label{reinterpret_cast<const char*>(&nop->data_block[1]),
marker_sz};
rasterizer->ScopeMarkerBegin(label);
break;
}
case PM4CmdNop::PayloadType::DebugMarkerPop: {
rasterizer->ScopeMarkerEnd();
break;
}
default: default:
break; break;
} }
@ -295,8 +306,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
regs.num_indices = draw_index->index_count; regs.num_indices = draw_index->index_count;
regs.draw_initiator = draw_index->draw_initiator; regs.draw_initiator = draw_index->draw_initiator;
if (rasterizer) { if (rasterizer) {
rasterizer->ScopeMarkerBegin( const auto cmd_address = reinterpret_cast<const void*>(header);
fmt::format("dcb:{}:DrawIndex2", reinterpret_cast<const void*>(dcb.data()))); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address));
rasterizer->Breadcrumb(u64(cmd_address));
rasterizer->Draw(true); rasterizer->Draw(true);
rasterizer->ScopeMarkerEnd(); rasterizer->ScopeMarkerEnd();
} }
@ -308,8 +320,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
regs.num_indices = draw_index_off->index_count; regs.num_indices = draw_index_off->index_count;
regs.draw_initiator = draw_index_off->draw_initiator; regs.draw_initiator = draw_index_off->draw_initiator;
if (rasterizer) { if (rasterizer) {
rasterizer->ScopeMarkerBegin(fmt::format( const auto cmd_address = reinterpret_cast<const void*>(header);
"dcb:{}:DrawIndexOffset2", reinterpret_cast<const void*>(dcb.data()))); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexOffset2", cmd_address));
rasterizer->Breadcrumb(u64(cmd_address));
rasterizer->Draw(true, draw_index_off->index_offset); rasterizer->Draw(true, draw_index_off->index_offset);
rasterizer->ScopeMarkerEnd(); rasterizer->ScopeMarkerEnd();
} }
@ -320,8 +333,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
regs.num_indices = draw_index->index_count; regs.num_indices = draw_index->index_count;
regs.draw_initiator = draw_index->draw_initiator; regs.draw_initiator = draw_index->draw_initiator;
if (rasterizer) { if (rasterizer) {
rasterizer->ScopeMarkerBegin( const auto cmd_address = reinterpret_cast<const void*>(header);
fmt::format("dcb:{}:DrawIndexAuto", reinterpret_cast<const void*>(dcb.data()))); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address));
rasterizer->Breadcrumb(u64(cmd_address));
rasterizer->Draw(false); rasterizer->Draw(false);
rasterizer->ScopeMarkerEnd(); rasterizer->ScopeMarkerEnd();
} }
@ -334,8 +348,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
regs.cs_program.dim_z = dispatch_direct->dim_z; regs.cs_program.dim_z = dispatch_direct->dim_z;
regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator;
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
rasterizer->ScopeMarkerBegin( const auto cmd_address = reinterpret_cast<const void*>(header);
fmt::format("dcb:{}:Dispatch", reinterpret_cast<const void*>(dcb.data()))); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address));
rasterizer->Breadcrumb(u64(cmd_address));
rasterizer->DispatchDirect(); rasterizer->DispatchDirect();
rasterizer->ScopeMarkerEnd(); rasterizer->ScopeMarkerEnd();
} }
@ -486,8 +501,9 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, int vqid) {
regs.cs_program.dim_z = dispatch_direct->dim_z; regs.cs_program.dim_z = dispatch_direct->dim_z;
regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator;
if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) {
rasterizer->ScopeMarkerBegin(fmt::format( const auto cmd_address = reinterpret_cast<const void*>(header);
"acb[{}]:{}:Dispatch", vqid, reinterpret_cast<const void*>(acb.data()))); rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address));
rasterizer->Breadcrumb(u64(cmd_address));
rasterizer->DispatchDirect(); rasterizer->DispatchDirect();
rasterizer->ScopeMarkerEnd(); rasterizer->ScopeMarkerEnd();
} }

View File

@ -106,10 +106,8 @@ Buffer::Buffer(const Vulkan::Instance& instance_, MemoryUsage usage_, VAddr cpu_
VmaAllocationInfo alloc_info{}; VmaAllocationInfo alloc_info{};
buffer.Create(buffer_ci, usage, &alloc_info); buffer.Create(buffer_ci, usage, &alloc_info);
if (instance->HasDebuggingToolAttached()) {
const auto device = instance->GetDevice(); const auto device = instance->GetDevice();
Vulkan::SetObjectName(device, Handle(), "Buffer {:#x} {} KiB", cpu_addr, size_bytes / 1024); Vulkan::SetObjectName(device, Handle(), "Buffer {:#x}:{:#x}", cpu_addr, size_bytes);
}
// Map it if it is host visible. // Map it if it is host visible.
VkMemoryPropertyFlags property_flags{}; VkMemoryPropertyFlags property_flags{};
@ -152,10 +150,8 @@ StreamBuffer::StreamBuffer(const Vulkan::Instance& instance, Vulkan::Scheduler&
ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE); ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE);
const auto device = instance.GetDevice(); const auto device = instance.GetDevice();
if (instance.HasDebuggingToolAttached()) { Vulkan::SetObjectName(device, Handle(), "StreamBuffer({}):{:#x}", BufferTypeName(usage),
Vulkan::SetObjectName(device, Handle(), "StreamBuffer({}): {} KiB", BufferTypeName(usage), size_bytes);
size_bytes / 1024);
}
} }
std::pair<u8*, u64> StreamBuffer::Map(u64 size, u64 alignment) { std::pair<u8*, u64> StreamBuffer::Map(u64 size, u64 alignment) {

View File

@ -8,6 +8,7 @@
#include <fmt/ranges.h> #include <fmt/ranges.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h"
#include "sdl_window.h" #include "sdl_window.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
@ -213,6 +214,13 @@ bool Instance::CreateDevice() {
add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
const bool has_sync2 = add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
if (has_sync2) {
has_nv_checkpoints = Config::isMarkersEnabled()
? add_extension(VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME)
: false;
}
const auto family_properties = physical_device.getQueueFamilyProperties(); const auto family_properties = physical_device.getQueueFamilyProperties();
if (family_properties.empty()) { if (family_properties.empty()) {
@ -308,6 +316,9 @@ bool Instance::CreateDevice() {
vk::PhysicalDeviceRobustness2FeaturesEXT{ vk::PhysicalDeviceRobustness2FeaturesEXT{
.nullDescriptor = true, .nullDescriptor = true,
}, },
vk::PhysicalDeviceSynchronization2Features{
.synchronization2 = has_sync2,
},
}; };
if (!color_write_en) { if (!color_write_en) {

View File

@ -88,6 +88,10 @@ public:
return profiler_context; return profiler_context;
} }
bool HasNvCheckpoints() const {
return has_nv_checkpoints;
}
/// Returns true when a known debugging tool is attached. /// Returns true when a known debugging tool is attached.
bool HasDebuggingToolAttached() const { bool HasDebuggingToolAttached() const {
return has_renderdoc || has_nsight_graphics; return has_renderdoc || has_nsight_graphics;
@ -259,6 +263,7 @@ private:
bool debug_utils_supported{}; bool debug_utils_supported{};
bool has_nsight_graphics{}; bool has_nsight_graphics{};
bool has_renderdoc{}; bool has_renderdoc{};
bool has_nv_checkpoints{};
}; };
} // namespace Vulkan } // namespace Vulkan

View File

@ -221,12 +221,61 @@ vk::UniqueInstance CreateInstance(vk::DynamicLoader& dl, Frontend::WindowSystemT
vk::Bool32 enable_sync = vk::Bool32 enable_sync =
enable_validation && Config::vkValidationSyncEnabled() ? vk::True : vk::False; enable_validation && Config::vkValidationSyncEnabled() ? vk::True : vk::False;
vk::LayerSettingEXT layer_set = { vk::Bool32 enable_gpuav =
enable_validation && Config::vkValidationSyncEnabled() ? vk::True : vk::False;
const char* gpuav_mode = enable_validation && Config::vkValidationGpuEnabled()
? "GPU_BASED_GPU_ASSISTED"
: "GPU_BASED_NONE";
const std::array layer_setings = {
vk::LayerSettingEXT{
.pLayerName = VALIDATION_LAYER_NAME, .pLayerName = VALIDATION_LAYER_NAME,
.pSettingName = "validate_sync", .pSettingName = "validate_sync",
.type = vk::LayerSettingTypeEXT::eBool32, .type = vk::LayerSettingTypeEXT::eBool32,
.valueCount = 1, .valueCount = 1,
.pValues = &enable_sync, .pValues = &enable_sync,
},
vk::LayerSettingEXT{
.pLayerName = VALIDATION_LAYER_NAME,
.pSettingName = "sync_queue_submit",
.type = vk::LayerSettingTypeEXT::eBool32,
.valueCount = 1,
.pValues = &enable_sync,
},
vk::LayerSettingEXT{
.pLayerName = VALIDATION_LAYER_NAME,
.pSettingName = "validate_gpu_based",
.type = vk::LayerSettingTypeEXT::eString,
.valueCount = 1,
.pValues = &gpuav_mode,
},
vk::LayerSettingEXT{
.pLayerName = VALIDATION_LAYER_NAME,
.pSettingName = "gpuav_reserve_binding_slot",
.type = vk::LayerSettingTypeEXT::eBool32,
.valueCount = 1,
.pValues = &enable_gpuav,
},
vk::LayerSettingEXT{
.pLayerName = VALIDATION_LAYER_NAME,
.pSettingName = "gpuav_descriptor_checks",
.type = vk::LayerSettingTypeEXT::eBool32,
.valueCount = 1,
.pValues = &enable_gpuav,
},
vk::LayerSettingEXT{
.pLayerName = VALIDATION_LAYER_NAME,
.pSettingName = "gpuav_validate_indirect_buffer",
.type = vk::LayerSettingTypeEXT::eBool32,
.valueCount = 1,
.pValues = &enable_gpuav,
},
vk::LayerSettingEXT{
.pLayerName = VALIDATION_LAYER_NAME,
.pSettingName = "gpuav_buffer_copies",
.type = vk::LayerSettingTypeEXT::eBool32,
.valueCount = 1,
.pValues = &enable_gpuav,
},
}; };
vk::StructureChain<vk::InstanceCreateInfo, vk::LayerSettingsCreateInfoEXT> instance_ci_chain = { vk::StructureChain<vk::InstanceCreateInfo, vk::LayerSettingsCreateInfoEXT> instance_ci_chain = {
@ -238,8 +287,8 @@ vk::UniqueInstance CreateInstance(vk::DynamicLoader& dl, Frontend::WindowSystemT
.ppEnabledExtensionNames = extensions.data(), .ppEnabledExtensionNames = extensions.data(),
}, },
vk::LayerSettingsCreateInfoEXT{ vk::LayerSettingsCreateInfoEXT{
.settingCount = 1, .settingCount = layer_setings.size(),
.pSettings = &layer_set, .pSettings = layer_setings.data(),
}, },
}; };

View File

@ -230,16 +230,42 @@ void Rasterizer::UpdateDepthStencilState() {
cmdbuf.setDepthBoundsTestEnable(depth.depth_bounds_enable); cmdbuf.setDepthBoundsTestEnable(depth.depth_bounds_enable);
} }
void Rasterizer::ScopeMarkerBegin(const std::string& str) { void Rasterizer::ScopeMarkerBegin(const std::string_view& str) {
if (!Config::isMarkersEnabled()) {
return;
}
const auto cmdbuf = scheduler.CommandBuffer(); const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
.pLabelName = str.c_str(), .pLabelName = str.data(),
}); });
} }
void Rasterizer::ScopeMarkerEnd() { void Rasterizer::ScopeMarkerEnd() {
if (!Config::isMarkersEnabled()) {
return;
}
const auto cmdbuf = scheduler.CommandBuffer(); const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.endDebugUtilsLabelEXT(); cmdbuf.endDebugUtilsLabelEXT();
} }
void Rasterizer::ScopedMarkerInsert(const std::string_view& str) {
if (!Config::isMarkersEnabled()) {
return;
}
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.insertDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
.pLabelName = str.data(),
});
}
void Rasterizer::Breadcrumb(u64 id) {
if (!instance.HasNvCheckpoints()) {
return;
}
scheduler.CommandBuffer().setCheckpointNV(id);
}
} // namespace Vulkan } // namespace Vulkan

View File

@ -35,8 +35,10 @@ public:
void DispatchDirect(); void DispatchDirect();
void ScopeMarkerBegin(const std::string& str); void ScopeMarkerBegin(const std::string_view& str);
void ScopeMarkerEnd(); void ScopeMarkerEnd();
void ScopedMarkerInsert(const std::string_view& str);
void Breadcrumb(u64 id);
void InvalidateMemory(VAddr addr, u64 size); void InvalidateMemory(VAddr addr, u64 size);
void MapMemory(VAddr addr, u64 size); void MapMemory(VAddr addr, u64 size);

View File

@ -158,6 +158,13 @@ void Scheduler::SubmitExecution(SubmitInfo& info) {
try { try {
instance.GetGraphicsQueue().submit(submit_info, info.fence); instance.GetGraphicsQueue().submit(submit_info, info.fence);
} catch (vk::DeviceLostError& err) { } catch (vk::DeviceLostError& err) {
if (instance.HasNvCheckpoints()) {
const auto checkpoint_data = instance.GetGraphicsQueue().getCheckpointData2NV();
for (const auto& cp : checkpoint_data) {
LOG_CRITICAL(Render_Vulkan, "{}: {:#x}", vk::to_string(cp.stage),
reinterpret_cast<u64>(cp.pCheckpointMarker));
}
}
UNREACHABLE_MSG("Device lost during submit: {}", err.what()); UNREACHABLE_MSG("Device lost during submit: {}", err.what());
} }

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
@ -154,6 +155,9 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
}; };
image.Create(image_ci); image.Create(image_ci);
Vulkan::SetObjectName(instance->GetDevice(), (vk::Image)image, "Image {:#x}:{:#x}",
info.guest_address, info.guest_size_bytes);
} }
void Image::Transit(vk::ImageLayout dst_layout, vk::Flags<vk::AccessFlagBits> dst_mask, void Image::Transit(vk::ImageLayout dst_layout, vk::Flags<vk::AccessFlagBits> dst_mask,