PC layer: probe which SteamVR process renders the environment (Steam Frame diagnosis)

A probe in vrserver and the device/draw diagnostics established that the Steam Frame renders its home on the headset,
not on the PC (the compositor draws nothing, the vrlink streaming driver makes no environment shaders), so a PC-side
layer can't change its home. Kept as the record of that investigation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ToastconcernandClaude Opus 4.8 committed 2026-10-21 21:37:37 -05:00
1 parent 727ff9579a
commit cda415d8a4
1 file changed
+68 -14
+68 -14
View File
@@ -46,17 +46,14 @@ namespace {
// ── process / files ──────────────────────────────────────────────────────────────────────────────
bool isCompositor() {
static int v = -1;
if (v < 0) {
wchar_t path[MAX_PATH] = {};
GetModuleFileNameW(nullptr, path, MAX_PATH);
const wchar_t* base = wcsrchr(path, L'\\');
base = base ? base + 1 : path;
v = _wcsicmp(base, L"vrcompositor.exe") == 0;
}
return v == 1;
bool processIs(const wchar_t* exe) {
wchar_t path[MAX_PATH] = {};
GetModuleFileNameW(nullptr, path, MAX_PATH);
const wchar_t* base = wcsrchr(path, L'\\');
return _wcsicmp(base ? base + 1 : path, exe) == 0;
}
bool isCompositor() { static const int v = processIs(L"vrcompositor.exe"); return v; }
bool isVrServer() { static const int v = processIs(L"vrserver.exe"); return v; }
// %LOCALAPPDATA%\Frame Home (home_platform.h's root() on Windows), where the logs live.
fs::path dir() {
@@ -90,6 +87,24 @@ void logf(const char* fmt, ...) {
std::fclose(f);
}
bool g_diagnostic = false; // this process (vrserver) only watches what the streaming driver draws; it replaces nothing
void probef(const char* fmt, ...) {
std::lock_guard<std::mutex> l(g_logMutex);
std::error_code ec;
fs::create_directories(dir() / "logs", ec);
FILE* f = nullptr;
if (_wfopen_s(&f, (dir() / "logs" / "frame-d3d-probe.log").wstring().c_str(), L"a") || !f) return;
SYSTEMTIME t;
GetLocalTime(&t);
std::fprintf(f, "%02d:%02d:%02d ", t.wHour, t.wMinute, t.wSecond);
va_list ap;
va_start(ap, fmt);
std::vfprintf(f, fmt, ap);
va_end(ap);
std::fputc('\n', f);
std::fclose(f);
}
std::vector<char> readFile(const fs::path& p) {
std::ifstream in(p, std::ios::binary);
return {std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
@@ -145,6 +160,8 @@ std::mutex g_shaderMutex, g_texMutex;
std::atomic<bool> g_off{false};
std::atomic<bool> g_connected{false};
std::atomic<uint64_t> g_replaced{0}, g_skipped{0}; // draws swapped for the home / left undrawn (a diagnostic)
std::atomic<uint64_t> g_drawsHere{0}, g_drawsOther{0}; // every draw on a hooked context (any shader), for diagnosis
std::atomic<int> g_deviceCount{0}, g_deferredCount{0}; // distinct devices / deferred contexts the compositor made
envshare::State* g_state = nullptr; // Frame Home's shared State (it owns it; we map its handle)
HANDLE g_stateMap = nullptr;
@@ -333,6 +350,7 @@ using PFN_DrawInstanced = void(STDMETHODCALLTYPE*)(ID3D11DeviceContext*, UINT, U
using PFN_DrawIndexedInstanced = void(STDMETHODCALLTYPE*)(ID3D11DeviceContext*, UINT, UINT, UINT, INT, UINT);
PFN_CreatePixelShader g_realCreatePixelShader = nullptr;
PFN_CreatePixelShader g_serverRealCreatePixelShader = nullptr; // vrserver probe (diagnostic)
PFN_CreateDeferredContext g_realCreateDeferredContext = nullptr;
PFN_CreateDeferredContext1 g_realCreateDeferredContext1 = nullptr;
@@ -365,6 +383,27 @@ HRESULT STDMETHODCALLTYPE CreatePixelShader_hook(ID3D11Device* dev, const void*
std::lock_guard<std::mutex> l(g_shaderMutex);
if (auto it = g_shaderKinds.find(h); it != g_shaderKinds.end()) g_psKind[*out] = it->second;
if (auto it = g_shaderName.find(h); it != g_shaderName.end()) g_psName[*out] = it->second;
static std::unordered_set<void*> devices; // how many distinct devices the compositor makes shaders on
if (devices.insert(dev).second) { g_deviceCount.store(static_cast<int>(devices.size())); logf("pixel shader created on device %p (%s)%s", (void*)dev, dev == g_device ? "ours" : "another", g_device ? "" : " [before wrap]"); }
}
return hr;
}
// vrserver probe: log whether the streaming driver creates the environment's own pixel shaders here.
HRESULT STDMETHODCALLTYPE CreatePixelShaderProbe_hook(ID3D11Device* dev, const void* code, SIZE_T len, ID3D11ClassLinkage* link, ID3D11PixelShader** out) {
const HRESULT hr = g_serverRealCreatePixelShader(dev, code, len, link, out);
if (SUCCEEDED(hr) && code && len) {
static std::atomic<int> total{0};
total.fetch_add(1);
const uint64_t h = fnv(code, len);
auto it = g_shaderName.find(h);
if (it != g_shaderName.end()) {
static std::unordered_set<std::string> seen;
static std::mutex m;
std::lock_guard<std::mutex> l(m);
if (seen.insert(it->second).second) probef("vrserver creates %s%s", it->second.c_str(), g_shaderKinds.count(h) ? " <-- AN ENVIRONMENT SHADER (home could be drawn here)" : "");
}
if (total.load() == 1 || total.load() == 300) probef("vrserver has created %d pixel shaders", total.load());
}
return hr;
}
@@ -431,6 +470,7 @@ struct HomeBind {
// The texture lock is held across a replaced draw so the pipe thread can't free the texture under it.
template <typename Draw>
void replaceDraw(ID3D11DeviceContext* ctx, CtxHooks* h, Draw&& draw) {
(ctx == g_context ? g_drawsHere : g_drawsOther).fetch_add(1, std::memory_order_relaxed); // every draw we see
noteDraw(ctx); // diagnostic: what the compositor draws, and on which context
ID3D11PixelShader* bound = nullptr;
const Kind k = classify(ctx, bound);
@@ -515,12 +555,12 @@ void hookContext(ID3D11DeviceContext* ctx) {
HRESULT STDMETHODCALLTYPE CreateDeferredContext_hook(ID3D11Device* dev, UINT flags, ID3D11DeviceContext** out) {
const HRESULT hr = g_realCreateDeferredContext(dev, flags, out);
if (SUCCEEDED(hr) && out && *out && dev == g_device) hookContext(*out);
if (SUCCEEDED(hr) && out && *out) { g_deferredCount.fetch_add(1); logf("CreateDeferredContext: device %p (%s)", (void*)dev, dev == g_device ? "ours" : "another"); if (dev == g_device) hookContext(*out); }
return hr;
}
HRESULT STDMETHODCALLTYPE CreateDeferredContext1_hook(ID3D11Device1* dev, UINT flags, ID3D11DeviceContext1** out) {
const HRESULT hr = g_realCreateDeferredContext1(dev, flags, out);
if (SUCCEEDED(hr) && out && *out && reinterpret_cast<ID3D11Device*>(dev) == g_device) hookContext(*out);
if (SUCCEEDED(hr) && out && *out) { g_deferredCount.fetch_add(1); logf("CreateDeferredContext1: device %p (%s)", (void*)dev, reinterpret_cast<ID3D11Device*>(dev) == g_device ? "ours" : "another"); if (reinterpret_cast<ID3D11Device*>(dev) == g_device) hookContext(*out); }
return hr;
}
@@ -662,10 +702,12 @@ void watchOff() {
// A heartbeat (every 5 s) of what the layer is doing, for testing without a headset to look through.
if (++sec % 5 == 0) {
const uint64_t r = g_replaced.exchange(0), s = g_skipped.exchange(0);
const uint64_t dh = g_drawsHere.exchange(0), dohter = g_drawsOther.exchange(0);
size_t here, other;
{ std::lock_guard<std::mutex> l(g_shaderMutex); here = g_seenHere.size(); other = g_seenOther.size(); }
logf("heartbeat: home over %llu aurora draws, %llu undrawn (5 s); distinct shaders drawn here=%zu other=%zu; connected=%d",
(unsigned long long)r, (unsigned long long)s, here, other, g_connected.load() ? 1 : 0);
logf("heartbeat: replaced %llu, skipped %llu; draws seen here=%llu other=%llu (5 s); named shaders here=%zu other=%zu; devices=%d deferred=%d hookedCtx=%d; connected=%d",
(unsigned long long)r, (unsigned long long)s, (unsigned long long)dh, (unsigned long long)dohter, here, other,
g_deviceCount.load(), g_deferredCount.load(), g_ctxHookCount.load(), g_connected.load() ? 1 : 0);
}
}
}
@@ -723,6 +765,18 @@ extern "C" HRESULT WINAPI D3D11CreateDevice( // exported by layer/d3d11_proxy.
(*device)->GetImmediateContext(&immediate); // +1 ref, kept by wrap()
if (immediate) wrap(*device, immediate);
}
// Probe: the Steam Frame streams, and its home may be rendered by the vrlink driver in vrserver, not the
// compositor. In vrserver, hook CreatePixelShader to see whether the environment's own shaders are made there
// (if so, that's where to draw the home for a streamed headset). Diagnostic only — nothing is replaced here.
if (SUCCEEDED(hr) && isVrServer() && device && *device) {
static std::once_flag once;
std::call_once(once, [&] {
g_diagnostic = true;
scanAuroraShaders();
hookSlot(*device, 15, reinterpret_cast<void*>(CreatePixelShaderProbe_hook), &g_serverRealCreatePixelShader);
probef("vrserver: watching for the environment's shaders (%zu aurora shaders known)", g_shaderKinds.size());
});
}
return hr;
}