Direct3DHook 屏幕捕获高级技巧多重采样 Resolve、GPU 缩放与 KeyedMutex 跨设备纹理共享全解析【免费下载链接】Direct3DHookDirectX Capture and Overlays by using Direct3D API hooks项目地址: https://gitcode.com/gh_mirrors/di/Direct3DHookDirect3DHook 是一款基于 Direct3D API Hook 的屏幕捕获工具它能潜入游戏进程内部在渲染管线里直接截图并支持游戏内覆盖层如 FPS 显示。本文带你读懂它背后最硬核的 3 个高级技巧多重采样 Resolve、GPU 缩放和 KeyedMutex 跨设备纹理共享 为什么 Direct3DHook 的截图方式与众不同大多数截图工具如 PrintScreen、BitBlt从系统层面抓取屏幕像素遇到 3D 加速窗口经常会拿到黑屏。Direct3DHook 的思路完全不同它通过注入目标进程直接 Hook 游戏的SwapChain.Present函数——也就是游戏每一帧提交给显示器的最后一站。在这里渲染后的画面后缓冲区还热腾腾地在 GPU 上截图工具可以直接把它复制走。核心实现位于 D3D11 钩子 DXHookD3D11.cs它 Hook 了Present和ResizeTarget两个关键函数并在每次 Present 时检查是否有待处理的截图请求。整个捕获管线可以概括为三步步骤做什么关键技术① 取画面从后缓冲区取出当前帧多重采样 Resolve② 缩放按需缩放到目标尺寸GPU 全屏三角形条渲染③ 回传复制像素到内存并送回宿主进程KeyedMutex 跨设备共享接下来逐一拆解 多重采样 Resolve为什么开 MSAA 的截图容易出错MSAA 后缓冲区为什么不能直接读现代游戏为了画面边缘平滑普遍开启多重采样抗锯齿MSAA。开启后后缓冲区不再是一个像素一个颜色而是一个像素包含 2~16 个采样点每个采样点各存一份颜色值。这种纹理有两个麻烦不能直接 MapCPU 无法按普通 2D 数组的方式解读多采样数据尺寸计算复杂采样数会让内存布局和行距变得难以处理。所以行业通行做法是先用ResolveSubresource把多采样纹理结算Resolve成一张单采样纹理再去复制。Direct3DHook 的自动处理在 DXHookD3D11.cs 中Hook 逻辑只做一个简单判断采样数SampleDescription.Count 1开了 MSAA→ 调用ResolveSubresource将画面解析到单采样纹理_resolvedRT采样数等于 1未开 MSAA→ 直接用CopySubresourceRegion复制跳过 Resolve 省一次 GPU 开销。这个设计对新手非常友好无论游戏开没开抗锯齿你完全不用关心Hook 内部自动选择正确路径截图内容始终是干净的单采样图像。GPU 缩放把 4K 截图缩成缩略图的正确姿势为什么不在 CPU 上缩放很多工具的做法是先抓一张 4K 原图回内存再用 GDI 缩放。问题是 4K 图像一次搬运约 32MB 像素数据CPU 缩放还要逐像素插值又慢又占内存。Direct3DHook 的做法更聪明缩放整个在 GPU 上完成。当截图请求携带了Resize参数见 ScreenshotRequest.csHook 会创建一张目标尺寸的渲染目标纹理_resizedRT格式固定为R8G8B8A8_UNorm——这是兼容性最好的格式可直接编码为 BMP/PNG/JPEG。全屏三角形条一个顶点都不需要缩放的执行者是 ScreenAlignedQuadRenderer.cs。它有一个非常经典的技巧不创建任何顶点缓冲区。顶点着色器直接读取内置的SV_VertexID用一行位运算把 4 个虚拟顶点映射到屏幕四角像素着色器则对 Resolve 后的画面做双线性采样。最终一条三角形带Draw(4, 0)就把整个画面重新画到了目标尺寸的画布上。GPU 缩放流程 后缓冲区 --Resolve-- 单采样纹理 --全屏四边采样-- 目标尺寸纹理 (抗锯齿结算) (GPU 双线性缩放)此外渲染器还提供了UseLinearSampling开关可在双线性滤波平滑与最近邻采样锐利之间切换——需要原汁原味的像素缩放时直接关闭线性采样即可。KeyedMutex 跨设备纹理共享全文最硬核的部分 为什么要创建两套设备注意 Hook 中有两个设备游戏自己的设备currentRT.Device——后缓冲区属于它Resolve 必须由它执行Hook 自己创建的设备_device——用于缩放渲染、Staging 纹理和 CPU 读取。问题来了设备的 A 创建的纹理默认设备 B 是看不见的。跨设备共享纹理需要 DXGI 的共享资源 钥匙互斥锁机制创建纹理时带上SharedKeyedMutex标志见 DXHookD3D11.cs另一台设备通过OpenSharedResource用共享句柄打开这张纹理得到自己的视角_resolvedRTShared。Acquire/Release用钥匙数字传递画面KeyedMutex 的规则是谁拿到钥匙谁才能操作纹理。钥匙是一个 64 位数字两边交替传递游戏设备: Acquire(0) → Resolve 画面 → Release(1) ↓ 钥匙 1 交给对方 Hook 设备: Acquire(1) → 缩放/读取 → Release(0)在 DXHookD3D11.cs 中可以清楚看到这个交接仪式Resolve 前先用Acquire(0)锁住纹理写入后Release(1)把钥匙 1 交给 Hook 自己的设备缩放开跑前先Acquire(1)结束后Release(0)归还。后台线程读取像素时DXHookD3D11.cs也重复同样的握手Acquire(1)→ 复制到 Staging 纹理 →Release(0)再配合 GPU Event 查询等待拷贝完成最后MapSubresource把像素数据搬进内存。整个读取在后台线程完成绝不阻塞游戏渲染——这是截图工具对游戏帧率影响极小的关键。优雅降级共享失败就退回单设备跨设备共享对系统有一定要求Windows Vista 及以上、正确的驱动支持。如果共享纹理创建失败代码会捕获异常并自动重试改用游戏自己的设备同时承担缩放任务useSameDeviceForResize true此时_resolvedRTKeyedMutex为 null所有 Acquire/Release 逻辑自动跳过降级为单设备模式继续工作。这种先试高级路径、失败自动回退的设计是生产级 Hook 代码值得学习的典范。新手上手三步用起来 克隆项目并打开解决方案 Direct3DHook.sln编译生成Capture.dll与测试程序git clone https://gitcode.com/gh_mirrors/di/Direct3DHook运行示例测试程序 TestScreenshot/Form1.cs 会列出本机正在运行的 3D 游戏进程选中后点击 CaptureHook 就会注入并完成截图。想体验 GPU 缩放只需在界面上填入缩放宽/高逻辑见 Form1.cs返回的图片即为目标尺寸。接入自己的项目你的程序只需与宿主接口打交道——创建CaptureProcess注入目标游戏调用BeginGetScreenshot发起请求再等待ScreenshotReceived事件拿到结果即可。MSAA、缩放、设备共享全部发生在游戏进程内部宿主侧代码非常干净。要点速查表 ✅概念一句话理解对应代码多重采样 ResolveMSAA 画面先结算成单采样图再复制DXHookD3D11.csGPU 缩放全屏三角形条把画面重绘到目标尺寸CPU 零参与ScreenAlignedQuadRenderer.csKeyedMutex 共享两把数字钥匙交替传递保证跨设备读写安全DXHookD3D11.cs优雅降级共享失败自动退回单设备模式DXHookD3D11.cs无阻塞回传像素读取放后台线程 GPU Event 同步DXHookD3D11.cs 小贴士如果你的项目只需要基础截图能力也可以看看 DX9 版本的实现 DXHookD3D9.cs它没有跨设备共享的复杂度是理解整体架构的绝佳入门材料。【免费下载链接】Direct3DHookDirectX Capture and Overlays by using Direct3D API hooks项目地址: https://gitcode.com/gh_mirrors/di/Direct3DHook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考