如何使用D3d Shared Texture实现高性能游戏实时截图
BitBlt、PrintWindow、旧版桌面复制这类方案帧率低、拖慢游戏的核心原因是:所有方案都需要把GPU渲染完成的帧数据做一次GPU显存到系统内存的CPU拷贝,过程中要锁GPU帧缓冲、频繁切换内核态/用户态,CPU开销极高,碰到吃满CPU负载的游戏自然会互相抢占资源,帧率难以上30FPS。
D3D共享纹理是目前Windows平台游戏捕获性能最高的应用层方案,核心逻辑和OBS游戏捕获完全一致:
- 基于D3D/DXGI的跨进程GPU资源共享能力,全程不需要把帧数据拷贝到CPU内存,捕获程序直接在GPU侧访问游戏渲染完成的帧纹理,额外开销可以压到1%以内,1080P/2K分辨率下跑满游戏原生144/240帧率都不会拖慢游戏运行。
- 主流实现用D3D11/D3D12的NT类型共享句柄,支持跨D3D版本共享:不管游戏是用D3D9、D3D11还是D3D12渲染,捕获端只要用同一块物理GPU创建D3D11设备,就能直接打开共享句柄拿到纹理,不需要额外做格式转换。
- 捕获时机完全对齐游戏渲染节奏:通过Hook游戏交换链的
Present方法,在游戏每帧渲染完成、准备提交到显示器的时候触发捕获,不会打断游戏本身的渲染管线,也不存在轮询带来的延迟和性能浪费。
代码实现步骤(对接OpenCV实时展示)
前置准备:安装Windows 10/11 SDK,编译时链接d3d11.lib dxgi.lib,OpenCV需要编译时开启WITH_D3D11选项,才能直接对接D3D纹理做GPU侧处理,避免不必要的CPU拷贝。
- 第一步:在捕获主程序里初始化D3D11设备
注意设备必须和游戏运行在同一块物理GPU上,双显卡笔记本要提前在系统显卡设置里指定两个程序都用独立显卡,否则共享句柄会打开失败。#include <d3d11.h> #include <dxgi1_2.h> #include <opencv2/opencv.hpp> #include <opencv2/core/directx.hpp> ID3D11Device* dev = nullptr; ID3D11DeviceContext* ctx = nullptr; HRESULT hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &dev, nullptr, &ctx ); if (FAILED(hr)) { /* 处理设备创建失败逻辑 */ } - 第二步:Hook游戏交换链的Present方法
用MinHook这类轻量Inline Hook库,注入游戏进程后HookIDXGISwapChain::Present虚函数,每次游戏调用Present提交帧的时候,拿到当前交换链的后备缓冲纹理:// 游戏进程内的Hook逻辑 ID3D11Texture2D* backbuf = nullptr; pSwapChain->GetBuffer(0, IID_PPV_ARGS(&backbuf)); static HANDLE shared_tex_handle = nullptr; // 仅首次获取后备缓冲、或游戏重建交换链(切分辨率/切全屏)时创建共享句柄 if (!shared_tex_handle) { IDXGIResource1* res = nullptr; backbuf->QueryInterface(__uuidof(IDXGIResource1), (void**)&res); res->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &shared_tex_handle); // 用命名管道/内存映射文件把shared_tex_handle传递给捕获主程序 res->Release(); } backbuf->Release(); - 第三步:捕获端拿到共享句柄后读取纹理,对接OpenCV处理展示
这一步全程在GPU侧完成,不需要CPU拷贝,性能和OBS捕获一致:ID3D11Texture2D* shared_tex = nullptr; dev->OpenSharedResource(shared_tex_handle, IID_PPV_ARGS(&shared_tex)); cv::UMat gpu_frame; // 直接把D3D11纹理转成OpenCV的GPU矩阵 cv::directx::convertFromD3D11Texture2D(shared_tex, gpu_frame); // 此处编写OpenCV画面编辑逻辑,所有操作均在GPU执行 cv::cvtColor(gpu_frame, gpu_frame, cv::COLOR_BGRA2BGR); cv::GaussianBlur(gpu_frame, gpu_frame, cv::Size(5,5), 0); // 可自行添加边缘检测、绘制UI元素等处理逻辑 // 实时展示,imshow原生支持UMat直接显示,无额外拷贝开销 cv::imshow("Game Capture", gpu_frame); cv::waitKey(1); shared_tex->Release();
常见避坑点
- 绝对不要调用
Map或者GetDC方法把GPU纹理锁存到系统内存,这一步拷贝会直接让性能掉回30FPS水平,所有画面处理尽量用OpenCV的UMat走GPU加速,只有需要本地存帧的时候再把UMat拷贝到CPU端的Mat即可。 - 共享句柄不要每帧重复创建,句柄可长期复用,只有检测到游戏交换链重建(切分辨率、切全屏/窗口模式)的时候,才需要释放旧句柄、重新获取新的后备缓冲创建共享句柄。
- 带内核级反作弊的游戏会检测应用层Hook,该方案和OBS游戏捕获逻辑完全一致,正常走反作弊软件签名白名单流程即可,不要用这套逻辑做违规外挂功能,否则会触发账号封禁。
- 如果不想自己实现进程注入逻辑,Win10 1903以上版本也可以直接用Windows Graphics Capture API,本质也是基于共享纹理实现,不需要手动写Hook,但对部分老独占全屏游戏的兼容性不如手动Hook Present的方案。
内容的提问来源于stack exchange,提问作者user541396
相关产品推荐
相关产品推荐

