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:
@@ -78,6 +78,7 @@ HookStatusView IpcServer::hook_status() const
|
||||
view.raw_input_gamepad = s.raw_input_gamepad != 0;
|
||||
view.raw_input_gamepad_sink = s.raw_input_gamepad_sink != 0;
|
||||
view.dinput_loaded = s.dinput_loaded != 0;
|
||||
view.vk_too_late = s.vk_too_late != 0;
|
||||
view.audio_streams_seen = s.audio_streams_seen;
|
||||
for (std::uint32_t i = 0; i < kMaxAudioStreams; ++i)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ struct HookStatusView
|
||||
bool raw_input_gamepad = false;
|
||||
bool raw_input_gamepad_sink = false;
|
||||
bool dinput_loaded = false;
|
||||
bool vk_too_late = false; // Vulkan game, but the capture hook attached too late (relaunch banner)
|
||||
|
||||
// Audio render-hook diagnostics (for the Audio panel's stream-count view).
|
||||
std::uint32_t audio_streams_seen = 0;
|
||||
|
||||
@@ -139,6 +139,12 @@ std::string apply_test_command(const std::string& cmd, coop::UiState& ui, coop::
|
||||
audio.dev_set_enabled(on);
|
||||
return "ok";
|
||||
}
|
||||
if (v == "video")
|
||||
{
|
||||
// Install/remove the hooked video subsystem (Present/GL/D3D9/Vulkan capture hooks).
|
||||
injection.request_video(arg(1) == "on");
|
||||
return "ok";
|
||||
}
|
||||
if (v == "debug")
|
||||
{
|
||||
ui.debug_details = (arg(1) == "on");
|
||||
@@ -219,6 +225,38 @@ std::string steam_manifest_path()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Red banner on the mirror window when a Vulkan game was injected too late for the hooked
|
||||
// capture (Vulkan caches its present pointer at startup, so the hook must be present before
|
||||
// vkCreateInstance). WGC still mirrors the window; this prompts the operator to relaunch with
|
||||
// Auto-attach so the hook arms before the game initializes Vulkan. Shown regardless of overlay
|
||||
// visibility, since it's an actionable alert about the mirror itself.
|
||||
void draw_vk_too_late_banner()
|
||||
{
|
||||
const ImGuiViewport* vp = ImGui::GetMainViewport();
|
||||
float w = vp->WorkSize.x - 40.0f;
|
||||
if (w > 760.0f)
|
||||
{
|
||||
w = 760.0f;
|
||||
}
|
||||
ImGui::SetNextWindowPos(ImVec2(vp->WorkPos.x + vp->WorkSize.x * 0.5f, vp->WorkPos.y + 16.0f),
|
||||
ImGuiCond_Always, ImVec2(0.5f, 0.0f));
|
||||
ImGui::SetNextWindowSize(ImVec2(w, 0.0f));
|
||||
const ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_AlwaysAutoResize;
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.28f, 0.03f, 0.03f, 0.92f));
|
||||
ImGui::Begin("##vk_too_late", nullptr, flags);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.45f, 1.0f));
|
||||
ImGui::TextWrapped("Detected a Vulkan game, but the mirror hook attached too late to capture it "
|
||||
"with low latency (Vulkan resolves its present function at startup). The window "
|
||||
"is mirroring via WGC meanwhile. For the hooked path, enable \"Auto re-attach "
|
||||
"this game on relaunch\" (and \"Set up Vulkan layer\" if it persists) in the "
|
||||
"Injection panel, then relaunch the game.");
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// When the overlay is hidden, briefly show a fading hint so the operator can find
|
||||
// the way back. The window is borderless and non-interactive so it never steals a
|
||||
// click or a frame from the mirror underneath.
|
||||
@@ -431,6 +469,10 @@ int run()
|
||||
{
|
||||
draw_overlay_hidden_hint(ImGui::GetTime() - overlay_hidden_at);
|
||||
}
|
||||
if (injection.hook_status().vk_too_late) // Vulkan game injected too late -> relaunch prompt
|
||||
{
|
||||
draw_vk_too_late_banner();
|
||||
}
|
||||
coop::apply_layout_end_frame(); // clear the one-shot "Reset layout" force
|
||||
|
||||
if (ui.request_quit) // File -> Exit
|
||||
|
||||
Reference in New Issue
Block a user