Make the inline-hook (suspended-inject) Vulkan path work on Sphere Spectacle
The earlier validation concluded suspended-inject was "not applicable -- the title requires launching through Steam." That was wrong; it was two bugs: 1. coop_vk_validate's inject mode launched the exe with CreateProcessW and a null working directory, so the game couldn't load steam_api64.dll / resources/ (loaded relative to cwd) and never rendered -> no presents. Launch with the game's own folder as cwd and it runs fine directly, no Steam needed. 2. The game resolves vkQueuePresentKHR / vkCreateSwapchainKHR via vkGetInstanceProcAddr (volk's volkLoadInstance does this), but vk_hook only substituted our detours when they were resolved via vkGetDeviceProcAddr -- so the present bypassed the hook. Intercept those names in hk_vkGetInstanceProcAddr too (our detours already gate on g_capture_enabled/g_device, so handing them out before the device exists is safe). With both fixed, inject mode captures Sphere Spectacle correctly: 1920x1080, correct colors/orientation (screenshot), ~480 fps present while mirroring at the ~150 Hz throttle -- no present-thread impact (the VkCapture fix is shared). Also makes the validator ASSERT a present-rate floor while capturing (it used to report the rate and rationalize it, which is exactly what hid the 144->3 FPS stall), and reports the true mirror rate from video.generation. Division of labor is about who launches the game: layer for Steam-launched (can't suspend), inject when we control the launch. README lessons-learned corrected accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -271,6 +271,20 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL hk_vkGetInstanceProcAddr(VkInstance ins
|
||||
{
|
||||
return reinterpret_cast<PFN_vkVoidFunction>(&hk_vkGetDeviceProcAddr);
|
||||
}
|
||||
// vkGetInstanceProcAddr can also resolve device-level functions (the loader returns a
|
||||
// dispatch trampoline). A game -- or volk's volkLoadInstance -- that resolves the present /
|
||||
// swapchain entry points this way (rather than via vkGetDeviceProcAddr) would otherwise get
|
||||
// the real loader pointer and bypass our capture. Sphere Spectacle does exactly this, so
|
||||
// intercept them here too. (Our detours gate on g_capture_enabled / g_device, so handing them
|
||||
// out before the device exists is safe.)
|
||||
if (std::strcmp(name, "vkQueuePresentKHR") == 0)
|
||||
{
|
||||
return reinterpret_cast<PFN_vkVoidFunction>(&hk_vkQueuePresentKHR);
|
||||
}
|
||||
if (std::strcmp(name, "vkCreateSwapchainKHR") == 0)
|
||||
{
|
||||
return reinterpret_cast<PFN_vkVoidFunction>(&hk_vkCreateSwapchainKHR);
|
||||
}
|
||||
}
|
||||
return real_gipa(instance, name);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user