能否使用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
相关产品推荐
相关产品推荐

