You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

疑问

  1. 请问我遗漏了什么步骤?
  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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:05:03