From 3b006f4fe28006d320c60fd2b4393fd3f27eacd7 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 27 Jul 2021 19:15:32 -0300 Subject: [PATCH] renderer_vulkan: Add setting to log pipeline statistics Use VK_KHR_pipeline_executable_properties when enabled and available to log statistics about the pipeline cache in a game. For example, this is on Turing GPUs when generating a pipeline cache from Super Smash Bros. Ultimate: Average pipeline statistics ========================================== Code size: 6433.167 Register count: 32.939 More advanced results could be presented, at the moment it's just an average of all 3D and compute pipelines. --- src/common/settings.h | 1 + src/video_core/CMakeLists.txt | 2 + .../renderer_vulkan/pipeline_statistics.cpp | 100 ++++++++++++++++++ .../renderer_vulkan/pipeline_statistics.h | 40 +++++++ .../renderer_vulkan/vk_compute_pipeline.cpp | 13 ++- .../renderer_vulkan/vk_compute_pipeline.h | 2 + .../renderer_vulkan/vk_graphics_pipeline.cpp | 16 ++- .../renderer_vulkan/vk_graphics_pipeline.h | 6 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 30 ++++-- .../renderer_vulkan/vk_pipeline_cache.h | 7 +- .../vulkan_common/vulkan_device.cpp | 29 +++++ src/video_core/vulkan_common/vulkan_device.h | 6 ++ .../vulkan_common/vulkan_wrapper.cpp | 38 +++++++ src/video_core/vulkan_common/vulkan_wrapper.h | 8 ++ src/yuzu/configuration/config.cpp | 2 + src/yuzu/configuration/configure_debug.cpp | 3 + src/yuzu/configuration/configure_debug.ui | 23 +++- src/yuzu_cmd/config.cpp | 1 + src/yuzu_cmd/default_ini.h | 4 + 19 files changed, 307 insertions(+), 24 deletions(-) create mode 100644 src/video_core/renderer_vulkan/pipeline_statistics.cpp create mode 100644 src/video_core/renderer_vulkan/pipeline_statistics.h diff --git a/src/common/settings.h b/src/common/settings.h index d8730f515..375569450 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -314,6 +314,7 @@ struct Values { // Renderer Setting renderer_backend{RendererBackend::OpenGL, "backend"}; BasicSetting renderer_debug{false, "debug"}; + BasicSetting renderer_shader_feedback{false, "shader_feedback"}; BasicSetting enable_nsight_aftermath{false, "nsight_aftermath"}; BasicSetting disable_shader_loop_safety_checks{false, "disable_shader_loop_safety_checks"}; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 007ecc13e..333f6f35f 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -106,6 +106,8 @@ add_library(video_core STATIC renderer_vulkan/maxwell_to_vk.cpp renderer_vulkan/maxwell_to_vk.h renderer_vulkan/pipeline_helper.h + renderer_vulkan/pipeline_statistics.cpp + renderer_vulkan/pipeline_statistics.h renderer_vulkan/renderer_vulkan.h renderer_vulkan/renderer_vulkan.cpp renderer_vulkan/vk_blit_screen.cpp diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp new file mode 100644 index 000000000..bfec931a6 --- /dev/null +++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp @@ -0,0 +1,100 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include + +#include "common/common_types.h" +#include "common/logging/log.h" +#include "video_core/renderer_vulkan/pipeline_statistics.h" +#include "video_core/vulkan_common/vulkan_device.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +using namespace std::string_view_literals; + +static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) { + switch (statistic.format) { + case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR: + return static_cast(statistic.value.i64); + case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR: + return statistic.value.u64; + case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR: + return static_cast(statistic.value.f64); + default: + return 0; + } +} + +PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {} + +void PipelineStatistics::Collect(VkPipeline pipeline) { + const auto& dev{device.GetLogical()}; + const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)}; + const u32 num_executables{static_cast(properties.size())}; + for (u32 executable = 0; executable < num_executables; ++executable) { + const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)}; + if (statistics.empty()) { + continue; + } + Stats stage_stats; + for (const auto& statistic : statistics) { + const char* const name{statistic.name}; + if (name == "Binary Size"sv || name == "Code size"sv || name == "Instruction Count"sv) { + stage_stats.code_size = GetUint64(statistic); + } else if (name == "Register Count"sv) { + stage_stats.register_count = GetUint64(statistic); + } else if (name == "SGPRs"sv || name == "numUsedSgprs"sv) { + stage_stats.sgpr_count = GetUint64(statistic); + } else if (name == "VGPRs"sv || name == "numUsedVgprs"sv) { + stage_stats.vgpr_count = GetUint64(statistic); + } else if (name == "Branches"sv) { + stage_stats.branches_count = GetUint64(statistic); + } else if (name == "Basic Block Count"sv) { + stage_stats.basic_block_count = GetUint64(statistic); + } + } + std::lock_guard lock{mutex}; + collected_stats.push_back(stage_stats); + } +} + +void PipelineStatistics::Report() const { + double num{}; + Stats total; + { + std::lock_guard lock{mutex}; + for (const Stats& stats : collected_stats) { + total.code_size += stats.code_size; + total.register_count += stats.register_count; + total.sgpr_count += stats.sgpr_count; + total.vgpr_count += stats.vgpr_count; + total.branches_count += stats.branches_count; + total.basic_block_count += stats.basic_block_count; + } + num = static_cast(collected_stats.size()); + } + std::string report; + const auto add = [&](const char* fmt, u64 value) { + if (value > 0) { + report += fmt::format(fmt::runtime(fmt), static_cast(value) / num); + } + }; + add("Code size: {:9.03f}\n", total.code_size); + add("Register count: {:9.03f}\n", total.register_count); + add("SGPRs: {:9.03f}\n", total.sgpr_count); + add("VGPRs: {:9.03f}\n", total.vgpr_count); + add("Branches count: {:9.03f}\n", total.branches_count); + add("Basic blocks: {:9.03f}\n", total.basic_block_count); + + LOG_INFO(Render_Vulkan, + "\nAverage pipeline statistics\n" + "==========================================\n" + "{}\n", + report); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.h b/src/video_core/renderer_vulkan/pipeline_statistics.h new file mode 100644 index 000000000..b61840107 --- /dev/null +++ b/src/video_core/renderer_vulkan/pipeline_statistics.h @@ -0,0 +1,40 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Device; + +class PipelineStatistics { +public: + explicit PipelineStatistics(const Device& device_); + + void Collect(VkPipeline pipeline); + + void Report() const; + +private: + struct Stats { + u64 code_size{}; + u64 register_count{}; + u64 sgpr_count{}; + u64 vgpr_count{}; + u64 branches_count{}; + u64 basic_block_count{}; + }; + + const Device& device; + mutable std::mutex mutex; + std::vector collected_stats; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 70b84c7a6..44faf626a 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -8,6 +8,7 @@ #include #include "video_core/renderer_vulkan/pipeline_helper.h" +#include "video_core/renderer_vulkan/pipeline_statistics.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" #include "video_core/renderer_vulkan/vk_compute_pipeline.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" @@ -26,6 +27,7 @@ using Tegra::Texture::TexturePair; ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* thread_worker, + PipelineStatistics* pipeline_statistics, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, vk::ShaderModule spv_module_) : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, @@ -36,7 +38,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(), uniform_buffer_sizes.begin()); - auto func{[this, &descriptor_pool, shader_notify] { + auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] { DescriptorLayoutBuilder builder{device}; builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT); @@ -50,10 +52,14 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript .pNext = nullptr, .requiredSubgroupSize = GuestWarpSize, }; + VkPipelineCreateFlags flags{}; + if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; + } pipeline = device.GetLogical().CreateComputePipeline({ .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, .pNext = nullptr, - .flags = 0, + .flags = flags, .stage{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, @@ -67,6 +73,9 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript .basePipelineHandle = 0, .basePipelineIndex = 0, }); + if (pipeline_statistics) { + pipeline_statistics->Collect(*pipeline); + } std::lock_guard lock{build_mutex}; is_built = true; build_condvar.notify_one(); diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index 52fec04d3..8c4b0a301 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -25,6 +25,7 @@ class ShaderNotify; namespace Vulkan { class Device; +class PipelineStatistics; class VKScheduler; class ComputePipeline { @@ -32,6 +33,7 @@ public: explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* thread_worker, + PipelineStatistics* pipeline_statistics, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info, vk::ShaderModule spv_module); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 18482e1d0..7c0f91007 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -11,6 +11,7 @@ #include "common/bit_field.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/pipeline_helper.h" +#include "video_core/renderer_vulkan/pipeline_statistics.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_render_pass_cache.h" @@ -217,8 +218,8 @@ GraphicsPipeline::GraphicsPipeline( VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, - RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, - std::array stages, + PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key_, std::array stages, const std::array& infos) : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_}, @@ -235,7 +236,7 @@ GraphicsPipeline::GraphicsPipeline( enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask; std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin()); } - auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool] { + auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] { DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)}; uses_push_descriptor = builder.CanUsePushDescriptor(); descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); @@ -250,6 +251,9 @@ GraphicsPipeline::GraphicsPipeline( const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))}; Validate(); MakePipeline(render_pass); + if (pipeline_statistics) { + pipeline_statistics->Collect(*pipeline); + } std::lock_guard lock{build_mutex}; is_built = true; @@ -782,10 +786,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { } */ } + VkPipelineCreateFlags flags{}; + if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; + } pipeline = device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = nullptr, - .flags = 0, + .flags = flags, .stageCount = static_cast(shader_stages.size()), .pStages = shader_stages.data(), .pVertexInputState = &vertex_input_ci, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 2bd48d697..1c780e944 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -60,6 +60,7 @@ struct hash { namespace Vulkan { class Device; +class PipelineStatistics; class RenderPassCache; class VKScheduler; class VKUpdateDescriptorQueue; @@ -73,8 +74,9 @@ public: VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, const Device& device, DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue, - Common::ThreadWorker* worker_thread, RenderPassCache& render_pass_cache, - const GraphicsPipelineCacheKey& key, std::array stages, + Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, + RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, + std::array stages, const std::array& infos); GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 57b163247..a37ca1fdf 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -29,6 +29,7 @@ #include "video_core/renderer_vulkan/fixed_pipeline_state.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/pipeline_helper.h" +#include "video_core/renderer_vulkan/pipeline_statistics.h" #include "video_core/renderer_vulkan/vk_compute_pipeline.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h" @@ -389,15 +390,19 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading size_t total{}; size_t built{}; bool has_loaded{}; + std::unique_ptr statistics; } state; + if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + state.statistics = std::make_unique(device); + } const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { ComputePipelineCacheKey key; file.read(reinterpret_cast(&key), sizeof(key)); workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { ShaderPools pools; - auto pipeline{CreateComputePipeline(pools, key, env, false)}; + auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)}; std::lock_guard lock{state.mutex}; if (pipeline) { compute_cache.emplace(key, std::move(pipeline)); @@ -425,7 +430,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading for (auto& env : envs) { env_ptrs.push_back(&env); } - auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), false)}; + auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), + state.statistics.get(), false)}; std::lock_guard lock{state.mutex}; graphics_cache.emplace(key, std::move(pipeline)); @@ -445,6 +451,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading lock.unlock(); workers.WaitForRequests(); + + if (state.statistics) { + state.statistics->Report(); + } } GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { @@ -486,7 +496,8 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const std::unique_ptr PipelineCache::CreateGraphicsPipeline( ShaderPools& pools, const GraphicsPipelineCacheKey& key, - std::span envs, bool build_in_parallel) try { + std::span envs, PipelineStatistics* statistics, + bool build_in_parallel) try { LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); size_t env_index{0}; std::array programs; @@ -540,7 +551,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique( maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, - descriptor_pool, update_descriptor_queue, thread_worker, render_pass_cache, key, + descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, std::move(modules), infos); } catch (const Shader::Exception& exception) { @@ -553,7 +564,8 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline() { GetGraphicsEnvironments(environments, graphics_key.unique_hashes); main_pools.ReleaseContents(); - auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)}; + auto pipeline{ + CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)}; if (!pipeline || pipeline_cache_filename.empty()) { return pipeline; } @@ -578,7 +590,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( env.SetCachedSize(shader->size_bytes); main_pools.ReleaseContents(); - auto pipeline{CreateComputePipeline(main_pools, key, env, true)}; + auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)}; if (!pipeline || pipeline_cache_filename.empty()) { return pipeline; } @@ -591,7 +603,7 @@ std::unique_ptr PipelineCache::CreateComputePipeline( std::unique_ptr PipelineCache::CreateComputePipeline( ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, - bool build_in_parallel) try { + PipelineStatistics* statistics, bool build_in_parallel) try { LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; @@ -605,8 +617,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline( } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique(device, descriptor_pool, update_descriptor_queue, - thread_worker, &shader_notify, program.info, - std::move(spv_module)); + thread_worker, statistics, &shader_notify, + program.info, std::move(spv_module)); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index efe5a7ed8..4c135b5dd 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -80,8 +80,9 @@ struct hash { namespace Vulkan { class ComputePipeline; -class Device; class DescriptorPool; +class Device; +class PipelineStatistics; class RasterizerVulkan; class RenderPassCache; class VKScheduler; @@ -128,7 +129,8 @@ private: std::unique_ptr CreateGraphicsPipeline( ShaderPools& pools, const GraphicsPipelineCacheKey& key, - std::span envs, bool build_in_parallel); + std::span envs, PipelineStatistics* statistics, + bool build_in_parallel); std::unique_ptr CreateComputePipeline(const ComputePipelineCacheKey& key, const ShaderInfo* shader); @@ -136,6 +138,7 @@ private: std::unique_ptr CreateComputePipeline(ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, + PipelineStatistics* statistics, bool build_in_parallel); const Device& device; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 44afdc1cd..8e56a89e1 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -526,6 +526,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR SetNext(next, workgroup_layout); } + VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties; + if (khr_pipeline_executable_properties) { + LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times"); + executable_properties = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR, + .pNext = nullptr, + .pipelineExecutableInfo = VK_TRUE, + }; + SetNext(next, executable_properties); + } + if (!ext_depth_range_unrestricted) { LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); } @@ -824,6 +835,7 @@ std::vector Device::LoadExtensions(bool requires_surface) { bool has_khr_shader_float16_int8{}; bool has_khr_workgroup_memory_explicit_layout{}; + bool has_khr_pipeline_executable_properties{}; bool has_ext_subgroup_size_control{}; bool has_ext_transform_feedback{}; bool has_ext_custom_border_color{}; @@ -878,6 +890,10 @@ std::vector Device::LoadExtensions(bool requires_surface) { test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); } + if (Settings::values.renderer_shader_feedback) { + test(has_khr_pipeline_executable_properties, + VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false); + } } VkPhysicalDeviceFeatures2KHR features{}; features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; @@ -1033,6 +1049,19 @@ std::vector Device::LoadExtensions(bool requires_surface) { khr_workgroup_memory_explicit_layout = true; } } + if (has_khr_pipeline_executable_properties) { + VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties; + executable_properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR; + executable_properties.pNext = nullptr; + features.pNext = &executable_properties; + physical.GetFeatures2KHR(features); + + if (executable_properties.pipelineExecutableInfo) { + extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); + khr_pipeline_executable_properties = true; + } + } if (khr_push_descriptor) { VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor; push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index df394e384..c19f40746 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -214,6 +214,11 @@ public: return khr_push_descriptor; } + /// Returns true if VK_KHR_pipeline_executable_properties is enabled. + bool IsKhrPipelineEexecutablePropertiesEnabled() const { + return khr_pipeline_executable_properties; + } + /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { return khr_workgroup_memory_explicit_layout; @@ -378,6 +383,7 @@ private: bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4. bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. + bool khr_pipeline_executable_properties{}; ///< Support for executable properties. bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 70898004a..a9faa4807 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -181,6 +181,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkGetMemoryWin32HandleKHR); #endif X(vkGetQueryPoolResults); + X(vkGetPipelineExecutablePropertiesKHR); + X(vkGetPipelineExecutableStatisticsKHR); X(vkGetSemaphoreCounterValueKHR); X(vkMapMemory); X(vkQueueSubmit); @@ -809,6 +811,42 @@ VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noe return requirements; } +std::vector Device::GetPipelineExecutablePropertiesKHR( + VkPipeline pipeline) const { + const VkPipelineInfoKHR info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR, + .pNext = nullptr, + .pipeline = pipeline, + }; + u32 num{}; + dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, nullptr); + std::vector properties(num); + for (auto& property : properties) { + property.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR; + } + Check(dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, properties.data())); + return properties; +} + +std::vector Device::GetPipelineExecutableStatisticsKHR( + VkPipeline pipeline, u32 executable_index) const { + const VkPipelineExecutableInfoKHR executable_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR, + .pNext = nullptr, + .pipeline = pipeline, + .executableIndex = executable_index, + }; + u32 num{}; + dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num, nullptr); + std::vector statistics(num); + for (auto& statistic : statistics) { + statistic.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR; + } + Check(dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num, + statistics.data())); + return statistics; +} + void Device::UpdateDescriptorSets(Span writes, Span copies) const noexcept { dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data()); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index d76bb4324..b7ae01c6c 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -295,6 +295,8 @@ struct DeviceDispatch : InstanceDispatch { #ifdef _WIN32 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; #endif + PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{}; + PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{}; PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{}; PFN_vkMapMemory vkMapMemory{}; @@ -879,6 +881,12 @@ public: VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept; + std::vector GetPipelineExecutablePropertiesKHR( + VkPipeline pipeline) const; + + std::vector GetPipelineExecutableStatisticsKHR( + VkPipeline pipeline, u32 executable_index) const; + void UpdateDescriptorSets(Span writes, Span copies) const noexcept; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 72027e773..91b6217db 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -825,6 +825,7 @@ void Config::ReadRendererValues() { if (global) { ReadBasicSetting(Settings::values.fps_cap); ReadBasicSetting(Settings::values.renderer_debug); + ReadBasicSetting(Settings::values.renderer_shader_feedback); ReadBasicSetting(Settings::values.enable_nsight_aftermath); ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks); } @@ -1360,6 +1361,7 @@ void Config::SaveRendererValues() { if (global) { WriteBasicSetting(Settings::values.fps_cap); WriteBasicSetting(Settings::values.renderer_debug); + WriteBasicSetting(Settings::values.renderer_shader_feedback); WriteBasicSetting(Settings::values.enable_nsight_aftermath); WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks); } diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index f7e29dbd7..c0b240c1e 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -43,6 +43,8 @@ void ConfigureDebug::SetConfiguration() { ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); ui->enable_graphics_debugging->setEnabled(runtime_lock); ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); + ui->enable_shader_feedback->setEnabled(runtime_lock); + ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue()); ui->enable_cpu_debugging->setEnabled(runtime_lock); ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue()); ui->enable_nsight_aftermath->setEnabled(runtime_lock); @@ -65,6 +67,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); + Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); Settings::values.disable_shader_loop_safety_checks = diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index c8baf2921..3fe9ff7de 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -111,8 +111,8 @@ Graphics - - + + true @@ -125,7 +125,7 @@ - + When checked, it enables Nsight Aftermath crash dumps @@ -135,7 +135,7 @@ - + true @@ -148,7 +148,17 @@ - + + + + When checked, yuzu will log statistics about the compiled pipeline cache + + + Enable Shader Feedback + + + + When checked, it executes shaders without loop logic changes @@ -276,11 +286,14 @@ open_log_button homebrew_args_edit enable_graphics_debugging + enable_shader_feedback enable_nsight_aftermath disable_macro_jit disable_loop_safety_checks + fs_access_log reporting_services quest_flag + enable_cpu_debugging use_debug_asserts use_auto_stub diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 5af1ee6a8..064ecaafa 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -444,6 +444,7 @@ void Config::ReadValues() { // Renderer ReadSetting("Renderer", Settings::values.renderer_backend); ReadSetting("Renderer", Settings::values.renderer_debug); + ReadSetting("Renderer", Settings::values.renderer_shader_feedback); ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks); ReadSetting("Renderer", Settings::values.vulkan_device); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index e646e2d2f..0c0c128ae 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -221,6 +221,10 @@ backend = # 0 (default): Disabled, 1: Enabled debug = +# Enable shader feedback. +# 0 (default): Disabled, 1: Enabled +renderer_shader_feedback = + # Enable Nsight Aftermath crash dumps # 0 (default): Disabled, 1: Enabled nsight_aftermath =