M2(DX9): D3D9 capture via Present read-back (covers plain D3D9 + D3D9Ex)

New d3d9_hook.cpp (own file like opengl_hook): inline-hooks
IDirect3DDevice9::Present (vtable index 17, discovered from a throwaway device;
clean prologue so inline is safe, stdcall() on x86) and read-backs the backbuffer
with GetRenderTargetData into a D3DPOOL_SYSTEMMEM surface, swizzles BGRA->RGBA,
and uploads it into the shared keyed-mutex texture on a hook-owned D3D11 device
(a D3D9 surface isn't D3D11-shareable). Both plain D3D9 and D3D9Ex call this same
Present, so one read-back path serves both -- the GPU shared-surface fast path
the plan sketched for D3D9Ex wasn't worth it. Wired into the video subsystem
(install/remove in dllmain); hook links d3d9.

mock_game_test decodes DX9 + DX9Ex frames through the hook; 15/15 ctest (incl.
the x86 sub-build). Roadmap: DX9 milestone done and removed (remaining
renumbered); architecture + lessons updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 11:55:06 +02:00
parent 5bccfd8b86
commit 67c1940d52
6 changed files with 456 additions and 34 deletions

View File

@@ -21,6 +21,7 @@
#include "hook_registry.hpp"
#include "ipc_client.hpp"
#include "mkb_hook.hpp"
#include "d3d9_hook.hpp"
#include "opengl_hook.hpp"
#include "present_hook.hpp"
#include "xinput_hook.hpp"
@@ -134,17 +135,19 @@ DWORD WINAPI worker_thread(LPVOID)
{
const bool present_ok = coop::hook::install_present_hooks(g_ipc);
const bool gl_ok = coop::hook::install_opengl_hooks(g_ipc);
video_installed = present_ok || gl_ok;
const bool d3d9_ok = coop::hook::install_d3d9_hooks(g_ipc);
video_installed = present_ok || gl_ok || d3d9_ok;
if (video_installed)
{
coop::hook::logf("worker_thread: video hooks installed (present=%d opengl=%d)",
present_ok ? 1 : 0, gl_ok ? 1 : 0);
coop::hook::logf("worker_thread: video hooks installed (present=%d opengl=%d d3d9=%d)",
present_ok ? 1 : 0, gl_ok ? 1 : 0, d3d9_ok ? 1 : 0);
}
}
else if (!want_video && video_installed)
{
coop::hook::remove_present_hooks();
coop::hook::remove_opengl_hooks();
coop::hook::remove_d3d9_hooks();
video_installed = false;
coop::hook::logf("worker_thread: video hooks removed (host request)");
}
@@ -255,6 +258,7 @@ BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved)
coop::hook::shutdown_audio_hooks(); // detach: remove detours + release the kept probe
coop::hook::remove_present_hooks();
coop::hook::remove_opengl_hooks();
coop::hook::remove_d3d9_hooks();
coop::hook::remove_mkb_hooks();
coop::hook::hook_registry_reset();
}