【问题标题】:Multiple constant buffer - register - dx12多个常量缓冲区 - 寄存器 - dx12
【发布时间】:2018-06-04 07:15:45
【问题描述】:

我通过该教程学习 dx12: https://www.braynzarsoft.net/viewtutorial/q16390-directx-12-constant-buffers-root-descriptor-tables#c0

我试图修改这一步以获得 2 个常量缓冲区(如果我理解得很好,那么一个寄存器 b0 和一个 b1)。

为此,我开始在我的根符号中说有 2 个参数:

// create root signature

// create a descriptor range (descriptor table) and fill it out
// this is a range of descriptors inside a descriptor heap
D3D12_DESCRIPTOR_RANGE  descriptorTableRanges[1]; // only one range right now
descriptorTableRanges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; // this is a range of constant buffer views (descriptors)
descriptorTableRanges[0].NumDescriptors = 2; // we only have one constant buffer, so the range is only 1
descriptorTableRanges[0].BaseShaderRegister = 0; // start index of the shader registers in the range
descriptorTableRanges[0].RegisterSpace = 0; // space 0. can usually be zero
descriptorTableRanges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; // this appends the range to the end of the root signature descriptor tables

// create a descriptor table
D3D12_ROOT_DESCRIPTOR_TABLE descriptorTable;
descriptorTable.NumDescriptorRanges = 0;// _countof(descriptorTableRanges); // we only have one range
descriptorTable.pDescriptorRanges = &descriptorTableRanges[0]; // the pointer to the beginning of our ranges array
D3D12_ROOT_DESCRIPTOR_TABLE descriptorTable2;
descriptorTable2.NumDescriptorRanges = 1;// _countof(descriptorTableRanges); // we only have one range
descriptorTable2.pDescriptorRanges = &descriptorTableRanges[0]; // the pointer to the beginning of our ranges array


// create a root parameter and fill it out
D3D12_ROOT_PARAMETER  rootParameters[2]; // only one parameter right now
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; // this is a descriptor table
rootParameters[0].DescriptorTable = descriptorTable; // this is our descriptor table for this root parameter
rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; // our pixel shader will be the only shader accessing this parameter for now
rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; // this is a descriptor table
rootParameters[1].DescriptorTable = descriptorTable2; // this is our descriptor table for this root parameter
rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; // our pixel shader will be the only shader accessing this parameter for now

但现在我无法将常量缓冲区链接到变量,我尝试在这部分代码中进行修改:

 // Create a constant buffer descriptor heap for each frame
// this is the descriptor heap that will store our constant buffer descriptor
for (int i = 0; i < frameBufferCount; ++i)
{
    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
    heapDesc.NumDescriptors = 1;
    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    hr = device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&mainDescriptorHeap[i]));
    if (FAILED(hr))
    {
        Running = false;
    }
}

// create the constant buffer resource heap
// We will update the constant buffer one or more times per frame, so we will use only an upload heap
// unlike previously we used an upload heap to upload the vertex and index data, and then copied over
// to a default heap. If you plan to use a resource for more than a couple frames, it is usually more
// efficient to copy to a default heap where it stays on the gpu. In this case, our constant buffer
// will be modified and uploaded at least once per frame, so we only use an upload heap

// create a resource heap, descriptor heap, and pointer to cbv for each frame
for (int i = 0; i < frameBufferCount; ++i)
{
    hr = device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), // this heap will be used to upload the constant buffer data
        D3D12_HEAP_FLAG_NONE, // no flags
        &CD3DX12_RESOURCE_DESC::Buffer(1024 * 64), // size of the resource heap. Must be a multiple of 64KB for single-textures and constant buffers
        D3D12_RESOURCE_STATE_GENERIC_READ, // will be data that is read from so we keep it in the generic read state
        nullptr, // we do not have use an optimized clear value for constant buffers
        IID_PPV_ARGS(&constantBufferUploadHeap[i]));
    constantBufferUploadHeap[i]->SetName(L"Constant Buffer Upload Resource Heap");

    D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
    cbvDesc.BufferLocation = constantBufferUploadHeap[i]->GetGPUVirtualAddress();
    cbvDesc.SizeInBytes = (sizeof(ConstantBuffer) + 255) & ~255;    // CB size is required to be 256-byte aligned.
    device->CreateConstantBufferView(&cbvDesc, mainDescriptorHeap[i]->GetCPUDescriptorHandleForHeapStart());

    ZeroMemory(&cbColorMultiplierData, sizeof(cbColorMultiplierData));

    CD3DX12_RANGE readRange(0, 0);    // We do not intend to read from this resource on the CPU. (End is less than or equal to begin)
    hr = constantBufferUploadHeap[i]->Map(0, &readRange, reinterpret_cast<void**>(&cbColorMultiplierGPUAddress[i]));
    memcpy(cbColorMultiplierGPUAddress[i], &cbColorMultiplierData, sizeof(cbColorMultiplierData));
}

谢谢

【问题讨论】:

    标签: c++ graphics directx directx-12


    【解决方案1】:

    您的根签名不正确,您正在尝试设置一个没有范围的描述符表。

    您有 3 种方法在根签名中注册常量缓冲区,使用根常量、使用根常量缓冲区和使用描述符表。前两个为每个根参数连接一个常量缓冲区,而第三个允许在单个表中设置多个常量缓冲区。

    在您的情况下,类型描述符表的单个根参数,具有引用 2 数组的单个范围足以让您绑定 2 个常量缓冲区。

    我建议您阅读如何在 HLSL 中声明 root signatures,以便更好地理解该概念以及它如何转换为 C++ 声明。

    至于操作常量缓冲区的运行时部分。你必须再次非常小心,它们在 d3d12 中没有生命周期管理,也没有像 d3d11 那样的驱动程序,如果不确保 GPU 已经使用以前的内容完成,你就不能就地更新一个恒定的缓冲存储器。解决方案通常是使用环形缓冲区来分配帧常量缓冲区,并使用栅栏来防止过早覆盖。

    我强烈建议您坚持使用 d3d11。 d3d12 不是它的替代品,它是为了克服一些性能问题,这些问题只有在极其复杂的渲染器中才能找到,并且如果您的应用程序不适合已经具备 GPU 和 d3d11 专业知识的人使用GTA V 的复杂程度(只是一个例子),你只是通过切换到 d3d12 来射击你的脚。

    【讨论】:

    • 谢谢,指出这些问题并给我指向 HLSL 根签名的链接 :) 我没有这个代码了,我实际上正在处理这个问题:github.com/Zeldarck/TrainingDx12 但我会回到这个问题上以后我开始图形编程,所以我对dx11也一无所知。感谢您的警告,但目前我更喜欢继续使用 dx12 :)
    • @Zeldarck 这是一个非常糟糕的主意,但如果你坚持下去。一遍又一遍地阅读 Microsoft 文档并避免使用教程,学习文档以建立自己对 API 的理解。并且在危险方面,尤其要注意 CPU/GPU 同步和内存管理。
    【解决方案2】:

    您真正的问题是:您在一个范围内定义了 2 条 CBV 描述符,然后在该范围内定义了 2 条描述符表。因此,您定义了 4 个 CBV 而不是 2 个,并且当您定义描述符堆时,您将 heapDesc.NumDescriptors 设置为 1 而不是 4,因为您在代码中定义了 4 个常量缓冲区描述符,而不是 2。

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-08
    • 2016-06-15
    • 1970-01-01
    • 2016-08-02
    • 1970-01-01
    • 2017-03-25
    相关资源
    最近更新 更多