diff options
19 files changed, 307 insertions, 24 deletions
diff --git a/src/common/settings.h b/src/common/settings.h index cfc1ab46f..a88ee045d 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -319,6 +319,7 @@ struct Values { // Renderer Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"}; BasicSetting<bool> renderer_debug{false, "debug"}; + BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; BasicSetting<bool> 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 <string_view> + +#include <fmt/format.h> + +#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<u64>(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<u64>(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<u32>(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<double>(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<double>(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 <mutex> +#include <vector> + +#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<Stats> 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 <boost/container/small_vector.hpp> #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<vk::ShaderModule, NUM_STAGES> stages, + PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, const std::array<const Shader::Info*, NUM_STAGES>& 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<u32>(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<Vulkan::GraphicsPipelineCacheKey> { 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<vk::ShaderModule, NUM_STAGES> stages, + Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, + RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, + std::array<vk::ShaderModule, NUM_STAGES> stages, const std::array<const Shader::Info*, NUM_STAGES>& 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<PipelineStatistics> statistics; } state; + if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + state.statistics = std::make_unique<PipelineStatistics>(device); + } const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { ComputePipelineCacheKey key; file.read(reinterpret_cast<char*>(&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<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( ShaderPools& pools, const GraphicsPipelineCacheKey& key, - std::span<Shader::Environment* const> envs, bool build_in_parallel) try { + std::span<Shader::Environment* const> envs, PipelineStatistics* statistics, + bool build_in_parallel) try { LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); size_t env_index{0}; std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; @@ -540,7 +551,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique<GraphicsPipeline>( 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<GraphicsPipeline> 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<ComputePipeline> 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<ComputePipeline> PipelineCache::CreateComputePipeline( std::unique_ptr<ComputePipeline> 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<ComputePipeline> PipelineCache::CreateComputePipeline( } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; return std::make_unique<ComputePipeline>(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<Vulkan::ComputePipelineCacheKey> { 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<GraphicsPipeline> CreateGraphicsPipeline( ShaderPools& pools, const GraphicsPipelineCacheKey& key, - std::span<Shader::Environment* const> envs, bool build_in_parallel); + std::span<Shader::Environment* const> envs, PipelineStatistics* statistics, + bool build_in_parallel); std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key, const ShaderInfo* shader); @@ -136,6 +138,7 @@ private: std::unique_ptr<ComputePipeline> 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<const char*> 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<const char*> 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<const char*> 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<VkPipelineExecutablePropertiesKHR> 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<VkPipelineExecutablePropertiesKHR> 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<VkPipelineExecutableStatisticKHR> 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<VkPipelineExecutableStatisticKHR> 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<VkWriteDescriptorSet> writes, Span<VkCopyDescriptorSet> 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<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR( + VkPipeline pipeline) const; + + std::vector<VkPipelineExecutableStatisticKHR> GetPipelineExecutableStatisticsKHR( + VkPipeline pipeline, u32 executable_index) const; + void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, Span<VkCopyDescriptorSet> copies) const noexcept; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index ecd5dfac1..f3b8787f5 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); } @@ -1363,6 +1364,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 @@ <property name="title"> <string>Graphics</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_6"> - <item> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> <widget class="QCheckBox" name="enable_graphics_debugging"> <property name="enabled"> <bool>true</bool> @@ -125,7 +125,7 @@ </property> </widget> </item> - <item> + <item row="2" column="0"> <widget class="QCheckBox" name="enable_nsight_aftermath"> <property name="toolTip"> <string>When checked, it enables Nsight Aftermath crash dumps</string> @@ -135,7 +135,7 @@ </property> </widget> </item> - <item> + <item row="0" column="1"> <widget class="QCheckBox" name="disable_macro_jit"> <property name="enabled"> <bool>true</bool> @@ -148,7 +148,17 @@ </property> </widget> </item> - <item> + <item row="1" column="0"> + <widget class="QCheckBox" name="enable_shader_feedback"> + <property name="toolTip"> + <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> + </property> + <property name="text"> + <string>Enable Shader Feedback</string> + </property> + </widget> + </item> + <item row="1" column="1"> <widget class="QCheckBox" name="disable_loop_safety_checks"> <property name="toolTip"> <string>When checked, it executes shaders without loop logic changes</string> @@ -276,11 +286,14 @@ <tabstop>open_log_button</tabstop> <tabstop>homebrew_args_edit</tabstop> <tabstop>enable_graphics_debugging</tabstop> + <tabstop>enable_shader_feedback</tabstop> <tabstop>enable_nsight_aftermath</tabstop> <tabstop>disable_macro_jit</tabstop> <tabstop>disable_loop_safety_checks</tabstop> + <tabstop>fs_access_log</tabstop> <tabstop>reporting_services</tabstop> <tabstop>quest_flag</tabstop> + <tabstop>enable_cpu_debugging</tabstop> <tabstop>use_debug_asserts</tabstop> <tabstop>use_auto_stub</tabstop> </tabstops> 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 = |