Phase 2: Present-hook video path (shared-texture mirror)

Add an injected IDXGISwapChain::Present / Present1 hook as a lower-latency,
border-free alternative to WGC. The hook copies the swapchain backbuffer into a
shared keyed-mutex texture (coop_video_<pid>); the host opens it by name and
samples it. New opt-in HookSubsys_Video (protocol v6 -> v7); the Video mirror
panel gains a WGC vs Hooked source toggle that installs/removes the subsystem.

Verified by present_hook_test (drives a real D3D11 swapchain end-to-end and reads
the rendered pixels back through the shared texture) and against Phantom Brave
(D3D9: hook installs cleanly and stays idle, WGC fallback). All 5 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 11:34:12 +02:00
parent 9557b9ca69
commit 36b861d167
21 changed files with 1160 additions and 36 deletions

View File

@@ -1,5 +1,7 @@
// Owns the video-mirror pipeline (WGC capture + frame renderer) and its UI.
// The target window comes from the injected hook's reported game HWND.
// Owns the video-mirror pipeline and its UI. Two interchangeable sources feed the
// same letterboxed renderer: Windows Graphics Capture (no injection, the default)
// and the injected Present-hook's shared backbuffer texture (lower latency, needs
// the video subsystem hooked). The target window/pid come from the injected hook.
#pragma once
#include <cstdint>
@@ -8,23 +10,33 @@
#include <windows.h>
#include "capture/frame_renderer.hpp"
#include "capture/shared_texture.hpp"
#include "capture/window_capture.hpp"
#include "ui/app_chrome.hpp"
namespace coop
{
class InjectionPanel;
class CapturePanel
{
public:
bool init(ID3D11Device* device);
// The window to mirror (0 if none yet); typically the injected game's HWND.
// The window to mirror via WGC (0 if none yet); typically the injected game's HWND.
void set_target(HWND target)
{
target_ = target;
}
// The injection panel supplies the target pid + Present-hook video channel and
// lets this panel install/remove the video subsystem when the source is Hooked.
void set_injection(InjectionPanel* injection)
{
injection_ = injection;
}
// `stats` are the host's render frame-timing, drawn as the mirror's
// frametime / FPS graphs (this window is what the mirror renders into).
void draw_ui(const FrameStats& stats);
@@ -33,13 +45,22 @@ public:
void render(ID3D11DeviceContext* ctx, std::uint32_t dst_w, std::uint32_t dst_h);
private:
enum Source : int
{
Source_Wgc = 0, // Windows Graphics Capture
Source_Hooked = 1, // injected Present-hook shared texture
};
void draw_perf_graphs(const FrameStats& stats);
ID3D11Device* device_ = nullptr;
FrameRenderer renderer_;
WindowCapture capture_;
SharedTextureSource shared_;
InjectionPanel* injection_ = nullptr;
HWND target_ = nullptr;
bool enabled_ = false;
int source_ = Source_Wgc;
};
} // namespace coop