DIrectx12-常量缓冲区理解

常量的数据从CPU内存传给GPU,首先需要给常量数据建立一个描述符,让GPU知道这是一个什么资源,应该怎么处理。

描述符堆可以理解为描述符数组,取数据的时候通过“GetCPUDescriptorHandleForHeapStart”函数获取描述符堆的首地址,然后偏移描述符大小 * 要取描述符的索引

struct ObjectConstants
{
     XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
     float time;
};

上传数据并创建缓冲区

mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);//上传数据到GPU内存

    UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants)); //数据大小必须为256的倍数

    D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mObjectCB->Resource()->GetGPUVirtualAddress();//获取数据在GPU内存的地址
    // 偏移到缓冲区中的第i个对象常量buffer。 
    int boxCBufIndex = 0;
    cbAddress += boxCBufIndex*objCBByteSize;
    //始化描述符
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
    cbvDesc.BufferLocation = cbAddress;
    cbvDesc.SizeInBytes = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
    //创建常量缓冲区
    md3dDevice->CreateConstantBufferView(&cbvDesc,mCbvHeap->GetCPUDescriptorHandleForHeapStart());

创建并描述缓冲堆

D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
    cbvHeapDesc.NumDescriptors = 1; //缓冲区数量
    cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;//缓冲区类型  CBV常量缓冲  SRV着色器  UAV无类型缓冲区
    cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;//指示 该描述符应用于着色器阶段
    cbvHeapDesc.NodeMask = 0;//对于单适配器操作,将此设置为零
    ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc,IID_PPV_ARGS(&mCbvHeap)));//创建描述符堆

后面就到根签名发挥作用了,将着色器需要用到的数据绑定到对应的寄存器槽上,供着色器访问。

案例程序注释翻译:着色程序通常需要资源作为输入(常量缓冲,纹理,采样)。 根签名定义了着色程序期望的资源。 如果我们把着色器程序看作一个函数,把输入资源看作函数参数,那么根签名就可以看作是定义函数签名。 

    // 根参数可以是表、根描述符或根常量。 
    CD3DX12_ROOT_PARAMETER slotRootParameter[1];

    // 创建一个CBV描述符表
    CD3DX12_DESCRIPTOR_RANGE cbvTable; // 一个辅助结构,可以轻松初始化D3D12_DESCRIPTOR_RANGE结构
    cbvTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);//类型  数量  着色器寄存器
    slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable);

    // 根签名是一个根参数数组。 
    CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(1, slotRootParameter, 0, nullptr, 
        D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

    // 创建一个带有单个插槽的根签名,它指向一个由单个常量缓冲区组成的描述符范围 
    ComPtr<ID3DBlob> serializedRootSig = nullptr;
    ComPtr<ID3DBlob> errorBlob = nullptr;
    HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
        serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

    if(errorBlob != nullptr)
    {
        ::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
    }
    ThrowIfFailed(hr);

    ThrowIfFailed(md3dDevice->CreateRootSignature(0, serializedRootSig->GetBufferPointer(),serializedRootSig->GetBufferSize(),IID_PPV_ARGS(&mRootSignature)));

 

posted @ 2022-06-30 14:30  过往云烟吧  阅读(298)  评论(0)    收藏  举报