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

@@ -0,0 +1,69 @@
// Host-side consumer of the injected Present-hook's shared backbuffer texture.
// Opens the keyed-mutex texture the hook publishes (coop_video_<pid>) by name,
// copies the latest frame into a private texture under the keyed mutex, and
// exposes an SRV the FrameRenderer can draw -- the lower-latency alternative to
// Windows Graphics Capture. See hook/src/present_hook.cpp and coop/protocol.hpp.
#pragma once
#include <cstdint>
#include <string>
#include <d3d11_1.h>
#include <wrl/client.h>
#include "ipc/ipc_server.hpp"
namespace coop
{
class SharedTextureSource
{
public:
// Binds to the host's device (must support ID3D11Device1). Returns false if not.
bool init(ID3D11Device* device);
// Polls the hook's video channel for `pid`. (Re)opens the shared texture when
// the pid/dimensions/format change, and copies the newest frame into the
// private texture under the keyed mutex. Returns true if a frame is available
// to draw (sticky: keeps the last copy until a new one or a reset).
bool update(const VideoShareView& share, unsigned long pid);
// Drop the opened resources (target changed / mirror turned off).
void reset();
[[nodiscard]] ID3D11ShaderResourceView* srv() const
{
return srv_.Get();
}
[[nodiscard]] std::uint32_t width() const
{
return width_;
}
[[nodiscard]] std::uint32_t height() const
{
return height_;
}
[[nodiscard]] std::uint64_t frames_copied() const
{
return frames_copied_;
}
private:
bool reopen(unsigned long pid, const VideoShareView& share);
Microsoft::WRL::ComPtr<ID3D11Device1> device_;
Microsoft::WRL::ComPtr<ID3D11DeviceContext> ctx_;
Microsoft::WRL::ComPtr<ID3D11Texture2D> shared_; // opened from the hook, by name
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> mutex_;
Microsoft::WRL::ComPtr<ID3D11Texture2D> private_; // our sampled copy
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> srv_;
unsigned long pid_ = 0;
std::uint32_t width_ = 0;
std::uint32_t height_ = 0;
std::uint32_t format_ = 0;
std::uint32_t last_generation_ = 0;
std::uint64_t frames_copied_ = 0;
};
} // namespace coop