无CPU拷贝下跨设备复制格式不兼容的ID3D11Texture2D方法
D3D11纹理跨设备复制+格式转换+CUDA兼容解决方案(Dolphin模拟器场景)
问题根源
- 直接修改
D3D11_TEXTURE2D_DESC::Format后CopyResource失败:CopyResource要求源/目标纹理格式必须完全兼容(同格式或硬件原生支持的无转换兼容对),DXGI_FORMAT_R10G10B10A2_UNORM和DXGI_FORMAT_R8G8B8A8_UNORM不在此列,因此操作静默失效。 - 不修改格式时
cudaGraphicsD3D11RegisterResource失败:CUDA不支持R10G10B10A2_UNORM格式,仅支持有限的标准格式(如R8G8B8A8_UNORM、R32_FLOAT等)。
无CPU拷贝的实现步骤
1. 创建符合要求的目标纹理
要同时满足格式转换和CUDA共享的需求,目标纹理的参数必须正确配置:
D3D11_TEXTURE2D_DESC targetDesc = {}; // 匹配源纹理的尺寸、层级数 targetDesc.Width = srcDesc.Width; targetDesc.Height = srcDesc.Height; targetDesc.MipLevels = 1; targetDesc.ArraySize = 1; // 设置为CUDA支持的R8G8B8A8_UNORM targetDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; targetDesc.SampleDesc.Count = 1; targetDesc.SampleDesc.Quality = 0; // 默认用法,GPU读写 targetDesc.Usage = D3D11_USAGE_DEFAULT; // 绑定渲染目标(用于格式转换)、着色器资源、无序访问(可选) targetDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; // 开启跨设备共享 targetDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; // 禁止CPU访问,避免拷贝 targetDesc.CPUAccessFlags = 0; ID3D11Texture2D* targetTex = nullptr; HRESULT hr = d3dDevice->CreateTexture2D(&targetDesc, nullptr, &targetTex);
2. 用GPU渲染管线完成格式转换
不能直接用CopyResource,必须通过像素着色器做格式转换:
- 编写极简像素着色器(HLSL):
Texture2D inputTex : register(t0); SamplerState linearSampler : register(s0); float4 PS(float2 uv : TEXCOORD0) : SV_Target { // 直接采样源纹理,UNORM格式下R10G10B10A2转R8G8B8A8仅需截断,GPU自动处理 return inputTex.Sample(linearSampler, uv); }
- 设置渲染目标为
targetTex,绘制全屏四边形,将源纹理内容渲染到目标纹理上。这一步完全在GPU端完成,无CPU参与。
3. 跨设备共享目标纹理
获取共享句柄并在CUDA关联的D3D设备上打开:
IDXGIResource* dxgiRes = nullptr; targetTex->QueryInterface(__uuidof(IDXGIResource), (void**)&dxgiRes); HANDLE sharedHandle = nullptr; dxgiRes->GetSharedHandle(&sharedHandle); dxgiRes->Release(); // 在CUDA对应的D3D11设备上打开共享资源 ID3D11Texture2D* cudaSharedTex = nullptr; cudaD3dDevice->OpenSharedResource(sharedHandle, __uuidof(ID3D11Texture2D), (void**)&cudaSharedTex);
4. 注册到CUDA供CNN使用
现在纹理格式符合要求,直接注册并映射:
cudaGraphicsResource* cudaRes = nullptr; cudaError_t err = cudaGraphicsD3D11RegisterResource(&cudaRes, cudaSharedTex, cudaGraphicsRegisterFlagsNone); if (err == cudaSuccess) { // 映射资源到CUDA地址空间 cudaGraphicsMapResources(1, &cudaRes, nullptr); uint8_t* devPtr = nullptr; size_t pitch = 0; cudaGraphicsResourceGetMappedPointer((void**)&devPtr, &pitch, cudaRes); // 将devPtr传入CNN处理(注意pitch对应的行字节数) cudaGraphicsUnmapResources(1, &cudaRes, nullptr); }
注意事项
- 确保两个D3D11设备(Dolphin的设备和CUDA关联设备)均支持DirectX 11及以上,且驱动支持跨设备共享。
- 绘制全屏四边形时,要正确设置视口与UV坐标,避免画面拉伸或错位。
- 追求性能的话,可改用Compute Shader完成格式转换,减少渲染管线的状态切换开销。
内容的提问来源于stack exchange,提问作者Emre Tekmen
相关产品推荐
相关产品推荐

