summaryrefslogtreecommitdiff
path: root/src/video_core/renderer_vulkan
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-07-27 19:15:32 -0300
committerGravatar ReinUsesLisp2021-07-27 21:29:24 -0300
commit3b006f4fe28006d320c60fd2b4393fd3f27eacd7 (patch)
treee8704a3796e766a764e2643a2621451a8fe4ea49 /src/video_core/renderer_vulkan
parentMerge pull request #6748 from lioncash/engine-init (diff)
downloadyuzu-3b006f4fe28006d320c60fd2b4393fd3f27eacd7.tar.gz
yuzu-3b006f4fe28006d320c60fd2b4393fd3f27eacd7.tar.xz
yuzu-3b006f4fe28006d320c60fd2b4393fd3f27eacd7.zip
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.
Diffstat (limited to 'src/video_core/renderer_vulkan')
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.cpp100
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.h40
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp16
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h7
8 files changed, 195 insertions, 19 deletions
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include <fmt/format.h>
8
9#include "common/common_types.h"
10#include "common/logging/log.h"
11#include "video_core/renderer_vulkan/pipeline_statistics.h"
12#include "video_core/vulkan_common/vulkan_device.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h"
14
15namespace Vulkan {
16
17using namespace std::string_view_literals;
18
19static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) {
20 switch (statistic.format) {
21 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR:
22 return static_cast<u64>(statistic.value.i64);
23 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR:
24 return statistic.value.u64;
25 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR:
26 return static_cast<u64>(statistic.value.f64);
27 default:
28 return 0;
29 }
30}
31
32PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {}
33
34void PipelineStatistics::Collect(VkPipeline pipeline) {
35 const auto& dev{device.GetLogical()};
36 const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)};
37 const u32 num_executables{static_cast<u32>(properties.size())};
38 for (u32 executable = 0; executable < num_executables; ++executable) {
39 const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)};
40 if (statistics.empty()) {
41 continue;
42 }
43 Stats stage_stats;
44 for (const auto& statistic : statistics) {
45 const char* const name{statistic.name};
46 if (name == "Binary Size"sv || name == "Code size"sv || name == "Instruction Count"sv) {
47 stage_stats.code_size = GetUint64(statistic);
48 } else if (name == "Register Count"sv) {
49 stage_stats.register_count = GetUint64(statistic);
50 } else if (name == "SGPRs"sv || name == "numUsedSgprs"sv) {
51 stage_stats.sgpr_count = GetUint64(statistic);
52 } else if (name == "VGPRs"sv || name == "numUsedVgprs"sv) {
53 stage_stats.vgpr_count = GetUint64(statistic);
54 } else if (name == "Branches"sv) {
55 stage_stats.branches_count = GetUint64(statistic);
56 } else if (name == "Basic Block Count"sv) {
57 stage_stats.basic_block_count = GetUint64(statistic);
58 }
59 }
60 std::lock_guard lock{mutex};
61 collected_stats.push_back(stage_stats);
62 }
63}
64
65void PipelineStatistics::Report() const {
66 double num{};
67 Stats total;
68 {
69 std::lock_guard lock{mutex};
70 for (const Stats& stats : collected_stats) {
71 total.code_size += stats.code_size;
72 total.register_count += stats.register_count;
73 total.sgpr_count += stats.sgpr_count;
74 total.vgpr_count += stats.vgpr_count;
75 total.branches_count += stats.branches_count;
76 total.basic_block_count += stats.basic_block_count;
77 }
78 num = static_cast<double>(collected_stats.size());
79 }
80 std::string report;
81 const auto add = [&](const char* fmt, u64 value) {
82 if (value > 0) {
83 report += fmt::format(fmt::runtime(fmt), static_cast<double>(value) / num);
84 }
85 };
86 add("Code size: {:9.03f}\n", total.code_size);
87 add("Register count: {:9.03f}\n", total.register_count);
88 add("SGPRs: {:9.03f}\n", total.sgpr_count);
89 add("VGPRs: {:9.03f}\n", total.vgpr_count);
90 add("Branches count: {:9.03f}\n", total.branches_count);
91 add("Basic blocks: {:9.03f}\n", total.basic_block_count);
92
93 LOG_INFO(Render_Vulkan,
94 "\nAverage pipeline statistics\n"
95 "==========================================\n"
96 "{}\n",
97 report);
98}
99
100} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8#include <vector>
9
10#include "common/common_types.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h"
12
13namespace Vulkan {
14
15class Device;
16
17class PipelineStatistics {
18public:
19 explicit PipelineStatistics(const Device& device_);
20
21 void Collect(VkPipeline pipeline);
22
23 void Report() const;
24
25private:
26 struct Stats {
27 u64 code_size{};
28 u64 register_count{};
29 u64 sgpr_count{};
30 u64 vgpr_count{};
31 u64 branches_count{};
32 u64 basic_block_count{};
33 };
34
35 const Device& device;
36 mutable std::mutex mutex;
37 std::vector<Stats> collected_stats;
38};
39
40} // 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 @@
8#include <boost/container/small_vector.hpp> 8#include <boost/container/small_vector.hpp>
9 9
10#include "video_core/renderer_vulkan/pipeline_helper.h" 10#include "video_core/renderer_vulkan/pipeline_helper.h"
11#include "video_core/renderer_vulkan/pipeline_statistics.h"
11#include "video_core/renderer_vulkan/vk_buffer_cache.h" 12#include "video_core/renderer_vulkan/vk_buffer_cache.h"
12#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 13#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
13#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 14#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -26,6 +27,7 @@ using Tegra::Texture::TexturePair;
26ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, 27ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool,
27 VKUpdateDescriptorQueue& update_descriptor_queue_, 28 VKUpdateDescriptorQueue& update_descriptor_queue_,
28 Common::ThreadWorker* thread_worker, 29 Common::ThreadWorker* thread_worker,
30 PipelineStatistics* pipeline_statistics,
29 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, 31 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
30 vk::ShaderModule spv_module_) 32 vk::ShaderModule spv_module_)
31 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, 33 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_},
@@ -36,7 +38,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
36 std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(), 38 std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
37 uniform_buffer_sizes.begin()); 39 uniform_buffer_sizes.begin());
38 40
39 auto func{[this, &descriptor_pool, shader_notify] { 41 auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] {
40 DescriptorLayoutBuilder builder{device}; 42 DescriptorLayoutBuilder builder{device};
41 builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT); 43 builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
42 44
@@ -50,10 +52,14 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
50 .pNext = nullptr, 52 .pNext = nullptr,
51 .requiredSubgroupSize = GuestWarpSize, 53 .requiredSubgroupSize = GuestWarpSize,
52 }; 54 };
55 VkPipelineCreateFlags flags{};
56 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
58 }
53 pipeline = device.GetLogical().CreateComputePipeline({ 59 pipeline = device.GetLogical().CreateComputePipeline({
54 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 60 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
55 .pNext = nullptr, 61 .pNext = nullptr,
56 .flags = 0, 62 .flags = flags,
57 .stage{ 63 .stage{
58 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 64 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
59 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, 65 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
@@ -67,6 +73,9 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
67 .basePipelineHandle = 0, 73 .basePipelineHandle = 0,
68 .basePipelineIndex = 0, 74 .basePipelineIndex = 0,
69 }); 75 });
76 if (pipeline_statistics) {
77 pipeline_statistics->Collect(*pipeline);
78 }
70 std::lock_guard lock{build_mutex}; 79 std::lock_guard lock{build_mutex};
71 is_built = true; 80 is_built = true;
72 build_condvar.notify_one(); 81 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;
25namespace Vulkan { 25namespace Vulkan {
26 26
27class Device; 27class Device;
28class PipelineStatistics;
28class VKScheduler; 29class VKScheduler;
29 30
30class ComputePipeline { 31class ComputePipeline {
@@ -32,6 +33,7 @@ public:
32 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, 33 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool,
33 VKUpdateDescriptorQueue& update_descriptor_queue, 34 VKUpdateDescriptorQueue& update_descriptor_queue,
34 Common::ThreadWorker* thread_worker, 35 Common::ThreadWorker* thread_worker,
36 PipelineStatistics* pipeline_statistics,
35 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info, 37 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info,
36 vk::ShaderModule spv_module); 38 vk::ShaderModule spv_module);
37 39
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 @@
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h" 12#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/pipeline_helper.h" 13#include "video_core/renderer_vulkan/pipeline_helper.h"
14#include "video_core/renderer_vulkan/pipeline_statistics.h"
14#include "video_core/renderer_vulkan/vk_buffer_cache.h" 15#include "video_core/renderer_vulkan/vk_buffer_cache.h"
15#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 16#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
16#include "video_core/renderer_vulkan/vk_render_pass_cache.h" 17#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
@@ -217,8 +218,8 @@ GraphicsPipeline::GraphicsPipeline(
217 VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, 218 VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
218 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, 219 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool,
219 VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, 220 VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
220 RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, 221 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
221 std::array<vk::ShaderModule, NUM_STAGES> stages, 222 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
222 const std::array<const Shader::Info*, NUM_STAGES>& infos) 223 const std::array<const Shader::Info*, NUM_STAGES>& infos)
223 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_}, 224 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_},
224 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_}, 225 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_},
@@ -235,7 +236,7 @@ GraphicsPipeline::GraphicsPipeline(
235 enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask; 236 enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
236 std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin()); 237 std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
237 } 238 }
238 auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool] { 239 auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
239 DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)}; 240 DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
240 uses_push_descriptor = builder.CanUsePushDescriptor(); 241 uses_push_descriptor = builder.CanUsePushDescriptor();
241 descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); 242 descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor);
@@ -250,6 +251,9 @@ GraphicsPipeline::GraphicsPipeline(
250 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))}; 251 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
251 Validate(); 252 Validate();
252 MakePipeline(render_pass); 253 MakePipeline(render_pass);
254 if (pipeline_statistics) {
255 pipeline_statistics->Collect(*pipeline);
256 }
253 257
254 std::lock_guard lock{build_mutex}; 258 std::lock_guard lock{build_mutex};
255 is_built = true; 259 is_built = true;
@@ -782,10 +786,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
782 } 786 }
783 */ 787 */
784 } 788 }
789 VkPipelineCreateFlags flags{};
790 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
791 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
792 }
785 pipeline = device.GetLogical().CreateGraphicsPipeline({ 793 pipeline = device.GetLogical().CreateGraphicsPipeline({
786 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 794 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
787 .pNext = nullptr, 795 .pNext = nullptr,
788 .flags = 0, 796 .flags = flags,
789 .stageCount = static_cast<u32>(shader_stages.size()), 797 .stageCount = static_cast<u32>(shader_stages.size()),
790 .pStages = shader_stages.data(), 798 .pStages = shader_stages.data(),
791 .pVertexInputState = &vertex_input_ci, 799 .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> {
60namespace Vulkan { 60namespace Vulkan {
61 61
62class Device; 62class Device;
63class PipelineStatistics;
63class RenderPassCache; 64class RenderPassCache;
64class VKScheduler; 65class VKScheduler;
65class VKUpdateDescriptorQueue; 66class VKUpdateDescriptorQueue;
@@ -73,8 +74,9 @@ public:
73 VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, 74 VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
74 VideoCore::ShaderNotify* shader_notify, const Device& device, 75 VideoCore::ShaderNotify* shader_notify, const Device& device,
75 DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue, 76 DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue,
76 Common::ThreadWorker* worker_thread, RenderPassCache& render_pass_cache, 77 Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics,
77 const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages, 78 RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key,
79 std::array<vk::ShaderModule, NUM_STAGES> stages,
78 const std::array<const Shader::Info*, NUM_STAGES>& infos); 80 const std::array<const Shader::Info*, NUM_STAGES>& infos);
79 81
80 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; 82 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 @@
29#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 29#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
30#include "video_core/renderer_vulkan/maxwell_to_vk.h" 30#include "video_core/renderer_vulkan/maxwell_to_vk.h"
31#include "video_core/renderer_vulkan/pipeline_helper.h" 31#include "video_core/renderer_vulkan/pipeline_helper.h"
32#include "video_core/renderer_vulkan/pipeline_statistics.h"
32#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 33#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
33#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 34#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
34#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 35#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
@@ -389,15 +390,19 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
389 size_t total{}; 390 size_t total{};
390 size_t built{}; 391 size_t built{};
391 bool has_loaded{}; 392 bool has_loaded{};
393 std::unique_ptr<PipelineStatistics> statistics;
392 } state; 394 } state;
393 395
396 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
397 state.statistics = std::make_unique<PipelineStatistics>(device);
398 }
394 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { 399 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
395 ComputePipelineCacheKey key; 400 ComputePipelineCacheKey key;
396 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 401 file.read(reinterpret_cast<char*>(&key), sizeof(key));
397 402
398 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { 403 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
399 ShaderPools pools; 404 ShaderPools pools;
400 auto pipeline{CreateComputePipeline(pools, key, env, false)}; 405 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
401 std::lock_guard lock{state.mutex}; 406 std::lock_guard lock{state.mutex};
402 if (pipeline) { 407 if (pipeline) {
403 compute_cache.emplace(key, std::move(pipeline)); 408 compute_cache.emplace(key, std::move(pipeline));
@@ -425,7 +430,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
425 for (auto& env : envs) { 430 for (auto& env : envs) {
426 env_ptrs.push_back(&env); 431 env_ptrs.push_back(&env);
427 } 432 }
428 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), false)}; 433 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
434 state.statistics.get(), false)};
429 435
430 std::lock_guard lock{state.mutex}; 436 std::lock_guard lock{state.mutex};
431 graphics_cache.emplace(key, std::move(pipeline)); 437 graphics_cache.emplace(key, std::move(pipeline));
@@ -445,6 +451,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
445 lock.unlock(); 451 lock.unlock();
446 452
447 workers.WaitForRequests(); 453 workers.WaitForRequests();
454
455 if (state.statistics) {
456 state.statistics->Report();
457 }
448} 458}
449 459
450GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { 460GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
@@ -486,7 +496,8 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
486 496
487std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( 497std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
488 ShaderPools& pools, const GraphicsPipelineCacheKey& key, 498 ShaderPools& pools, const GraphicsPipelineCacheKey& key,
489 std::span<Shader::Environment* const> envs, bool build_in_parallel) try { 499 std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
500 bool build_in_parallel) try {
490 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 501 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
491 size_t env_index{0}; 502 size_t env_index{0};
492 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 503 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
@@ -540,7 +551,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
540 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 551 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
541 return std::make_unique<GraphicsPipeline>( 552 return std::make_unique<GraphicsPipeline>(
542 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, 553 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device,
543 descriptor_pool, update_descriptor_queue, thread_worker, render_pass_cache, key, 554 descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
544 std::move(modules), infos); 555 std::move(modules), infos);
545 556
546} catch (const Shader::Exception& exception) { 557} catch (const Shader::Exception& exception) {
@@ -553,7 +564,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
553 GetGraphicsEnvironments(environments, graphics_key.unique_hashes); 564 GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
554 565
555 main_pools.ReleaseContents(); 566 main_pools.ReleaseContents();
556 auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)}; 567 auto pipeline{
568 CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
557 if (!pipeline || pipeline_cache_filename.empty()) { 569 if (!pipeline || pipeline_cache_filename.empty()) {
558 return pipeline; 570 return pipeline;
559 } 571 }
@@ -578,7 +590,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
578 env.SetCachedSize(shader->size_bytes); 590 env.SetCachedSize(shader->size_bytes);
579 591
580 main_pools.ReleaseContents(); 592 main_pools.ReleaseContents();
581 auto pipeline{CreateComputePipeline(main_pools, key, env, true)}; 593 auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
582 if (!pipeline || pipeline_cache_filename.empty()) { 594 if (!pipeline || pipeline_cache_filename.empty()) {
583 return pipeline; 595 return pipeline;
584 } 596 }
@@ -591,7 +603,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
591 603
592std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( 604std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
593 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, 605 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
594 bool build_in_parallel) try { 606 PipelineStatistics* statistics, bool build_in_parallel) try {
595 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 607 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
596 608
597 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 609 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -605,8 +617,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
605 } 617 }
606 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 618 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
607 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, 619 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
608 thread_worker, &shader_notify, program.info, 620 thread_worker, statistics, &shader_notify,
609 std::move(spv_module)); 621 program.info, std::move(spv_module));
610 622
611} catch (const Shader::Exception& exception) { 623} catch (const Shader::Exception& exception) {
612 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 624 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> {
80namespace Vulkan { 80namespace Vulkan {
81 81
82class ComputePipeline; 82class ComputePipeline;
83class Device;
84class DescriptorPool; 83class DescriptorPool;
84class Device;
85class PipelineStatistics;
85class RasterizerVulkan; 86class RasterizerVulkan;
86class RenderPassCache; 87class RenderPassCache;
87class VKScheduler; 88class VKScheduler;
@@ -128,7 +129,8 @@ private:
128 129
129 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline( 130 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
130 ShaderPools& pools, const GraphicsPipelineCacheKey& key, 131 ShaderPools& pools, const GraphicsPipelineCacheKey& key,
131 std::span<Shader::Environment* const> envs, bool build_in_parallel); 132 std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
133 bool build_in_parallel);
132 134
133 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key, 135 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
134 const ShaderInfo* shader); 136 const ShaderInfo* shader);
@@ -136,6 +138,7 @@ private:
136 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools, 138 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
137 const ComputePipelineCacheKey& key, 139 const ComputePipelineCacheKey& key,
138 Shader::Environment& env, 140 Shader::Environment& env,
141 PipelineStatistics* statistics,
139 bool build_in_parallel); 142 bool build_in_parallel);
140 143
141 const Device& device; 144 const Device& device;