M2(Vulkan): too-late detection + red relaunch banner on the mirror window
The hook reports vk_too_late when vulkan-1.dll is loaded and the GPA hook has been in for >4s but it never caught the app creating its device -- i.e. the app resolved its Vulkan functions before we hooked (injected too late). Surfaced through a new HookStatus.vk_too_late field (protocol 15->16) and shown by the host as a red banner on the mirror window: "Detected a Vulkan game, but the mirror hook attached too late... enable Auto re-attach (and Set up Vulkan layer if it persists) and relaunch." WGC keeps mirroring meanwhile. mock_game_test gains a too-late detection check (late-inject a Vulkan game -> vk_too_late trips). Verified live: the banner shows over the running tool when a Vulkan game is injected late with the video subsystem on (added a debug-harness `video on/off` command to drive it). 15/15 ctest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -227,6 +227,7 @@ DWORD WINAPI worker_thread(LPVOID)
|
||||
coop::hook::update_input_diagnostics(g_ipc); // refreshes each tick; registrations can change
|
||||
coop::hook::release_cursor_tick(); // free the operator's mouse if requested (no-op otherwise)
|
||||
coop::hook::hook_publish(g_ipc); // installed-hooks list + call counts
|
||||
g_ipc.set_vk_too_late(coop::hook::vk_injected_too_late()); // Vulkan attached-too-late banner
|
||||
g_ipc.heartbeat();
|
||||
|
||||
// Reconcile ~4x/s, but drain MKB events far more often (input must be
|
||||
|
||||
@@ -147,6 +147,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void set_vk_too_late(bool too_late)
|
||||
{
|
||||
if (block_ != nullptr)
|
||||
{
|
||||
block_->status.vk_too_late = too_late ? 1u : 0u;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the rumble the game requested for a slot (so the host can forward it to
|
||||
// the guest's controller). Plain stores; the hook is the sole writer.
|
||||
void note_rumble(std::uint32_t slot, std::uint16_t left, std::uint16_t right)
|
||||
|
||||
@@ -36,6 +36,7 @@ std::atomic<std::uint64_t> g_presents{0};
|
||||
std::atomic<std::uint64_t> g_frames_shared{0};
|
||||
std::atomic<bool> g_present_captured{false}; // set once we successfully read a present back
|
||||
bool g_unsupported_logged = false;
|
||||
ULONGLONG g_install_tick = 0; // GetTickCount64 when the GPA hook went in (for the too-late grace)
|
||||
|
||||
// Real entry points. g_real_gdpa and below are unhooked exports/results, so they're plain PFNs;
|
||||
// the real vkGetInstanceProcAddr is reached through the inline hook's trampoline (real_gipa()).
|
||||
@@ -638,6 +639,7 @@ bool install_vk_hooks(IpcClient& ipc)
|
||||
}
|
||||
g_unsupported_logged = false;
|
||||
g_hk_gipa = safetyhook::create_inline(gipa, reinterpret_cast<void*>(&hk_vkGetInstanceProcAddr));
|
||||
g_install_tick = GetTickCount64();
|
||||
hook_set_installed(g_id_present, static_cast<bool>(g_hk_gipa));
|
||||
logf("install_vk_hooks: vkGetInstanceProcAddr=%p hooked=%d", gipa, static_cast<bool>(g_hk_gipa) ? 1 : 0);
|
||||
return static_cast<bool>(g_hk_gipa);
|
||||
@@ -685,6 +687,7 @@ void remove_vk_hooks()
|
||||
g_device = VK_NULL_HANDLE;
|
||||
g_instance = VK_NULL_HANDLE;
|
||||
g_real_gdpa = nullptr;
|
||||
g_install_tick = 0;
|
||||
g_present_captured.store(false, std::memory_order_relaxed);
|
||||
g_presents.store(0, std::memory_order_relaxed);
|
||||
g_frames_shared.store(0, std::memory_order_relaxed);
|
||||
@@ -703,10 +706,16 @@ std::uint64_t vk_frames_shared()
|
||||
|
||||
bool vk_injected_too_late()
|
||||
{
|
||||
// vulkan-1.dll is loaded but we never captured a present -> the app resolved its present
|
||||
// pointer before we hooked (or doesn't go through our chain). The host shows the banner.
|
||||
return GetModuleHandleW(L"vulkan-1.dll") != nullptr && g_presents.load(std::memory_order_relaxed) == 0 &&
|
||||
!g_present_captured.load(std::memory_order_relaxed);
|
||||
// Too late = vulkan-1.dll is loaded and our GPA hook has been in for a few seconds, but we
|
||||
// never even saw the app create its device -> it resolved its Vulkan functions before our
|
||||
// hook (we're not in the chain). A game we hooked early always trips hk_vkCreateDevice
|
||||
// (g_device != null) well within the grace window, even before it presents. The host shows
|
||||
// the "relaunch with Auto-attach / Vulkan layer" banner on this.
|
||||
if (GetModuleHandleW(L"vulkan-1.dll") == nullptr || g_device != VK_NULL_HANDLE || g_install_tick == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (GetTickCount64() - g_install_tick) > 4000;
|
||||
}
|
||||
|
||||
} // namespace coop::hook
|
||||
|
||||
Reference in New Issue
Block a user