Direct3DHook 屏幕捕获高级技巧:多重采样 Resolve、GPU 缩放与 KeyedMutex 跨设备纹理共享全解析
Direct3DHook 屏幕捕获高级技巧:多重采样 Resolve、GPU 缩放与 KeyedMutex 跨设备纹理共享全解析
【免费下载链接】Direct3DHookDirectX Capture and Overlays by using Direct3D API hooks项目地址: https://gitcode.com/gh_mirrors/di/Direct3DHook
Direct3DHook 是一款基于 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 个采样点,每个采样点各存一份颜色值。
这种纹理有两个麻烦:
- 不能直接 Map:CPU 无法按普通 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.cs),Hook 会创建一张目标尺寸的渲染目标纹理_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 游戏进程,选中后点击 Capture,Hook 就会注入并完成截图。想体验 GPU 缩放,只需在界面上填入缩放宽/高(逻辑见 Form1.cs),返回的图片即为目标尺寸。
接入自己的项目:你的程序只需与宿主接口打交道——创建
CaptureProcess注入目标游戏,调用BeginGetScreenshot发起请求,再等待ScreenshotReceived事件拿到结果即可。MSAA、缩放、设备共享全部发生在游戏进程内部,宿主侧代码非常干净。
要点速查表 ✅
| 概念 | 一句话理解 | 对应代码 |
|---|---|---|
| 多重采样 Resolve | MSAA 画面先"结算"成单采样图再复制 | DXHookD3D11.cs |
| GPU 缩放 | 全屏三角形条把画面重绘到目标尺寸,CPU 零参与 | ScreenAlignedQuadRenderer.cs |
| KeyedMutex 共享 | 两把数字钥匙交替传递,保证跨设备读写安全 | 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),仅供参考
