Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions src/Features/RemoteControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Features/RenderDoc.h"
#include "Features/ScreenshotFeature.h"
#include "Globals.h"
#include "ShaderCompileStatus.h"
#include "State.h"

#include <imgui.h>
Expand Down Expand Up @@ -449,6 +450,34 @@ static mcp::json EngineStateBlob()
});
}

// Helper used by inspect(kind="shadercache"): runtime shader (re)compile
// status, built entirely from existing thread-safe ShaderCache accessors (no
// added state). Hot-reloading an .hlsl clears its variants and requeues them,
// so completedTasks advances once the recompile lands — poll it against a
// pre-deploy snapshot to know the new shader is live. A rising failedTasks /
// currentFailedCount means a (re)compile failed (otherwise invisible).
static mcp::json ShaderCacheBlob()
{
const uint frames = globals::state ? globals::state->frameCountAtomic.load(std::memory_order_relaxed) : 0u;
const SIE::ShaderCompileStatus s = SIE::GetShaderCompileStatus();
if (!s.valid) {
return mcp::json({
{ "plugin", "CommunityShaders" },
{ "frame_count", frames },
{ "error", "shaderCache unavailable" },
});
}
return mcp::json({
{ "plugin", "CommunityShaders" },
{ "frame_count", frames },
{ "compiling", s.compiling },
{ "completedTasks", s.completedTasks },
{ "totalTasks", s.totalTasks },
{ "failedTasks", s.failedTasks },
{ "currentFailedCount", s.currentFailedCount },
});
}

// Helper used by feature(action="list") to build one entry per feature.
static mcp::json FeatureEntry(Feature* f)
{
Expand Down Expand Up @@ -510,11 +539,17 @@ void RemoteControl::RegisterInspectTool()
" state — { plugin, frame_count, vr }. Frame counter "
"monotonically increases each render tick; use as a "
"ground truth for verifying that deferred operations "
"(see `console`) have had time to run.\n\n"
"(see `console`) have had time to run.\n"
" shadercache — { plugin, compiling, completedTasks, "
"totalTasks, failedTasks, currentFailedCount, "
"frame_count }. Poll completedTasks against a "
"pre-deploy snapshot to confirm a hot-reloaded "
"shader finished recompiling; a rising failedTasks "
"/ currentFailedCount means a compile failed.\n\n"
"For feature reads (enumerate / settings), use the "
"`feature` tool with action='list' or 'get'.")
.with_string_param("kind",
"Currently 'state'. New kinds will be added here "
"'state' or 'shadercache'. New kinds will be added here "
"rather than as new tools.")
.build();
server->register_tool(tool,
Expand All @@ -527,9 +562,12 @@ void RemoteControl::RegisterInspectTool()
if (kind == "state") {
return TextResult(EngineStateBlob().dump());
}
if (kind == "shadercache") {
return TextResult(ShaderCacheBlob().dump());
}
return ErrorResult("unknown kind",
{ { "kind", kind },
{ "supported", mcp::json::array({ "state" }) } });
{ "supported", mcp::json::array({ "state", "shadercache" }) } });
});
}

Expand Down
45 changes: 45 additions & 0 deletions src/ShaderCache.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "ShaderCache.h"
#include "Globals.h"
#include "ShaderCompileStatus.h"
#include "ShaderFileWatcher.h"
#include "Util.h"

Expand All @@ -14,6 +15,28 @@

namespace SIE
{
// Chrono-free snapshot of compile counters for consumers that can't include
// ShaderCache.h (see ShaderCompileStatus.h). Thread-safe: atomics + the
// shader-map lock inside GetCurrentFailedCount.
ShaderCompileStatus GetShaderCompileStatus()
{
auto* cache = globals::shaderCache;
if (!cache)
return {};
// Read the task counters once and derive `compiling` from the same
// snapshot, so callers never observe compiling=false with work still
// outstanding. Named-field init avoids depending on member order.
const uint64_t completed = cache->GetCompletedTasks();
const uint64_t total = cache->GetTotalTasks();
ShaderCompileStatus status{};
status.valid = true;
status.compiling = completed < total;
status.completedTasks = completed;
status.totalTasks = total;
status.failedTasks = cache->GetFailedTasks();
status.currentFailedCount = cache->GetCurrentFailedCount();
return status;
}
Comment thread
alandtse marked this conversation as resolved.

// Custom include handler to track all includes during shader compilation
class TrackingIncludeHandler : public ID3DInclude
Expand Down Expand Up @@ -1488,13 +1511,35 @@ namespace SIE
shaderBlob->Release();
}

#ifdef TRACY_ENABLE
{
// Timeline annotation: a (re)compile failed. Pairs with the
// MCP shadercache status (failedTasks) for build-agnostic
// detection; this gives the exact frame for perf correlation.
const auto tracyMsg = std::format("Shader compile FAILED: {} {} {:X}",
magic_enum::enum_name(type), magic_enum::enum_name(shaderClass), descriptor);
TracyMessageC(tracyMsg.c_str(), tracyMsg.size(), 0xFF4444);
}
#endif

cache.AddCompletedShader(shaderClass, shader, descriptor, nullptr);
return nullptr;
}
if (errorBlob)
logger::debug("Shader logs:\n{}", static_cast<char*>(errorBlob->GetBufferPointer()));
logger::debug("Compiled shader {}:{}:{:X}", magic_enum::enum_name(type), magic_enum::enum_name(shaderClass), descriptor);

#ifdef TRACY_ENABLE
{
// Timeline annotation: a shader (re)compiled successfully. During
// a hot-reload this marks the exact frame the new shader went
// live, so A/B perf windows split precisely on it.
const auto tracyMsg = std::format("Shader compiled: {} {} {:X}",
magic_enum::enum_name(type), magic_enum::enum_name(shaderClass), descriptor);
TracyMessage(tracyMsg.c_str(), tracyMsg.size());
}
#endif

// strip debug info
if (!globals::state->IsDeveloperMode()) {
ID3DBlob* strippedShaderBlob = nullptr;
Expand Down
26 changes: 26 additions & 0 deletions src/ShaderCompileStatus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <cstdint>

// Minimal, dependency-free view of the shader (re)compile state for consumers
// that must NOT pull in ShaderCache.h — notably RemoteControl.cpp, where
// ShaderCache.h's global-scope `using namespace std::chrono` would leak chrono
// names (e.g. `last`) into the vendored cpp-mcp headers (httplib/base64) and
// trip warning-as-error. Defined in ShaderCache.cpp against the live cache.
namespace SIE
{
struct ShaderCompileStatus
{
bool valid = false; ///< false when the shader cache singleton is unavailable
bool compiling = false;
uint64_t completedTasks = 0;
uint64_t totalTasks = 0;
uint64_t failedTasks = 0;
uint64_t currentFailedCount = 0;
};

/// Snapshot the current shader-cache compile counters. Thread-safe: reads
/// atomics and takes the shader-map lock internally. Callable from the
/// RemoteControl listener thread.
ShaderCompileStatus GetShaderCompileStatus();
}
Loading