我用该教程学习DX12:https://www.braynzarsoft.net/viewtutorial/q16390-directx-12-constant-buffers-root-descriptor-tables#c0
我试图修改此步骤以获得2个常数缓冲区(因此,如果我理解得很好,请寄存器B0和A 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));
}
谢谢
您的根签名不正确,您正在尝试设置一个没有范围的描述符表。
您有3种方法可以在根签名中注册常数,带有根常数,带根常数缓冲区和描述符表。前两个连接每个根参数的一个常数缓冲区,而第三个允许在单个表中设置多个常数缓冲区。
在您的情况下,类型描述符表的单个根参数,带有2个数组的单个范围足以让您绑定2个常数缓冲区。
我建议您阅读如何在HLSL中声明根签名,以更好地了解该概念及其如何转换为C 声明。
至于操纵常数缓冲区的运行时部分。您必须再次非常小心,它们在D3D12中没有终身时间管理,也不是像D3D11一样的驱动程序,如果不确保使用以前的内容已经完成GPU,则无法在适当的位置更新恒定的缓冲存储器。该解决方案通常是使用环缓冲区来分配框架常数缓冲区,并使用围栏来防止您过早覆盖。
我强烈建议您坚持使用D3D11。D3D12并不是它的替代品,它是为了克服某些性能问题,这些问题只能在极其复杂的渲染器中找到,并且已经被GPU专业知识和D3D11的人使用,如果您的应用程序不是为了GTA V的复杂性水平(只是一个示例),您只会通过切换到D3D12。
您的真正问题是:您在一个范围内定义了2个CBV描述符,而不是定义了具有此范围的2个描述符表。因此,您定义了4片CBV,而不是2片,并且当定义描述符堆时,将heapdesc.numdescriptor设置为1而不是4,因为您在代码中定义了4个常数buffer描述符,而不是2。