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

@@ -242,12 +242,15 @@ int wmain(int argc, wchar_t** argv)
const std::uint64_t produced = ring->frames_produced.load(std::memory_order_relaxed);
const std::uint64_t overruns = ring->overruns.load(std::memory_order_relaxed);
const bool fmt_ready = coop::audio_ring_format_ready(*ring);
const std::uint32_t vgen = block->video.generation.load(std::memory_order_acquire);
const std::uint64_t vpresent = block->video.present_calls;
std::printf("[%4.1fs] hb=%u streams=%u peak=%.4f ring{fmt=%d %uHz/%uch/%ubit produced=%llu "
"overruns=%llu}\n",
"overruns=%llu} video{present=%llu gen=%u %ux%u}\n",
(t + 1) * 0.5, heartbeat, streams, peak, fmt_ready ? 1 : 0, ring->sample_rate,
ring->channels, ring->bits, static_cast<unsigned long long>(produced),
static_cast<unsigned long long>(overruns));
static_cast<unsigned long long>(overruns), static_cast<unsigned long long>(vpresent),
vgen, block->video.width, block->video.height);
for (std::uint32_t i = 0; i < coop::kMaxAudioStreams && i < streams; ++i)
{
const coop::AudioStreamInfo& s = status.audio_streams[i];
@@ -261,7 +264,7 @@ int wmain(int argc, wchar_t** argv)
}
// Dump the hook registry so the installed-hooks list can be verified headless.
static const char* kSubsys[] = {"Input", "Focus", "Audio"};
static const char* kSubsys[] = {"Input", "Focus", "Audio", "Video"};
std::printf("\nInstalled hooks (%u):\n", status.hook_entry_count);
for (std::uint32_t i = 0; i < status.hook_entry_count && i < coop::kMaxHookEntries; ++i)
{