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:
@@ -11,7 +11,7 @@ namespace coop
|
||||
|
||||
// Bump whenever the layout of SharedBlock or CoopPadState changes. The hook
|
||||
// refuses to attach to a host with a mismatched version.
|
||||
inline constexpr std::uint32_t kProtocolVersion = 6;
|
||||
inline constexpr std::uint32_t kProtocolVersion = 7;
|
||||
|
||||
// 'COOP' little-endian, used to sanity-check the mapping before trusting it.
|
||||
inline constexpr std::uint32_t kProtocolMagic = 0x504F4F43u;
|
||||
@@ -66,7 +66,8 @@ enum HookSubsystem : std::uint32_t
|
||||
HookSubsys_Input = 0, // XInput hooks (forward the guest pad)
|
||||
HookSubsys_Focus = 1, // focus spoof (keep the game running unfocused)
|
||||
HookSubsys_Audio = 2, // WASAPI render-hook (audio mirror without echo)
|
||||
HookSubsys_Count = 3,
|
||||
HookSubsys_Video = 3, // IDXGISwapChain::Present hook (shared-texture video mirror)
|
||||
HookSubsys_Count = 4,
|
||||
};
|
||||
|
||||
// Maximum individual hooks reported in the registry (a few per subsystem).
|
||||
@@ -134,6 +135,29 @@ struct HookControl
|
||||
std::atomic<std::uint32_t> subsystem_disabled[HookSubsys_Count];
|
||||
};
|
||||
|
||||
// Present-hook video channel. When the video subsystem is installed, the hook
|
||||
// copies the game's swapchain backbuffer into a shared keyed-mutex texture named
|
||||
// coop_video_<pid> and publishes its dimensions/format here; the host opens that
|
||||
// texture by name and samples it (a lower-latency alternative to WGC). The hook
|
||||
// is the sole writer. `generation` bumps on every published frame (0 = nothing
|
||||
// shared yet); width/height/format describe the currently shared texture, so the
|
||||
// host reopens it whenever they change. The keyed mutex uses key 0 on both sides.
|
||||
struct VideoShare
|
||||
{
|
||||
std::atomic<std::uint32_t> generation; // bumps per published frame; 0 = none yet
|
||||
std::uint32_t width; // shared texture dimensions / DXGI format
|
||||
std::uint32_t height;
|
||||
std::uint32_t format; // DXGI_FORMAT of the shared texture
|
||||
std::uint64_t present_calls; // cumulative Present() detours (diagnostic)
|
||||
};
|
||||
|
||||
// The shared backbuffer texture is named per target pid, like the audio ring.
|
||||
inline constexpr wchar_t kVideoSharePrefix[] = L"Local\\coop_video_";
|
||||
|
||||
// Keyed-mutex key both producer and consumer use (a plain cross-process mutex on
|
||||
// the texture; the keyed mutex is created released at key 0).
|
||||
inline constexpr std::uint64_t kVideoMutexKey = 0;
|
||||
|
||||
// Top-level shared block. The host is the sole writer of pad state; the hook is
|
||||
// the sole reader. A seqlock (even = stable, odd = write in progress) lets the
|
||||
// reader grab a torn-free snapshot without a kernel lock on the hot path.
|
||||
@@ -151,8 +175,8 @@ struct SharedBlock
|
||||
// Host -> hook control (which subsystems to install).
|
||||
HookControl control;
|
||||
|
||||
// Phase 2 appends the shared-texture handle/dimensions control fields here;
|
||||
// keep new members at the end so existing offsets never shift.
|
||||
// Hook -> host Present-hook video channel (shared-texture dimensions/format).
|
||||
VideoShare video;
|
||||
};
|
||||
|
||||
static_assert(std::atomic<std::uint32_t>::is_always_lock_free,
|
||||
|
||||
@@ -128,4 +128,11 @@ inline std::wstring shared_memory_name(unsigned long target_pid)
|
||||
return std::wstring(kSharedMemoryPrefix) + std::to_wstring(target_pid);
|
||||
}
|
||||
|
||||
// Name of the Present-hook shared backbuffer texture (created by the hook with
|
||||
// CreateSharedHandle, opened by the host with OpenSharedResourceByName).
|
||||
inline std::wstring video_share_name(unsigned long target_pid)
|
||||
{
|
||||
return std::wstring(kVideoSharePrefix) + std::to_wstring(target_pid);
|
||||
}
|
||||
|
||||
} // namespace coop
|
||||
|
||||
Reference in New Issue
Block a user