如何将DirectX11 Texture2D转换为可在CPU上处理的图像
DirectX11 Texture2D 转CPU可访问图像实现方案
前置说明
只有源Texture2D绑定了D3D11_BIND_RENDER_TARGET或D3D11_BIND_SHADER_RESOURCE,且Usage类型不是D3D11_USAGE_IMMUTABLE时,才能通过资源复制的方式读取数据。如果源纹理是深度缓冲、多重采样纹理这类特殊资源,需要先做格式解析/转换再执行后续步骤。
具体实现步骤
步骤1:创建CPU可读的Staging纹理
Staging纹理是DX11专门用于CPU-GPU数据交互的资源类型,创建参数必须严格符合要求:
// 先获取源纹理的参数 D3D11_TEXTURE2D_DESC srcDesc; pSrcTexture->GetDesc(&srcDesc); D3D11_TEXTURE2D_DESC stagingDesc = {}; stagingDesc.Width = srcDesc.Width; stagingDesc.Height = srcDesc.Height; stagingDesc.MipLevels = 1; // 需要读取全Mip链就和srcDesc.MipLevels保持一致 stagingDesc.ArraySize = 1; stagingDesc.Format = srcDesc.Format; stagingDesc.SampleDesc.Count = 1; stagingDesc.SampleDesc.Quality = 0; stagingDesc.Usage = D3D11_USAGE_STAGING; stagingDesc.BindFlags = 0; stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; stagingDesc.MiscFlags = 0; ID3D11Texture2D* pStagingTexture = nullptr; HRESULT hr = pD3DDevice->CreateTexture2D(&stagingDesc, nullptr, &pStagingTexture); if (FAILED(hr)) { // 自行添加错误处理逻辑 return; }
步骤2:复制源纹理数据到Staging纹理
调用CopyResource执行GPU侧的资源复制,该操作为异步执行,如果需要立即读取数据可以增加事件查询做同步,避免读取到未完成的脏数据:
// 多重采样纹理需要先执行ResolveSubresource解析为非多重采样纹理再复制 if (srcDesc.SampleDesc.Count > 1) { pImmediateContext->ResolveSubresource(pStagingTexture, 0, pSrcTexture, 0, srcDesc.Format); } else { pImmediateContext->CopyResource(pStagingTexture, pSrcTexture); }
步骤3:映射纹理获取CPU侧可操作数据
映射后拿到的指针可以直接拷贝到自定义缓冲区做后续处理(像素计算、格式转换、存盘等):
D3D11_MAPPED_SUBRESOURCE mappedRes; hr = pImmediateContext->Map(pStagingTexture, 0, D3D11_MAP_READ, 0, &mappedRes); if (SUCCEEDED(hr)) { // 按纹理格式计算单像素字节数,此处以常用的R8G8B8A8_UNORM为例 const uint32_t pixelByteSize = 4; // 自行管理缓冲区生命周期,此处仅做示例 uint8_t* pCpuImageBuffer = new uint8_t[srcDesc.Width * srcDesc.Height * pixelByteSize]; // *不要直接按宽高整块拷贝,RowPitch存在对齐填充,必须逐行拷贝避免数据错位* for (uint32_t y = 0; y < srcDesc.Height; y++) { memcpy( pCpuImageBuffer + y * srcDesc.Width * pixelByteSize, (uint8_t*)mappedRes.pData + y * mappedRes.RowPitch, srcDesc.Width * pixelByteSize ); } // 处理完成后必须调用Unmap释放资源 pImmediateContext->Unmap(pStagingTexture, 0); // 此处pCpuImageBuffer就是CPU侧可自由处理的图像数据 } // 用完释放Staging纹理资源,高频使用场景可以复用Staging纹理避免反复创建销毁开销 pStagingTexture->Release();
注意事项
- 如果源纹理是BCn等压缩格式,映射拿到的是压缩后的数据,需要自行解压才能得到原始像素值,也可以在创建Staging纹理时指定非压缩格式,复制时由硬件完成格式转换
- 同步逻辑如果处理不当会引发GPU-CPU互相等待的性能问题,非必要场景不要每帧都执行纹理读取操作
内容的提问来源于stack exchange,提问作者Mike Hawk
相关产品推荐
相关产品推荐

