Memory management Direct3D虚拟GPU地址

Memory management Direct3D虚拟GPU地址,memory-management,upload,direct3d,Memory Management,Upload,Direct3d,下面是创建常量缓冲区的代码,用于将某些数据上载到GPU内存: void BoxApp::BuildConstantBuffers() { mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true); UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSiz

下面是创建常量缓冲区的代码,用于将某些数据上载到GPU内存:

    void BoxApp::BuildConstantBuffers()
    {

        mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);

        UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));

        D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mObjectCB->Resource()->GetGPUVirtualAddress();

    D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc; 

    cbvDesc.BufferLocation = cbAddress; 

    cbvDesc.SizeInBytes = objCBByteSize; 

    md3dDevice->CreateConstantBufferView( 
        &cbvDesc,
        mCbvHeap->GetCPUDescriptorHandleForHeapStart());

} 
void-BoxApp::BuildConstantBuffers()
{
mObjectCB=std::使_唯一(md3dDevice.Get(),1,true);
UINT objCBByteSize=d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
D3D12_GPU_VIRTUAL_ADDRESS cbAddress=mObjectCB->Resource()->GetGPUVirtualAddress();
D3D12_常量_缓冲区_视图_描述cbvDesc;
cbvDesc.BufferLocation=cbAddress;
cbvDesc.SizeInBytes=objcbbytes;
md3dDevice->CreateConstantBufferView(
&cbvDesc,
mCbvHeap->GetCPUDescriptorHandleForHeapStart());
} 
其中UploadBuffer为:

template<typename T>
class UploadBuffer
{
public:

    UploadBuffer(ID3D12Device* device, UINT elementCount, bool isConstantBuffer) : 
        mIsConstantBuffer(isConstantBuffer)
    {
        mElementByteSize = sizeof(T);

        if(isConstantBuffer)
            mElementByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(T));

        ThrowIfFailed(device->CreateCommittedResource(
            &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
            D3D12_HEAP_FLAG_NONE,
            &CD3DX12_RESOURCE_DESC::Buffer(mElementByteSize*elementCount),
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&mUploadBuffer)));

        ThrowIfFailed(mUploadBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mMappedData)));


    }
}
模板
类上载缓冲区
{
公众:
UploadBuffer(ID3D12Device*设备,UINT元素计数,布尔isConstantBuffer):
错误状态缓冲(isConstantBuffer)
{
mElementByteSize=sizeof(T);
如果(isConstantBuffer)
mElementByteSize=d3dUtil::CalcConstantBufferByteSize(sizeof(T));
ThrowIfFailed(设备->CreateCommittedResource(
&CD3DX12堆属性(D3D12堆类型上传),
D3D12_堆_标志_无,
&CD3DX12_RESOURCE_DESC::Buffer(mElementByteSize*elementCount),
D3D12_资源_状态_通用_读取,
nullptr,
IID_PPV_ARGS(&muploaddbuffer));
ThrowIfFailed(mUploadBuffer->Map(0,nullptr,reinterpret_cast(&mMappedData));
}
}
CreateConstantBufferView在内存中使用两个地址:1)堆开始,2)虚拟GPU内存地址(按BufferLocation字段)


将物理创建的缓冲区(常量对象)在哪里?为什么此方法使用两个不同的地址?

CPU访问内存时使用CPU地址。对于
D3D12\u HEAP\u TYPE\u UPLOAD
中的数据,该地址用于将数据写入资源,因为它位于某种“共享内存”中,CPU和GPU都可以访问。CPU地址是映射到所需访问类型的正确物理位置的虚拟内存地址

GPU地址在GPU访问内存时使用,通常在几何体输入汇编程序(VB/IB)或采样器/纹理描述符堆内部使用时使用。对于
D3D12\u HEAP\u TYPE\u DEFAULT
,内存只能由GPU访问,因此没有真正的CPU地址。GPU也可以直接读取
D3D12\u HEAP\u TYPE\u UPLOAD
资源。GPU地址是特定于GPU内存体系结构的虚拟地址

对于像Xbox One这样的统一内存体系结构(UMA)系统,CPU和GPU地址通常是相同的虚拟内存地址

首先通过CPU上的
Map
将数据复制到
D3D12\u HEAP\u TYPE\u UPLOAD
对象中,将数据加载到
D3D12\u HEAP\u TYPE\u DEFAULT
资源中,然后必须在GPU上发出命令列表命令,将数据从那里实际复制到
D3D12\u HEAP\u TYPE\u DEFAULT
资源中

对于常量,它们通常位于
D3D12_HEAP\u TYPE\u UPLOAD
堆中。虽然您也可以在这些堆中使用VBs和IBs,但它们实际上只对每帧更新的“使用动态”样式资源有用。在大多数GPU架构上,在
D3D12\u HEAP\u TYPE\u DEFAULT
中以“使用静态”样式获取这些资源的速度更快。由于常量通常在每一帧都会更改,因此将它们放入
D3D12\u HEAP\u TYPE\u DEFAULT
是没有意义的

请记住,作为应用程序开发人员,您负责通过fences进行CPU/GPU同步,因此您需要确保在GPU仍然需要时不会更改CPU上的内存。除了非常简单的情况(例如,它基本上为每个backbuffer帧创建一个常量缓冲区资源),通常需要某种类型的线性分配器内存管理器来管理常量。有关示例,请参见中的
GraphicsMemory


最后一个问题是,渲染目标通常必须位于GPU可访问内存中,即使在UMA系统上,CPU也无法访问该内存。在某些情况下,GPU实际上在“分片”中工作,这对渲染目标缓冲区也有影响。
D3D12_HEAP\u TYPE_READBACK
堆的目的是优化这样一种情况,即您希望GPU将数据从渲染目标写入一次CPU可以读取但不能写入的位置。

谢谢您的回答。关键点是“除了创建实际的顶点缓冲区资源外,我们还需要创建一个堆类型为D3D12_heap_type_upload的中间上传缓冲区资源。”这意味着我们必须创建两个独立的缓冲区,因为(见上文)CPU无法写入D3D12_heap_type_默认堆。