From 5a5c11f984e0888bd9e082b26dcdb4d80e0f41c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 4 Nov 2026 19:11:04 +0100 Subject: [PATCH] Headless: Print the number of pipelines created Vulkan and Metal pipelines, and OpenGL programs, including those precompiled from the shader cache. Benchmark times include the compiles, so this shows when two builds differ in how many they need. Co-Authored-By: Claude Opus 5.5 --- GPU/GLES/ShaderManagerGLES.cpp | 2 ++ GPU/GPU.h | 2 ++ GPU/Metal/DrawEngineMetal.mm | 1 + GPU/Vulkan/PipelineManagerVulkan.cpp | 2 ++ headless/Headless.cpp | 2 ++ 5 files changed, 9 insertions(+) diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 5d1191af4f..9fba6cc6c1 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -782,6 +782,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, // Check if we can link these. ls = new LinkedShader(render_, VSID, vs, FSID, fs, vs->UseHWTransform()); + gpuStats.totals.numPipelinesCreated++; ls->use(VSID); const LinkedShaderCacheEntry entry(vs, fs, ls); linkedShaderCache_.push_back(entry); @@ -1032,6 +1033,7 @@ bool ShaderManagerGLES::LoadCache(File::IOFile &f) { fsCache_.Get(fsid, &fs); if (vs && fs) { LinkedShader *ls = new LinkedShader(render_, vsid, vs, fsid, fs, vs->UseHWTransform(), true); + gpuStats.totals.numPipelinesCreated++; LinkedShaderCacheEntry entry(vs, fs, ls); linkedShaderCache_.push_back(entry); } diff --git a/GPU/GPU.h b/GPU/GPU.h index 2f8307d702..1ad02603a0 100644 --- a/GPU/GPU.h +++ b/GPU/GPU.h @@ -148,6 +148,8 @@ struct GPUStatsPerFrame { struct GPUStatsTotals { // Flip count. Doesn't really belong here. int numFlips; + // Vulkan and Metal pipelines, and OpenGL programs, including those precompiled from the shader cache. + int numPipelinesCreated; }; // The ToString function lives in GPUCommonHW.cpp. diff --git a/GPU/Metal/DrawEngineMetal.mm b/GPU/Metal/DrawEngineMetal.mm index b93a0eb1a8..9098dd60e4 100644 --- a/GPU/Metal/DrawEngineMetal.mm +++ b/GPU/Metal/DrawEngineMetal.mm @@ -278,6 +278,7 @@ DrawEngineMetal::Pipeline *DrawEngineMetal::GetOrCreatePipeline(const MetalPipel pipeline->state = [MetalPipelineState new]; pipeline->format = format; pipelines_.Insert(key, pipeline); + gpuStats.totals.numPipelinesCreated++; if (vs->Failed() || fs->Failed()) { [pipeline->state setPipeline:nil]; return pipeline; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 9478b3c4e8..575ba1c33e 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -11,6 +11,7 @@ #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Common/ShaderId.h" #include "GPU/GPUDefinitions.h" +#include "GPU/GPU.h" #include "Common/GPU/thin3d.h" #include "Common/GPU/Vulkan/VulkanRenderManager.h" #include "Common/GPU/Vulkan/VulkanQueueRunner.h" @@ -396,6 +397,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * // If the above failed, we got a null pipeline. We still insert it to keep track. pipelines_.Insert(key, pipeline); + gpuStats.totals.numPipelinesCreated++; // Don't return placeholder null pipelines. if (pipeline && pipeline->pipeline) { diff --git a/headless/Headless.cpp b/headless/Headless.cpp index a4a1b86c8d..0d4db7238e 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -766,6 +766,8 @@ static bool RunAutoTest(GraphicsContext *graphicsContext, CoreParameter &corePar } fprintf(stderr, "Frame stats: %d flips in %d emulated seconds, wall %.1fs, per second:%s\n", total, (int)flipsPerSecond.size(), time_now_d() - startTime, perSecond.c_str()); } + // What shader compiles cost the run, which the times include. + fprintf(stderr, "Pipelines created: %d\n", gpuStats.totals.numPipelinesCreated); if (coreParameter.bootFromState) { // Counted from the state's load. More than a few, and the game has gone off its usual path. fprintf(stderr, "Failed disc reads: %d\n", pspFileSystem.FailedDiscReads());