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

能否使用UpdateSubresources向DirectX12默认缓冲区追加顶点?

问题

UpdateSubresources的官方文档参数描述过于简略,对新手帮助有限,其函数定义如下:

UINT64 inline UpdateSubresources(
  _In_ ID3D12GraphicsCommandList *pCmdList,
  _In_ ID3D12Resource            *pDestinationResource,
  _In_ ID3D12Resource            *pIntermediate,
       UINT64                    IntermediateOffset,
  _In_ UINT                      FirstSubresource,
  _In_ UINT                      NumSubresources,
  _In_ D3D12_SUBRESOURCE_DATA    *pSrcData
);

我正遵循《DirectX 12 3D游戏编程入门》的代码示例开发,已实现创建默认缓冲区和上传缓冲区的函数:

com_ptr<ID3D12Resource> create_default_buffer(ID3D12Device* device, uint64_t size) {
    com_ptr<ID3D12Resource> buffer;

    CD3DX12_HEAP_PROPERTIES heap_props(D3D12_HEAP_TYPE_DEFAULT);
    auto res_desc = CD3DX12_RESOURCE_DESC::Buffer(size);
    auto hr = device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &res_desc, 
                                              D3D12_RESOURCE_STATE_COMMON,
                                              nullptr, __uuidof(buffer), buffer.put_void());
    check_hresult(hr);
    return buffer;
}

com_ptr<ID3D12Resource> create_upload_buffer(ID3D12Device* device, uint64_t size) {
    com_ptr<ID3D12Resource> buffer;

    CD3DX12_HEAP_PROPERTIES heap_props(D3D12_HEAP_TYPE_UPLOAD);
    auto res_desc = CD3DX12_RESOURCE_DESC::Buffer(size);
    auto hr = device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &res_desc,
                                              D3D12_RESOURCE_STATE_GENERIC_READ,
                                              nullptr, __uuidof(buffer), buffer.put_void());
    check_hresult(hr);
    return buffer;
}

现在我有存储在std::vector<Vertex>中的新顶点数据,希望追加到已创建的默认缓冲区中,请问如何用UpdateSubresources实现?该函数是否仅支持覆盖整个缓冲区?


解答

1. 核心结论

UpdateSubresources支持部分更新,并非只能覆盖整个缓冲区,完全可以实现缓冲区的追加写入。

2. 实现步骤

步骤1:转换目标缓冲区状态

默认缓冲区初始状态为D3D12_RESOURCE_STATE_COMMON,需先切换为D3D12_RESOURCE_STATE_COPY_DEST才能接收拷贝数据:

CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(
    pDefaultBuffer.Get(),
    D3D12_RESOURCE_STATE_COMMON,
    D3D12_RESOURCE_STATE_COPY_DEST
);
pCmdList->ResourceBarrier(1, &barrier);

步骤2:配置源数据结构体

计算目标缓冲区的追加偏移,填充D3D12_SUBRESOURCE_DATA:

// existingVertexCount:缓冲区中已有的顶点数量
uint64_t destOffset = existingVertexCount * sizeof(Vertex);
D3D12_SUBRESOURCE_DATA subresourceData = {};
subresourceData.pData = newVertexData.data(); // newVertexData是待追加的std::vector<Vertex>
subresourceData.RowPitch = newVertexData.size() * sizeof(Vertex); // 源数据总字节数
subresourceData.SlicePitch = subresourceData.RowPitch; // 缓冲区无切片,与RowPitch一致

步骤3:创建临时上传缓冲区

复用你已实现的create_upload_buffer函数,创建仅容纳新数据的中转缓冲区:

uint64_t uploadSize = newVertexData.size() * sizeof(Vertex);
com_ptr<ID3D12Resource> uploadBuffer = create_upload_buffer(pDevice, uploadSize);

步骤4:执行追加写入

通过IntermediateOffset指定目标缓冲区的写入起始位置,实现追加:

UpdateSubresources(
    pCmdList,
    pDefaultBuffer.Get(), // 目标默认缓冲区
    uploadBuffer.Get(),   // 中间上传缓冲区
    destOffset,           // 目标缓冲区的追加起始偏移
    0,                    // 缓冲区仅1个子资源,起始索引为0
    1,                    // 更新1个子资源
    &subresourceData      // 待写入的源数据
);

步骤5:恢复缓冲区状态

数据写入完成后,将缓冲区切换回绘制所需的状态(例如顶点缓冲区状态):

barrier = CD3DX12_RESOURCE_BARRIER::Transition(
    pDefaultBuffer.Get(),
    D3D12_RESOURCE_STATE_COPY_DEST,
    D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER
);
pCmdList->ResourceBarrier(1, &barrier);

3. 关键注意点

  • 缓冲区属于单subresource资源,因此FirstSubresource固定为0,NumSubresources固定为1。
  • IntermediateOffset是实现追加的核心,它直接对应目标缓冲区的写入起始偏移。
  • 必须确保目标默认缓冲区的剩余空间足够容纳新顶点数据(你已确认空间充足)。

内容的提问来源于stack exchange,提问作者Rob N

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:00:32