2025年三、D3D12学习笔记——顶点&索引数据传入GPU

三、D3D12学习笔记——顶点&索引数据传入GPU0 关键代码 几何数据需要按照一定的布局从 CPU 传入 GPU std vector d3d12 input element desc mInputLayout mInputLayout 语义 语义索引 格式 槽 偏移值 数据类型 d3d12 input element desc

大家好,我是讯享网,很高兴认识大家。

0.关键代码

几何数据需要按照一定的布局从CPU传入GPU:

std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout; mInputLayout =     { //语义,语义索引,格式,槽,偏移值,数据类型,是否实例化         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },         { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },     };

讯享网

传递数据通过以下代码实现:

讯享网Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferGPU = nullptr; Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferUploader = nullptr; //将数据传递到VertexBufferGPU VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),mCommandList.Get(), vertices.data(), vbByteSize, VertexBufferUploader); //绘图时使用 D3D12_VERTEX_BUFFER_VIEW vbv; vbv.BufferLocation = VertexBufferGPU->GetGPUVirtualAddress(); vbv.StrideInBytes = VertexByteStride; vbv.SizeInBytes = VertexBufferByteSize; cmdList->IASetVertexBuffers(0, 1, &vbv);

将CPU定义好的顶点数据vertices.data(),通过cmdList->IASetVertexBuffers(0, 1, &vbv);这把钥匙放进了GPU,本身IA开头的函数就意味着输入装配InputAssemble。

1.资源定义

Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferGPU = nullptr; Microsoft::WRL::ComPtr<ID3D12Resource> VertexBufferUploader = nullptr;

ID3D12Resource这个类代表了GPU资源,RTV和DSV,也是这个类型,D3D12对这个进行了统一,其实在DX11中还是区分什么ID3D11Buffer和ID3D11Texture2D这样的,那么DX12既然统一了,那怎么来区分呢?接着往下看。

2.数据传输的渠道

讯享网VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), vertices.data(), vbByteSize,VertexBufferUploader);

对应上边的资源定义,一个是左值,另一个是最后一个参数,对应默认堆和上传堆,VertexBufferGPU实际是个默认堆,而VertexBufferUploader实际是个上传堆: 

Microsoft::WRL::ComPtr<ID3D12Resource> d3dUtil::CreateDefaultBuffer( ID3D12Device* device, ID3D12GraphicsCommandList* cmdList, const void* initData, UINT64 byteSize, Microsoft::WRL::ComPtr<ID3D12Resource>& uploadBuffer) { ComPtr<ID3D12Resource> defaultBuffer; // Create the actual default buffer resource. ThrowIfFailed(device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(byteSize), D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(defaultBuffer.GetAddressOf()))); // In order to copy CPU memory data into our default buffer, we need to create // an intermediate upload heap. ThrowIfFailed(device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(byteSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(uploadBuffer.GetAddressOf()))); // Describe the data we want to copy into the default buffer. D3D12_SUBRESOURCE_DATA subResourceData = {}; subResourceData.pData = initData; subResourceData.RowPitch = byteSize; subResourceData.SlicePitch = subResourceData.RowPitch; // Schedule to copy the data to the default buffer resource. At a high level, the helper function UpdateSubresources // will copy the CPU memory into the intermediate upload heap. Then, using ID3D12CommandList::CopySubresourceRegion, // the intermediate upload heap data will be copied to mBuffer. cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST)); UpdateSubresources<1>(cmdList, defaultBuffer.Get(), uploadBuffer.Get(), 0, 0, 1, &subResourceData); cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ)); // Note: uploadBuffer has to be kept alive after the above function calls because // the command list has not been executed yet that performs the actual copy. // The caller can Release the uploadBuffer after it knows the copy has been executed. return defaultBuffer; }
讯享网// Create the actual default buffer resource. ThrowIfFailed(device->CreateCommittedResource(         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),         D3D12_HEAP_FLAG_NONE,         &CD3DX12_RESOURCE_DESC::Buffer(byteSize),         D3D12_RESOURCE_STATE_COMMON,         nullptr,         IID_PPV_ARGS(defaultBuffer.GetAddressOf()))); // In order to copy CPU memory data into our default buffer, we need to create // an intermediate upload heap.  ThrowIfFailed(device->CreateCommittedResource(         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),         D3D12_HEAP_FLAG_NONE,         &CD3DX12_RESOURCE_DESC::Buffer(byteSize),         D3D12_RESOURCE_STATE_GENERIC_READ,         nullptr,         IID_PPV_ARGS(uploadBuffer.GetAddressOf())));

函数CreateCommittedResource,创建一个堆和一个资源,根据类型的不同就是默认堆和上传堆。注意到默认堆创建的时候的访问权限Common,我们现在是要往默认堆上传数据,也就是写入,因此进行资源转换:

cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(),  D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST));

准备数据:

讯享网D3D12_SUBRESOURCE_DATA subResourceData = {}; subResourceData.pData = initData;//数据本身 subResourceData.RowPitch = byteSize;//描述数据的大小,占用内存 subResourceData.SlicePitch = subResourceData.RowPitch;//描述数据的大小,占用内存

万事俱备,只欠东风:

//核心函数,将数据subResourceData从CPU传导上传堆,再到默认堆 // <1>是模板参数,表示Subresource的数量 UpdateSubresources<1>(cmdList, defaultBuffer.Get(), uploadBuffer.Get(), 0, 0, 1, &subResourceData);

注意cmdlist,表明实际是将传数据放在命令列表,交由命令队列来做的,这里有数据及其描述,也有中间过渡的上传堆uploadBuffer,还有传入终点defaultBuffer。

现在的默认堆是支持写入的,后边获取数据实际是读出:

讯享网cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ));

现在数据已经被我们送到GPU了,但是还没通知到输入装配。

3.通知输入装配

通知时大概的对话是这样的:

我们:货已经到了,放在了XXXX,它大概这么大,每多少个是一个基本单元。所以我们列出了以下关键词:


讯享网

D3D12_VERTEX_BUFFER_VIEW vbv;
        vbv.BufferLocation = VertexBufferGPU->GetGPUVirtualAddress();//获得GPU存储位置地址
        vbv.SizeInBytes = VertexBufferByteSize;
        vbv.StrideInBytes = VertexByteStride;

然后,将我们的这个命令又安排进队列等待执行:

cmdList->IASetVertexBuffers(0, 1, &vbv);

注意这个函数的定义:

讯享网IASetVertexBuffers(              _In_  UINT StartSlot,//起始槽编号,0~15             _In_  UINT NumViews,//顶点缓冲区数量,描述第三个参数数组有几个顶点缓冲,要注意一个槽对应一个缓冲区,所以也就是说第三个参数最多不能超过16             _In_reads_opt_(NumViews)  const D3D12_VERTEX_BUFFER_VIEW *pViews)

看到这里,我们就把CPU的数据送到了GPU的存储位置上,并且告知了输入装配阶段,老规矩,注意事项:

实际上如果有多个顶点缓冲区View1和View2,我们可以用下边的操作:

cmdList->IASetVertexBuffers(0, 1, &View1);

cmdList->IASetVertexBuffers(0, 1, &View2);

也就是说一个槽上边可以放多个信息,只要你不一次性放进去(意味着通知当时拿出当时数据,下次通知会更改数据)。

一般而言我们绘图都是根据索引来的,类似顶点缓冲区传递数据。

4.索引缓冲区

Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferGPU = nullptr; Microsoft::WRL::ComPtr<ID3D12Resource> IndexBufferUploader = nullptr; IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), indices.data(), ibByteSize, IndexBufferUploader); D3D12_INDEX_BUFFER_VIEW ibv; ibv.BufferLocation = IndexBufferGPU->GetGPUVirtualAddress();//获得GPU存储位置地址 ibv.Format = IndexFormat;//描述格式,因为sizeof就能得到间隔了,其实和上边原理一样 ibv.SizeInBytes = IndexBufferByteSize; cmdList->IASetIndexBuffer(&ibv);

准备好了顶点,索引数据,剩下就是告知以何种方式组装:

讯享网cmdList->IASetPrimitiveTopology(PrimitiveType);

都准备好了,DrawCall!!!

5.DrawCall

这里我们根据顶点缓冲和索引缓冲数据使用函数:

cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);

同样是加入命令队列,首先告诉他我们每IndexCount个索引画一个Mesh,从索引堆里边第StartIndexLocation开始看,从顶点堆里第BaseVertexLocation开始看,最后一个桉树表示是否使用实例化。

至此,驱动单个像素生成的流水线就启动了。

小讯
上一篇 2025-02-16 09:06
下一篇 2025-03-13 11:01

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/124844.html