diff --git a/src/common/emulatorConfig.cpp b/src/common/emulatorConfig.cpp index 136a4a9..ef0e8e1 100644 --- a/src/common/emulatorConfig.cpp +++ b/src/common/emulatorConfig.cpp @@ -93,6 +93,10 @@ bool SpirvDebugPrintfEnabled() { return g_config->spirv_debug_printf_enabled; } +bool GpuAssistedValidationEnabled() { + return g_config->gpu_assisted_validation_enabled && g_config->vulkan_validation_enabled; +} + bool RenderDocEnabled() { return g_config->renderdoc_enabled; } diff --git a/src/common/emulatorConfig.h b/src/common/emulatorConfig.h index c0a8f27..1dbf49f 100644 --- a/src/common/emulatorConfig.h +++ b/src/common/emulatorConfig.h @@ -49,6 +49,7 @@ struct ConfigOptions { std::filesystem::path printf_output_file = "_kyty.txt"; ProfilerDirection profiler_direction = ProfilerDirection::None; bool spirv_debug_printf_enabled = false; + bool gpu_assisted_validation_enabled = false; bool renderdoc_enabled = false; bool readback_linear_images = false; bool playgo_hack_enabled = false; @@ -84,6 +85,8 @@ ProfilerDirection GetProfilerDirection(); bool SpirvDebugPrintfEnabled(); +bool GpuAssistedValidationEnabled(); + bool RenderDocEnabled(); bool ReadbackLinearImagesEnabled(); bool PlayGoHackEnabled(); diff --git a/src/graphics/host_gpu/renderer/context.cpp b/src/graphics/host_gpu/renderer/context.cpp index 33aa888..e72278c 100644 --- a/src/graphics/host_gpu/renderer/context.cpp +++ b/src/graphics/host_gpu/renderer/context.cpp @@ -17,10 +17,29 @@ #include #include +#include #include #include namespace Libs::Graphics { +namespace { + +void ReportVulkanFatal(const char* what, vk::Result result, uint32_t slot, uint64_t submit_seq, + uint32_t debug_op, uint64_t debug_submit, uint32_t arg0, uint32_t arg1, + uint32_t arg2, uint32_t arg3, uint64_t arg4) { + LOGF("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 + " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", + what, VulkanToString(result).c_str(), static_cast(result), slot, submit_seq, debug_op, + debug_submit, arg0, arg1, arg2, arg3, arg4); + std::printf("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 + " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", + what, VulkanToString(result).c_str(), static_cast(result), slot, submit_seq, + debug_op, debug_submit, arg0, arg1, arg2, arg3, arg4); + std::fflush(stdout); +} + +} // namespace + FenceResourceRetainer::~FenceResourceRetainer() { if (!m_resources.empty()) { EXIT("fence resource retainer destroyed before release\n"); @@ -169,8 +188,9 @@ void CommandBuffer::Execute(const SubmitInfo& submit) { auto result = graphics.device.resetFences(1, &fence); if (result != vk::Result::eSuccess) { - LOGF("vkResetFences failed before submit: %s (%d)\n", VulkanToString(result).c_str(), - static_cast(result)); + ReportVulkanFatal("vkResetFences (before submit)", result, m_slot->id, m_submit_seq, + m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, + m_debug_arg3, m_debug_arg4); } EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); @@ -192,11 +212,9 @@ void CommandBuffer::Execute(const SubmitInfo& submit) { m_fence_waited = false; if (result != vk::Result::eSuccess) { - LOGF("vkQueueSubmit failed: %s (%d), slot=%u submit_seq=%" PRIu64 - " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - VulkanToString(result).c_str(), static_cast(result), m_slot->id, m_submit_seq, - m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, - m_debug_arg4); + ReportVulkanFatal("vkQueueSubmit", result, m_slot->id, m_submit_seq, m_debug_op, + m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, + m_debug_arg4); } EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); } @@ -213,11 +231,9 @@ void CommandBuffer::WaitForFenceOnly() { auto device = m_graphics.device; auto result = device.waitForFences(1, &m_slot->fence, VK_TRUE, UINT64_MAX); if (result != vk::Result::eSuccess) { - LOGF("vkWaitForFences failed: %s (%d), slot=%u submit_seq=%" PRIu64 - " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - VulkanToString(result).c_str(), static_cast(result), m_slot->id, m_submit_seq, - m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, - m_debug_arg4); + ReportVulkanFatal("vkWaitForFences", result, m_slot->id, m_submit_seq, m_debug_op, + m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, + m_debug_arg4); } EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); m_fence_waited = true; diff --git a/src/graphics/presentation/window/vulkanWindow.cpp b/src/graphics/presentation/window/vulkanWindow.cpp index ac39823..9beee80 100644 --- a/src/graphics/presentation/window/vulkanWindow.cpp +++ b/src/graphics/presentation/window/vulkanWindow.cpp @@ -554,6 +554,14 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V EXIT_NOT_IMPLEMENTED(supported_features2.features.depthBiasClamp != VK_TRUE); features12.timelineSemaphore = VK_TRUE; + if (Config::GpuAssistedValidationEnabled()) { + if (supported_features12.bufferDeviceAddress != VK_TRUE) { + EXIT("--gpu-assisted-validation requires bufferDeviceAddress, unsupported by this " + "device\n"); + } + features12.bufferDeviceAddress = VK_TRUE; + } + vk::PhysicalDeviceFeatures device_features {}; device_features.fragmentStoresAndAtomics = VK_TRUE; device_features.samplerAnisotropy = VK_TRUE; @@ -571,6 +579,9 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V graphics.sample_rate_shading_enabled = true; device_features.vertexPipelineStoresAndAtomics = supported_features2.features.vertexPipelineStoresAndAtomics; + if (Config::GpuAssistedValidationEnabled()) { + device_features.shaderInt64 = supported_features2.features.shaderInt64; + } if (graphics.subgroup_size_control_enabled && supported_features13.subgroupSizeControl == VK_TRUE) { @@ -833,23 +844,26 @@ void WindowContext::CreateVulkan() { app_info.engineVersion = 1; app_info.apiVersion = VULKAN_TARGET_API_VERSION; // NOLINT - vk::ValidationFeatureEnableEXT enabled_features[] = { + if (Config::SpirvDebugPrintfEnabled() && Config::GpuAssistedValidationEnabled()) { + EXIT("--spirv-debug-printf and --gpu-assisted-validation are mutually exclusive\n"); + } + + vk::ValidationFeatureEnableEXT enabled_features[3] = {}; + uint32_t enabled_features_count = 0; #ifdef KYTY_ENABLE_BEST_PRACTICES - vk::ValidationFeatureEnableEXT::eBestPractices, + enabled_features[enabled_features_count++] = vk::ValidationFeatureEnableEXT::eBestPractices; #endif #ifdef KYTY_ENABLE_DEBUG_PRINTF - vk::ValidationFeatureEnableEXT::eDebugPrintf, -#endif - }; - - uint32_t enabled_features_count = - sizeof(enabled_features) / sizeof(vk::ValidationFeatureEnableEXT); - -#ifdef KYTY_ENABLE_DEBUG_PRINTF - if (!Config::SpirvDebugPrintfEnabled()) { - enabled_features_count--; + if (Config::SpirvDebugPrintfEnabled()) { + enabled_features[enabled_features_count++] = vk::ValidationFeatureEnableEXT::eDebugPrintf; } #endif + if (Config::GpuAssistedValidationEnabled()) { + enabled_features[enabled_features_count++] = vk::ValidationFeatureEnableEXT::eGpuAssisted; + LOGF("Vulkan GPU-assisted validation is enabled; expect a large slowdown\n"); + std::printf("Vulkan GPU-assisted validation is enabled; expect a large slowdown\n"); + std::fflush(stdout); + } vk::ValidationFeaturesEXT validation_features {}; validation_features.sType = vk::StructureType::eValidationFeaturesEXT; diff --git a/src/main.cpp b/src/main.cpp index 5272b1f..21db25b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -51,6 +51,8 @@ static void PrintUsage() { ::printf(" --vblank-frequency Virtual vblank frequency. Default: 60.\n"); ::printf(" --console-language <0-29> Console language. Default: 1 (English US).\n"); ::printf(" --vulkan-validation Enable Vulkan validation.\n"); + ::printf(" --gpu-assisted-validation Bounds-check shader accesses on the GPU.\n" + " Implies --vulkan-validation; very slow.\n"); ::printf(" --shader-validation Enable shader validation.\n"); ::printf(" --shader-optimization-type None, Size, or Performance.\n"); ::printf(" --shader-log-direction Silent, Console, or File.\n"); @@ -213,6 +215,11 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he ::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str()); return false; } + } else if (arg == "--gpu-assisted-validation") { + if (!ParseBool(value, options.config.gpu_assisted_validation_enabled)) { + ::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str()); + return false; + } } else if (arg == "--shader-validation") { if (!ParseBool(value, options.config.shader_validation_enabled)) { ::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str()); @@ -277,6 +284,10 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he } } + if (options.config.gpu_assisted_validation_enabled) { + options.config.vulkan_validation_enabled = true; + } + return show_help || (!options.app0_dir.empty() && !options.elf.empty()); }