D3D12_TEXTURE2D从GPU复制到CPU可读缓冲区报错,求排查
问题:获取D3D12纹理CPU可读缓冲区时出错
需求
获取无法被CPU直接访问的D3D12_RESOURCE_DIMENSION_TEXTURE2D类型纹理的CPU可读缓冲区,尝试将该纹理复制到采用READBACK堆类型的中间资源。
实现代码
ID3D12Resource* newTex; hr = GetD3D12Device()->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(GetRequiredIntermediateSize(texture, 0, 1)), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&newTex)); if (hr < 0) { return; } D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; srcLocation.pResource = texture; srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; dstLocation.pResource = newTex; dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; commandList_->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr); void* pData; hr = newTex->Map(0, nullptr, &pData); if (hr < 0) { return; }
错误日志
D3D12 ERROR: ID3D12CommandList::CopyTextureRegion: D3D12_SUBRESOURCE_FOOTPRINT::Format is not supported at the current feature level with the dimensionality implied by the D3D12_SUBRESOURCE_FOOTPRINT::Height and D3D12_SUBRESOURCE_FOOTPRINT::Depth. Format = UNKNOWN, Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D, Height = 0, Depth = 0, and FeatureLevel is D3D_FEATURE_LEVEL_12_2.
疑问
- 请问我遗漏了什么步骤?
- 将该纹理转换为CPU可读缓冲区的方案是否可行?
解答
方案可行性
这个方案完全可行,通过READBACK堆的中间资源中转获取CPU可读数据,是D3D12中获取GPU纹理CPU访问权的标准做法。
遗漏步骤分析
错误核心是目标复制位置dstLocation的PlacedFootprint参数未正确初始化。当使用D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT时,必须手动填充dstLocation.PlacedFootprint的完整信息(包括格式、尺寸、行间距等),你当前仅初始化了结构体,未填充这些关键数据,导致D3D12无法识别纹理布局。
修正后的完整实现
ID3D12Resource* newTex; HRESULT hr; // 1. 获取源纹理子资源的布局信息 D3D12_RESOURCE_DESC srcDesc = texture->GetDesc(); UINT64 requiredBufferSize = 0; D3D12_SUBRESOURCE_FOOTPRINT srcFootprint = {}; UINT numRows = 0; UINT64 rowSizeInBytes = 0; GetD3D12Device()->GetCopyableFootprints( &srcDesc, 0, // 起始子资源索引 1, // 要获取的子资源数量 0, // 偏移量 &srcFootprint, &numRows, &rowSizeInBytes, &requiredBufferSize ); // 2. 创建READBACK类型的中间缓冲区 hr = GetD3D12Device()->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(requiredBufferSize), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&newTex) ); if (FAILED(hr)) { return; } // 3. 初始化源复制位置 D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; srcLocation.pResource = texture; srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; srcLocation.SubresourceIndex = 0; // 指定要复制的子资源索引 // 4. 初始化目标复制位置,填充PlacedFootprint D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; dstLocation.pResource = newTex; dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dstLocation.PlacedFootprint.Footprint = srcFootprint; dstLocation.PlacedFootprint.Offset = 0; // 从缓冲区起始位置放置纹理数据 // 5. 确保源纹理处于COPY_SOURCE状态(如果当前不是) if (srcDesc.State != D3D12_RESOURCE_STATE_COPY_SOURCE) { D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( texture, srcDesc.State, D3D12_RESOURCE_STATE_COPY_SOURCE ); commandList_->ResourceBarrier(1, &barrier); } // 6. 执行复制命令 commandList_->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr); // 7. 提交命令并等待GPU完成复制(关键步骤,否则Map读取的数据不完整) commandList_->Close(); ID3D12CommandQueue* cmdQueue = ...; // 获取你的命令队列 cmdQueue->ExecuteCommandLists(1, &commandList_); // 使用Fence等待GPU完成 ID3D12Fence* fence = nullptr; UINT64 fenceValue = 1; GetD3D12Device()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); cmdQueue->Signal(fence, fenceValue); if (fence->GetCompletedValue() < fenceValue) { HANDLE event = CreateEvent(nullptr, FALSE, FALSE, nullptr); fence->SetEventOnCompletion(fenceValue, event); WaitForSingleObject(event, INFINITE); CloseHandle(event); } // 8. 映射缓冲区获取CPU可读数据 void* pData; hr = newTex->Map(0, nullptr, &pData); if (FAILED(hr)) { return; } // 此时pData指向纹理数据,可通过srcFootprint解析像素 // 例如:每行像素起始地址 = (BYTE*)pData + rowIndex * srcFootprint.RowPitch
额外注意事项
- 必须等待GPU完成复制操作后再调用
Map,否则读取到的是未初始化或不完整的数据。 - 若源纹理当前状态不是
D3D12_RESOURCE_STATE_COPY_SOURCE,需先通过ResourceBarrier转换状态,否则复制命令会失败。 GetCopyableFootprints不仅能计算所需缓冲区大小,还能获取子资源的布局信息,方便后续解析像素数据。
内容的提问来源于stack exchange,提问作者Z.Zhang
相关产品推荐
相关产品推荐

