GS: Track shader compile count and duration for OSD

This commit is contained in:
SternXD
2026-05-15 08:59:41 -04:00
committed by lightningterror
parent 2f8c98dccf
commit 3aa0685786
9 changed files with 84 additions and 73 deletions
+55 -45
View File
@@ -3,87 +3,97 @@
#pragma once
#include "common/Pcsx2Types.h"
#include "common/Timer.h"
#include <atomic>
#include <chrono>
#include <cstdint>
namespace GSShaderCompileIndicator
{
inline constexpr std::uint64_t OSD_RECENT_COMPILE_NS = 500'000'000ULL;
inline constexpr u64 RECENT_COMPILE_HOLD_NS = 1'500'000'000ULL;
inline std::atomic<std::uint32_t> s_active_compilations{0};
inline std::atomic<std::uint64_t> s_last_compile_activity_ns{0};
inline std::atomic<u32> s_count{0};
inline std::atomic<u64> s_time_ns{0};
inline std::atomic<u64> s_last_time{0};
namespace detail
inline u64 GetRecentCompileHold()
{
inline std::uint64_t steady_now_ns()
static const u64 hold = static_cast<u64>(Common::Timer::ConvertNanosecondsToValue(static_cast<double>(RECENT_COMPILE_HOLD_NS)));
return hold;
}
inline void OnCompileDone(u64 duration_ns, u64 start_time)
{
const u64 now = Common::Timer::GetCurrentValue();
const u64 last = s_last_time.load(std::memory_order_relaxed);
if (last != 0 && start_time > last && (start_time - last) >= GetRecentCompileHold())
{
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count());
s_count.store(0, std::memory_order_relaxed);
s_time_ns.store(0, std::memory_order_relaxed);
}
} // namespace detail
inline void TouchCompileActivity()
{
s_last_compile_activity_ns.store(detail::steady_now_ns(), std::memory_order_relaxed);
s_count.fetch_add(1, std::memory_order_relaxed);
s_time_ns.fetch_add(duration_ns, std::memory_order_relaxed);
s_last_time.store(now, std::memory_order_relaxed);
}
inline void BeginCompilation()
inline u32 GetCount()
{
TouchCompileActivity();
s_active_compilations.fetch_add(1, std::memory_order_relaxed);
return s_count.load(std::memory_order_relaxed);
}
inline void EndCompilation()
inline u32 GetTimeMs()
{
s_active_compilations.fetch_sub(1, std::memory_order_relaxed);
TouchCompileActivity();
const u64 time_ns = s_time_ns.load(std::memory_order_relaxed);
const u32 ms = static_cast<u32>(time_ns / 1000000);
if (ms > 0)
return ms;
return GetCount() > 0 ? 1u : 0u;
}
inline std::uint32_t GetActiveCompilationCount()
inline bool IsVisible()
{
return s_active_compilations.load(std::memory_order_relaxed);
}
if (GetCount() == 0)
return false;
inline bool ShouldShowOnOSD(std::uint64_t hold_ns = OSD_RECENT_COMPILE_NS)
{
if (GetActiveCompilationCount() != 0)
return true;
const std::uint64_t last = s_last_compile_activity_ns.load(std::memory_order_relaxed);
const u64 last = s_last_time.load(std::memory_order_relaxed);
if (last == 0)
return false;
const std::uint64_t now = detail::steady_now_ns();
return now > last && (now - last) < hold_ns;
return (Common::Timer::GetCurrentValue() - last) < GetRecentCompileHold();
}
inline float GetPostCompileFadeAlpha(std::uint64_t hold_ns = OSD_RECENT_COMPILE_NS)
inline float GetFadeAlpha()
{
if (GetActiveCompilationCount() != 0)
return 1.0f;
const std::uint64_t last = s_last_compile_activity_ns.load(std::memory_order_relaxed);
const u64 last = s_last_time.load(std::memory_order_relaxed);
if (last == 0)
return 0.0f;
const std::uint64_t now = detail::steady_now_ns();
const u64 now = Common::Timer::GetCurrentValue();
if (now <= last)
return 1.0f;
const std::uint64_t elapsed = now - last;
if (elapsed >= hold_ns)
const u64 hold = GetRecentCompileHold();
const u64 elapsed = now - last;
if (elapsed >= hold)
return 0.0f;
return 1.0f - static_cast<float>(elapsed) / static_cast<float>(hold_ns);
return 1.0f - static_cast<float>(elapsed) / static_cast<float>(hold);
}
struct ScopedCompilation
struct CompileTimer
{
ScopedCompilation() { BeginCompilation(); }
~ScopedCompilation() { EndCompilation(); }
ScopedCompilation(const ScopedCompilation&) = delete;
ScopedCompilation& operator=(const ScopedCompilation&) = delete;
Common::Timer timer;
CompileTimer() = default;
~CompileTimer()
{
OnCompileDone(static_cast<u64>(timer.GetTimeNanoseconds()), timer.GetStartValue());
}
CompileTimer(const CompileTimer&) = delete;
CompileTimer& operator=(const CompileTimer&) = delete;
};
} // namespace GSShaderCompileIndicator
+1 -1
View File
@@ -488,7 +488,7 @@ wil::com_ptr_nothrow<ID3DBlob> D3D::CompileShader(D3D::ShaderType type, D3D::Sha
const std::string_view code, const D3D_SHADER_MACRO* macros /* = nullptr */,
const char* entry_point /* = "main" */)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
const char* target;
switch (shader_model)
+2 -2
View File
@@ -541,7 +541,7 @@ D3D12ShaderCache::ComPtr<ID3DBlob> D3D12ShaderCache::CompileAndAddShaderBlob(
D3D12ShaderCache::ComPtr<ID3D12PipelineState> D3D12ShaderCache::CompileAndAddPipeline(
ID3D12Device* device, const CacheIndexKey& key, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
ComPtr<ID3D12PipelineState> pso;
HRESULT hr = device->CreateGraphicsPipelineState(&gpdesc, IID_PPV_ARGS(pso.put()));
@@ -558,7 +558,7 @@ D3D12ShaderCache::ComPtr<ID3D12PipelineState> D3D12ShaderCache::CompileAndAddPip
D3D12ShaderCache::ComPtr<ID3D12PipelineState> D3D12ShaderCache::CompileAndAddPipeline(
ID3D12Device* device, const CacheIndexKey& key, const D3D12_COMPUTE_PIPELINE_STATE_DESC& gpdesc)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
ComPtr<ID3D12PipelineState> pso;
HRESULT hr = device->CreateComputePipelineState(&gpdesc, IID_PPV_ARGS(pso.put()));
+2 -2
View File
@@ -713,7 +713,7 @@ MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
MRCOwned<id<MTLRenderPipelineState>> GSDeviceMTL::MakePipeline(MTLRenderPipelineDescriptor* desc, id<MTLFunction> vertex, id<MTLFunction> fragment, NSString* name)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
[desc setLabel:name];
[desc setVertexFunction:vertex];
[desc setFragmentFunction:fragment];
@@ -730,7 +730,7 @@ MRCOwned<id<MTLRenderPipelineState>> GSDeviceMTL::MakePipeline(MTLRenderPipeline
MRCOwned<id<MTLComputePipelineState>> GSDeviceMTL::MakeComputePipeline(id<MTLFunction> compute, NSString* name)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
MRCOwned<MTLComputePipelineDescriptor*> desc = MRCTransfer([MTLComputePipelineDescriptor new]);
[desc setLabel:name];
[desc setComputeFunction:compute];
+2 -10
View File
@@ -3,7 +3,6 @@
#include "GSMTLDeviceInfo.h"
#include "GS/GS.h"
#include "GS/GSShaderCompileIndicator.h"
#include "common/Console.h"
#include "common/Path.h"
@@ -57,10 +56,7 @@ static bool detectPrimIDSupport(id<MTLDevice> dev, id<MTLLibrary> lib)
[desc setFragmentFunction:MRCTransfer([lib newFunctionWithName:@"primid_test"])];
[[desc colorAttachments][0] setPixelFormat:MTLPixelFormatR8Uint];
NSError* err;
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
[[dev newRenderPipelineStateWithDescriptor:desc error:&err] release];
}
[[dev newRenderPipelineStateWithDescriptor:desc error:&err] release];
return !err;
}
@@ -92,11 +88,7 @@ static DetectionResult detectIntelGPU(id<MTLDevice> dev, id<MTLLibrary> lib)
[pdesc setVertexFunction:MRCTransfer([lib newFunctionWithName:@"fs_triangle"])];
[pdesc setFragmentFunction:MRCTransfer([lib newFunctionWithName:@"fbfetch_test"])];
[[pdesc colorAttachments][0] setPixelFormat:MTLPixelFormatRGBA8Unorm];
MRCOwned<id<MTLRenderPipelineState>> pipe;
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
pipe = MRCTransfer([dev newRenderPipelineStateWithDescriptor:pdesc error:nil]);
}
auto pipe = MRCTransfer([dev newRenderPipelineStateWithDescriptor:pdesc error:nil]);
if (!pipe)
return DetectionResult::HaswellOrNotIntel;
auto buf = MRCTransfer([dev newBufferWithLength:4 options:MTLResourceStorageModeShared]);
+2 -2
View File
@@ -367,7 +367,7 @@ bool GLShaderCache::WriteToBlobFile(const CacheIndexKey& key, const std::vector<
std::optional<GLProgram> GLShaderCache::CompileProgram(const std::string_view vertex_shader,
const std::string_view fragment_shader, const PreLinkCallback& callback, bool set_retrievable)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
GLProgram prog;
if (!prog.Compile(vertex_shader, fragment_shader))
@@ -388,7 +388,7 @@ std::optional<GLProgram> GLShaderCache::CompileProgram(const std::string_view ve
std::optional<GLProgram> GLShaderCache::CompileComputeProgram(
const std::string_view glsl, const PreLinkCallback& callback, bool set_retrievable)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
GLProgram prog;
if (!prog.CompileCompute(glsl))
+2 -2
View File
@@ -280,7 +280,7 @@ void Vulkan::GraphicsPipelineBuilder::Clear()
VkPipeline Vulkan::GraphicsPipelineBuilder::Create(
VkDevice device, VkPipelineCache pipeline_cache, bool clear /* = true */)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
VkPipeline pipeline;
VkResult res = vkCreateGraphicsPipelines(device, pipeline_cache, 1, &m_ci, nullptr, &pipeline);
@@ -591,7 +591,7 @@ void Vulkan::ComputePipelineBuilder::Clear()
VkPipeline Vulkan::ComputePipelineBuilder::Create(
VkDevice device, VkPipelineCache pipeline_cache /*= VK_NULL_HANDLE*/, bool clear /*= true*/)
{
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
VkPipeline pipeline;
VkResult res = vkCreateComputePipelines(device, pipeline_cache, 1, &m_ci, nullptr, &pipeline);
+1 -1
View File
@@ -231,7 +231,7 @@ std::optional<VKShaderCache::SPIRVCodeVector> VKShaderCache::CompileShaderToSPV(
if (!dyn_shaderc::Open())
return ret;
const GSShaderCompileIndicator::ScopedCompilation compiling;
const GSShaderCompileIndicator::CompileTimer compile_timer;
shaderc_compile_options_t options = dyn_shaderc::shaderc_compile_options_initialize();
pxAssertRel(options, "shaderc_compile_options_initialize() failed");
+17 -8
View File
@@ -785,7 +785,7 @@ __ri void ImGuiManager::DrawShaderCompileIndicator(float scale, float margin, fl
static bool s_indicator_was_visible = false;
static double s_indicator_fade_in_start = 0.0;
if (!GSConfig.OsdShowGPU || !GSShaderCompileIndicator::ShouldShowOnOSD())
if (!GSConfig.OsdShowGPU || !GSShaderCompileIndicator::IsVisible())
{
s_indicator_was_visible = false;
return;
@@ -799,14 +799,23 @@ __ri void ImGuiManager::DrawShaderCompileIndicator(float scale, float margin, fl
constexpr double fade_in_seconds = 0.12;
const float fade_in_alpha = static_cast<float>(
std::min(1.0, (imgui_time - s_indicator_fade_in_start) / fade_in_seconds));
const float fade_out_alpha = GSShaderCompileIndicator::GetPostCompileFadeAlpha();
const float indicator_alpha = std::clamp(fade_in_alpha * fade_out_alpha, 0.0f, 1.0f);
const ImU32 text_col = IM_COL32(255, 255, 255, static_cast<int>(std::lround(255.0f * indicator_alpha)));
const ImU32 shadow_col = IM_COL32(0, 0, 0, static_cast<int>(std::lround(100.0f * indicator_alpha)));
const ImU32 spinner_track_col =
IM_COL32(255, 255, 255, static_cast<int>(std::lround(45.0f * indicator_alpha)));
const float fade_out_alpha = GSShaderCompileIndicator::GetFadeAlpha();
const float alpha = std::clamp(fade_in_alpha * fade_out_alpha, 0.0f, 1.0f);
const ImU32 text_col = IM_COL32(255, 255, 255, static_cast<int>(std::lround(255.0f * alpha)));
const ImU32 shadow_col = IM_COL32(0, 0, 0, static_cast<int>(std::lround(100.0f * alpha)));
const ImU32 spinner_track_col = IM_COL32(255, 255, 255, static_cast<int>(std::lround(45.0f * alpha)));
static constexpr const char* COMPILED_ONE =
TRANSLATE_NOOP("ImGuiOverlays", "Compiled {0} shader in {1}ms");
static constexpr const char* COMPILED_MANY =
TRANSLATE_NOOP("ImGuiOverlays", "Compiled {0} shaders in {1}ms");
const u32 count = GSShaderCompileIndicator::GetCount();
const u32 time_ms = GSShaderCompileIndicator::GetTimeMs();
const std::string label = (count == 1) ?
fmt::format(TRANSLATE_FS("ImGuiOverlays", COMPILED_ONE), count, time_ms) :
fmt::format(TRANSLATE_FS("ImGuiOverlays", COMPILED_MANY), count, time_ms);
const std::string label = TRANSLATE_STR("ImGuiOverlays", "Compiling Shaders");
ImFont* const font = ImGuiManager::GetOSDFont();
const float font_size = ImGuiManager::GetFontSizeStandard();
const float baseline_y =