Direct3D虚拟GPU地址

Direct3D virtual GPU address

有创建常量缓冲区的代码,用于将一些数据上传到 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());

} 

其中 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)));


    }
}

CreateConstantBufferView 在内存中使用两个地址:1) 堆开始,2) BufferLocation 字段的虚拟 GPU 内存地址

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

当CPU访问内存时使用CPU地址。对于 D3D12_HEAP_TYPE_UPLOAD 中的数据,该地址用于将数据写入资源,因为它位于 CPU 和 GPU 都可以访问的某种 'shared memory' 中。 CPU 地址是映射到所需访问类型的正确物理位置的虚拟内存地址。

GPU 地址在 GPU 访问内存时使用,通常是当输入汇编程序将其用于几何 (VB/IB) 或在 sampler/texture 描述符堆内时。对于 D3D12_HEAP_TYPE_DEFAULT,内存只能由 GPU 访问,因此实际上并没有 CPU 地址。 GPU 也可以直接读取 D3D12_HEAP_TYPE_UPLOAD 资源。 GPU 地址是特定于 GPU 内存架构的虚拟地址。

For Unified Memory Architecture (UMA) systems like Xbox One, the CPU and GPU addresses are often the same virtual memory address.

您通过 首先将数据加载到 D3D12_HEAP_TYPE_DEFAULT 资源, 通过 CPU 上的 Map 将其复制到 D3D12_HEAP_TYPE_UPLOAD 对象,那么您必须在 GPU 上发出 command-list 命令,以实际将数据从那里复制到 D3D12_HEAP_TYPE_DEFAULT 资源。

对于常量,它们通常位于 D3D12_HEAP_TYPE_UPLOAD 堆中。虽然您也可以在这些堆中使用 VB 和 IB,但它们实际上仅对每帧更新的 "usage dynamic" 样式资源有用。在大多数 GPU 架构上,将它们作为 D3D12_HEAP_TYPE_DEFAULT 中的 "usage static" 样式资源获取速度更快。由于常量通常每帧都会更改,因此将它们放在 D3D12_HEAP_TYPE_DEFAULT.

中没有意义

请记住,作为应用程序开发人员,您负责通过 fences 进行 CPU/GPU 同步,因此您需要确保在 GPU 仍在运行时不要更改 CPU 上的内存需要它。除了非常简单的情况(比如 this sample where it basically creates one constant buffer resource per backbuffer frame), you usually need some kind of linear allocator memory manager for constants. For an example, see GraphicsMemory in the DirectX Tool Kit for DX12.

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