fix(rhi): honor firstSet in set-aware d3d12 bindings

This commit is contained in:
2026-03-26 14:43:51 +08:00
parent 476a56724f
commit 9218ea20b5
6 changed files with 529 additions and 103 deletions

View File

@@ -17,6 +17,13 @@ class D3D12Device;
class D3D12PipelineLayout : public RHIPipelineLayout { class D3D12PipelineLayout : public RHIPipelineLayout {
public: public:
struct SetRootParameterMapping {
std::unordered_map<uint32_t, uint32_t> constantBufferRootIndices;
uint32_t shaderResourceTableRootIndex = UINT32_MAX;
uint32_t unorderedAccessTableRootIndex = UINT32_MAX;
uint32_t samplerTableRootIndex = UINT32_MAX;
};
D3D12PipelineLayout(); D3D12PipelineLayout();
~D3D12PipelineLayout() override; ~D3D12PipelineLayout() override;
@@ -27,6 +34,9 @@ public:
void* GetNativeHandle() override { return m_rootSignature.Get(); } void* GetNativeHandle() override { return m_rootSignature.Get(); }
ID3D12RootSignature* GetRootSignature() const { return m_rootSignature.Get(); } ID3D12RootSignature* GetRootSignature() const { return m_rootSignature.Get(); }
bool UsesSetLayouts() const { return m_desc.setLayoutCount > 0 && m_desc.setLayouts != nullptr; }
uint32_t GetSetLayoutCount() const { return m_desc.setLayoutCount; }
bool HasConstantBufferBinding(uint32_t binding) const; bool HasConstantBufferBinding(uint32_t binding) const;
uint32_t GetConstantBufferRootParameterIndex(uint32_t binding) const; uint32_t GetConstantBufferRootParameterIndex(uint32_t binding) const;
bool HasShaderResourceTable() const; bool HasShaderResourceTable() const;
@@ -35,10 +45,21 @@ public:
uint32_t GetUnorderedAccessTableRootParameterIndex() const; uint32_t GetUnorderedAccessTableRootParameterIndex() const;
bool HasSamplerTable() const; bool HasSamplerTable() const;
uint32_t GetSamplerTableRootParameterIndex() const; uint32_t GetSamplerTableRootParameterIndex() const;
bool HasConstantBufferBinding(uint32_t setIndex, uint32_t binding) const;
uint32_t GetConstantBufferRootParameterIndex(uint32_t setIndex, uint32_t binding) const;
bool HasShaderResourceTable(uint32_t setIndex) const;
uint32_t GetShaderResourceTableRootParameterIndex(uint32_t setIndex) const;
bool HasUnorderedAccessTable(uint32_t setIndex) const;
uint32_t GetUnorderedAccessTableRootParameterIndex(uint32_t setIndex) const;
bool HasSamplerTable(uint32_t setIndex) const;
uint32_t GetSamplerTableRootParameterIndex(uint32_t setIndex) const;
const RHIPipelineLayoutDesc& GetDesc() const { return m_desc; } const RHIPipelineLayoutDesc& GetDesc() const { return m_desc; }
private: private:
bool InitializeInternal(D3D12Device* device, const RHIPipelineLayoutDesc& desc); bool InitializeInternal(D3D12Device* device, const RHIPipelineLayoutDesc& desc);
bool InitializeSetAwareRootSignature();
bool InitializeFlatRootSignature();
ComPtr<ID3D12RootSignature> m_rootSignature; ComPtr<ID3D12RootSignature> m_rootSignature;
D3D12Device* m_device; D3D12Device* m_device;
@@ -47,6 +68,7 @@ private:
uint32_t m_shaderResourceTableRootIndex = UINT32_MAX; uint32_t m_shaderResourceTableRootIndex = UINT32_MAX;
uint32_t m_unorderedAccessTableRootIndex = UINT32_MAX; uint32_t m_unorderedAccessTableRootIndex = UINT32_MAX;
uint32_t m_samplerTableRootIndex = UINT32_MAX; uint32_t m_samplerTableRootIndex = UINT32_MAX;
std::vector<SetRootParameterMapping> m_setRootParameterMappings;
std::vector<DescriptorSetLayoutDesc> m_setLayouts; std::vector<DescriptorSetLayoutDesc> m_setLayouts;
std::vector<std::vector<DescriptorSetLayoutBinding>> m_setLayoutBindings; std::vector<std::vector<DescriptorSetLayoutBinding>> m_setLayoutBindings;
std::vector<D3D12_ROOT_PARAMETER> m_rootParameters; std::vector<D3D12_ROOT_PARAMETER> m_rootParameters;

View File

@@ -197,15 +197,23 @@ void D3D12CommandList::SetGraphicsDescriptorSets(
continue; continue;
} }
const uint32_t setIndex = firstSet + i;
D3D12DescriptorSet* d3d12Set = static_cast<D3D12DescriptorSet*>(descriptorSets[i]); D3D12DescriptorSet* d3d12Set = static_cast<D3D12DescriptorSet*>(descriptorSets[i]);
const DescriptorSetLayoutBinding* bindings = d3d12Set->GetBindings(); const DescriptorSetLayoutBinding* bindings = d3d12Set->GetBindings();
for (uint32_t bindingIndex = 0; bindingIndex < d3d12Set->GetBindingCount(); ++bindingIndex) { for (uint32_t bindingIndex = 0; bindingIndex < d3d12Set->GetBindingCount(); ++bindingIndex) {
const DescriptorSetLayoutBinding& binding = bindings[bindingIndex]; const DescriptorSetLayoutBinding& binding = bindings[bindingIndex];
const bool hasBinding = d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasConstantBufferBinding(setIndex, binding.binding)
: d3d12Layout->HasConstantBufferBinding(binding.binding);
const uint32_t rootParameterIndex = d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetConstantBufferRootParameterIndex(setIndex, binding.binding)
: d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding);
if (static_cast<DescriptorType>(binding.type) == DescriptorType::CBV && if (static_cast<DescriptorType>(binding.type) == DescriptorType::CBV &&
d3d12Layout->HasConstantBufferBinding(binding.binding) && hasBinding &&
d3d12Set->UploadConstantBuffer(binding.binding)) { d3d12Set->UploadConstantBuffer(binding.binding)) {
SetGraphicsRootConstantBufferView( SetGraphicsRootConstantBufferView(
d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding), rootParameterIndex,
d3d12Set->GetConstantBufferGPUAddress(binding.binding)); d3d12Set->GetConstantBufferGPUAddress(binding.binding));
} }
} }
@@ -216,28 +224,49 @@ void D3D12CommandList::SetGraphicsDescriptorSets(
} }
if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) { if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) {
if (d3d12Set->HasBindingType(DescriptorType::SRV) && d3d12Layout->HasShaderResourceTable()) { const bool hasSrvTable = d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasShaderResourceTable(setIndex)
: d3d12Layout->HasShaderResourceTable();
const bool hasUavTable = d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasUnorderedAccessTable(setIndex)
: d3d12Layout->HasUnorderedAccessTable();
if (d3d12Set->HasBindingType(DescriptorType::SRV) && hasSrvTable) {
const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV); const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV);
const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding);
if (gpuHandle.ptr != 0) { if (gpuHandle.ptr != 0) {
SetGraphicsRootDescriptorTable(d3d12Layout->GetShaderResourceTableRootParameterIndex(), gpuHandle); SetGraphicsRootDescriptorTable(
d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetShaderResourceTableRootParameterIndex(setIndex)
: d3d12Layout->GetShaderResourceTableRootParameterIndex(),
gpuHandle);
} }
} }
if (d3d12Set->HasBindingType(DescriptorType::UAV) && d3d12Layout->HasUnorderedAccessTable()) { if (d3d12Set->HasBindingType(DescriptorType::UAV) && hasUavTable) {
const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV); const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV);
const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding);
if (gpuHandle.ptr != 0) { if (gpuHandle.ptr != 0) {
SetGraphicsRootDescriptorTable(d3d12Layout->GetUnorderedAccessTableRootParameterIndex(), gpuHandle); SetGraphicsRootDescriptorTable(
d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetUnorderedAccessTableRootParameterIndex(setIndex)
: d3d12Layout->GetUnorderedAccessTableRootParameterIndex(),
gpuHandle);
} }
} }
} else if (heap->GetType() == DescriptorHeapType::Sampler && } else if (heap->GetType() == DescriptorHeapType::Sampler &&
HasSamplerBindings(d3d12Set) && HasSamplerBindings(d3d12Set) &&
d3d12Layout->HasSamplerTable()) { (d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasSamplerTable(setIndex)
: d3d12Layout->HasSamplerTable())) {
const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler); const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler);
const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding);
if (gpuHandle.ptr != 0) { if (gpuHandle.ptr != 0) {
SetGraphicsRootDescriptorTable(d3d12Layout->GetSamplerTableRootParameterIndex(), gpuHandle); SetGraphicsRootDescriptorTable(
d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetSamplerTableRootParameterIndex(setIndex)
: d3d12Layout->GetSamplerTableRootParameterIndex(),
gpuHandle);
} }
} }
} }
@@ -285,15 +314,23 @@ void D3D12CommandList::SetComputeDescriptorSets(
continue; continue;
} }
const uint32_t setIndex = firstSet + i;
D3D12DescriptorSet* d3d12Set = static_cast<D3D12DescriptorSet*>(descriptorSets[i]); D3D12DescriptorSet* d3d12Set = static_cast<D3D12DescriptorSet*>(descriptorSets[i]);
const DescriptorSetLayoutBinding* bindings = d3d12Set->GetBindings(); const DescriptorSetLayoutBinding* bindings = d3d12Set->GetBindings();
for (uint32_t bindingIndex = 0; bindingIndex < d3d12Set->GetBindingCount(); ++bindingIndex) { for (uint32_t bindingIndex = 0; bindingIndex < d3d12Set->GetBindingCount(); ++bindingIndex) {
const DescriptorSetLayoutBinding& binding = bindings[bindingIndex]; const DescriptorSetLayoutBinding& binding = bindings[bindingIndex];
const bool hasBinding = d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasConstantBufferBinding(setIndex, binding.binding)
: d3d12Layout->HasConstantBufferBinding(binding.binding);
const uint32_t rootParameterIndex = d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetConstantBufferRootParameterIndex(setIndex, binding.binding)
: d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding);
if (static_cast<DescriptorType>(binding.type) == DescriptorType::CBV && if (static_cast<DescriptorType>(binding.type) == DescriptorType::CBV &&
d3d12Layout->HasConstantBufferBinding(binding.binding) && hasBinding &&
d3d12Set->UploadConstantBuffer(binding.binding)) { d3d12Set->UploadConstantBuffer(binding.binding)) {
m_commandList->SetComputeRootConstantBufferView( m_commandList->SetComputeRootConstantBufferView(
d3d12Layout->GetConstantBufferRootParameterIndex(binding.binding), rootParameterIndex,
d3d12Set->GetConstantBufferGPUAddress(binding.binding)); d3d12Set->GetConstantBufferGPUAddress(binding.binding));
} }
} }
@@ -304,33 +341,48 @@ void D3D12CommandList::SetComputeDescriptorSets(
} }
if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) { if (heap->GetType() == DescriptorHeapType::CBV_SRV_UAV) {
if (d3d12Set->HasBindingType(DescriptorType::SRV) && d3d12Layout->HasShaderResourceTable()) { const bool hasSrvTable = d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasShaderResourceTable(setIndex)
: d3d12Layout->HasShaderResourceTable();
const bool hasUavTable = d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasUnorderedAccessTable(setIndex)
: d3d12Layout->HasUnorderedAccessTable();
if (d3d12Set->HasBindingType(DescriptorType::SRV) && hasSrvTable) {
const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV); const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV);
const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding);
if (gpuHandle.ptr != 0) { if (gpuHandle.ptr != 0) {
m_commandList->SetComputeRootDescriptorTable( m_commandList->SetComputeRootDescriptorTable(
d3d12Layout->GetShaderResourceTableRootParameterIndex(), d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetShaderResourceTableRootParameterIndex(setIndex)
: d3d12Layout->GetShaderResourceTableRootParameterIndex(),
gpuHandle); gpuHandle);
} }
} }
if (d3d12Set->HasBindingType(DescriptorType::UAV) && d3d12Layout->HasUnorderedAccessTable()) { if (d3d12Set->HasBindingType(DescriptorType::UAV) && hasUavTable) {
const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV); const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV);
const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding);
if (gpuHandle.ptr != 0) { if (gpuHandle.ptr != 0) {
m_commandList->SetComputeRootDescriptorTable( m_commandList->SetComputeRootDescriptorTable(
d3d12Layout->GetUnorderedAccessTableRootParameterIndex(), d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetUnorderedAccessTableRootParameterIndex(setIndex)
: d3d12Layout->GetUnorderedAccessTableRootParameterIndex(),
gpuHandle); gpuHandle);
} }
} }
} else if (heap->GetType() == DescriptorHeapType::Sampler && } else if (heap->GetType() == DescriptorHeapType::Sampler &&
HasSamplerBindings(d3d12Set) && HasSamplerBindings(d3d12Set) &&
d3d12Layout->HasSamplerTable()) { (d3d12Layout->UsesSetLayouts()
? d3d12Layout->HasSamplerTable(setIndex)
: d3d12Layout->HasSamplerTable())) {
const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler); const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler);
const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding); const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding);
if (gpuHandle.ptr != 0) { if (gpuHandle.ptr != 0) {
m_commandList->SetComputeRootDescriptorTable( m_commandList->SetComputeRootDescriptorTable(
d3d12Layout->GetSamplerTableRootParameterIndex(), d3d12Layout->UsesSetLayouts()
? d3d12Layout->GetSamplerTableRootParameterIndex(setIndex)
: d3d12Layout->GetSamplerTableRootParameterIndex(),
gpuHandle); gpuHandle);
} }
} }

View File

@@ -1,6 +1,8 @@
#include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h" #include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h"
#include "XCEngine/RHI/D3D12/D3D12Device.h" #include "XCEngine/RHI/D3D12/D3D12Device.h"
#include <algorithm>
namespace XCEngine { namespace XCEngine {
namespace RHI { namespace RHI {
@@ -28,6 +30,52 @@ void AccumulateDescriptorCounts(const DescriptorSetLayoutDesc& setLayout, RHIPip
} }
} }
uint32_t CountBindingEntriesOfType(const DescriptorSetLayoutDesc& setLayout, DescriptorType type) {
uint32_t count = 0;
for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) {
if (static_cast<DescriptorType>(setLayout.bindings[bindingIndex].type) == type) {
++count;
}
}
return count;
}
uint32_t SumDescriptorCountOfType(const DescriptorSetLayoutDesc& setLayout, DescriptorType type) {
uint32_t count = 0;
for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) {
const DescriptorSetLayoutBinding& binding = setLayout.bindings[bindingIndex];
if (static_cast<DescriptorType>(binding.type) == type) {
count += binding.count > 0 ? binding.count : 1u;
}
}
return count;
}
std::vector<const DescriptorSetLayoutBinding*> GatherBindingsOfTypeSorted(
const DescriptorSetLayoutDesc& setLayout,
DescriptorType type) {
std::vector<const DescriptorSetLayoutBinding*> bindings;
bindings.reserve(setLayout.bindingCount);
for (uint32_t bindingIndex = 0; bindingIndex < setLayout.bindingCount; ++bindingIndex) {
const DescriptorSetLayoutBinding& binding = setLayout.bindings[bindingIndex];
if (static_cast<DescriptorType>(binding.type) == type) {
bindings.push_back(&binding);
}
}
std::sort(
bindings.begin(),
bindings.end(),
[](const DescriptorSetLayoutBinding* left, const DescriptorSetLayoutBinding* right) {
return left->binding < right->binding;
});
return bindings;
}
} // namespace } // namespace
D3D12PipelineLayout::D3D12PipelineLayout() D3D12PipelineLayout::D3D12PipelineLayout()
@@ -88,7 +136,315 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel
m_shaderResourceTableRootIndex = UINT32_MAX; m_shaderResourceTableRootIndex = UINT32_MAX;
m_unorderedAccessTableRootIndex = UINT32_MAX; m_unorderedAccessTableRootIndex = UINT32_MAX;
m_samplerTableRootIndex = UINT32_MAX; m_samplerTableRootIndex = UINT32_MAX;
m_setRootParameterMappings.clear();
if (UsesSetLayouts()) {
if (!InitializeSetAwareRootSignature()) {
return false;
}
} else {
if (!InitializeFlatRootSignature()) {
return false;
}
}
D3D12_ROOT_SIGNATURE_DESC rootSigDesc = D3D12RootSignature::CreateDesc(
m_rootParameters.empty() ? nullptr : m_rootParameters.data(),
static_cast<uint32_t>(m_rootParameters.size()),
nullptr, 0,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ID3D12Device* d3d12Device = device->GetDevice();
if (!d3d12Device) {
return false;
}
ID3DBlob* signature = nullptr;
ID3DBlob* error = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
if (FAILED(hr)) {
if (error) {
error->Release();
}
return false;
}
hr = d3d12Device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature));
signature->Release();
if (FAILED(hr)) {
return false;
}
return true;
}
void D3D12PipelineLayout::Shutdown() {
m_rootSignature.Reset();
m_desc = {};
m_rootParameters.clear();
m_descriptorRanges.clear();
m_constantBufferRootIndices.clear();
m_shaderResourceTableRootIndex = UINT32_MAX;
m_unorderedAccessTableRootIndex = UINT32_MAX;
m_samplerTableRootIndex = UINT32_MAX;
m_setRootParameterMappings.clear();
m_setLayouts.clear();
m_setLayoutBindings.clear();
m_device = nullptr;
}
bool D3D12PipelineLayout::HasConstantBufferBinding(uint32_t binding) const {
if (UsesSetLayouts()) {
return HasConstantBufferBinding(0, binding);
}
return m_constantBufferRootIndices.find(binding) != m_constantBufferRootIndices.end();
}
uint32_t D3D12PipelineLayout::GetConstantBufferRootParameterIndex(uint32_t binding) const {
if (UsesSetLayouts()) {
return GetConstantBufferRootParameterIndex(0, binding);
}
auto it = m_constantBufferRootIndices.find(binding);
if (it != m_constantBufferRootIndices.end()) {
return it->second;
}
return UINT32_MAX;
}
bool D3D12PipelineLayout::HasShaderResourceTable() const {
if (UsesSetLayouts()) {
return HasShaderResourceTable(0);
}
return m_shaderResourceTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetShaderResourceTableRootParameterIndex() const {
if (UsesSetLayouts()) {
return GetShaderResourceTableRootParameterIndex(0);
}
return m_shaderResourceTableRootIndex;
}
bool D3D12PipelineLayout::HasUnorderedAccessTable() const {
if (UsesSetLayouts()) {
return HasUnorderedAccessTable(0);
}
return m_unorderedAccessTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetUnorderedAccessTableRootParameterIndex() const {
if (UsesSetLayouts()) {
return GetUnorderedAccessTableRootParameterIndex(0);
}
return m_unorderedAccessTableRootIndex;
}
bool D3D12PipelineLayout::HasSamplerTable() const {
if (UsesSetLayouts()) {
return HasSamplerTable(0);
}
return m_samplerTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetSamplerTableRootParameterIndex() const {
if (UsesSetLayouts()) {
return GetSamplerTableRootParameterIndex(0);
}
return m_samplerTableRootIndex;
}
bool D3D12PipelineLayout::HasConstantBufferBinding(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0 && HasConstantBufferBinding(binding);
}
if (setIndex >= m_setRootParameterMappings.size()) {
return false;
}
return m_setRootParameterMappings[setIndex].constantBufferRootIndices.find(binding) !=
m_setRootParameterMappings[setIndex].constantBufferRootIndices.end();
}
uint32_t D3D12PipelineLayout::GetConstantBufferRootParameterIndex(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? GetConstantBufferRootParameterIndex(binding) : UINT32_MAX;
}
if (setIndex >= m_setRootParameterMappings.size()) {
return UINT32_MAX;
}
const auto& rootIndices = m_setRootParameterMappings[setIndex].constantBufferRootIndices;
auto it = rootIndices.find(binding);
if (it != rootIndices.end()) {
return it->second;
}
return UINT32_MAX;
}
bool D3D12PipelineLayout::HasShaderResourceTable(uint32_t setIndex) const {
if (!UsesSetLayouts()) {
return setIndex == 0 && HasShaderResourceTable();
}
return setIndex < m_setRootParameterMappings.size() &&
m_setRootParameterMappings[setIndex].shaderResourceTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetShaderResourceTableRootParameterIndex(uint32_t setIndex) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? GetShaderResourceTableRootParameterIndex() : UINT32_MAX;
}
return setIndex < m_setRootParameterMappings.size()
? m_setRootParameterMappings[setIndex].shaderResourceTableRootIndex
: UINT32_MAX;
}
bool D3D12PipelineLayout::HasUnorderedAccessTable(uint32_t setIndex) const {
if (!UsesSetLayouts()) {
return setIndex == 0 && HasUnorderedAccessTable();
}
return setIndex < m_setRootParameterMappings.size() &&
m_setRootParameterMappings[setIndex].unorderedAccessTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetUnorderedAccessTableRootParameterIndex(uint32_t setIndex) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? GetUnorderedAccessTableRootParameterIndex() : UINT32_MAX;
}
return setIndex < m_setRootParameterMappings.size()
? m_setRootParameterMappings[setIndex].unorderedAccessTableRootIndex
: UINT32_MAX;
}
bool D3D12PipelineLayout::HasSamplerTable(uint32_t setIndex) const {
if (!UsesSetLayouts()) {
return setIndex == 0 && HasSamplerTable();
}
return setIndex < m_setRootParameterMappings.size() &&
m_setRootParameterMappings[setIndex].samplerTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetSamplerTableRootParameterIndex(uint32_t setIndex) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? GetSamplerTableRootParameterIndex() : UINT32_MAX;
}
return setIndex < m_setRootParameterMappings.size()
? m_setRootParameterMappings[setIndex].samplerTableRootIndex
: UINT32_MAX;
}
bool D3D12PipelineLayout::InitializeSetAwareRootSignature() {
const uint32_t setCount = m_desc.setLayoutCount;
m_setRootParameterMappings.assign(setCount, SetRootParameterMapping{});
uint32_t rootParameterCount = 0;
uint32_t descriptorRangeCount = 0;
for (uint32_t setIndex = 0; setIndex < setCount; ++setIndex) {
const DescriptorSetLayoutDesc& setLayout = m_desc.setLayouts[setIndex];
rootParameterCount += CountBindingEntriesOfType(setLayout, DescriptorType::CBV);
rootParameterCount += SumDescriptorCountOfType(setLayout, DescriptorType::SRV) > 0 ? 1u : 0u;
rootParameterCount += SumDescriptorCountOfType(setLayout, DescriptorType::UAV) > 0 ? 1u : 0u;
rootParameterCount += SumDescriptorCountOfType(setLayout, DescriptorType::Sampler) > 0 ? 1u : 0u;
descriptorRangeCount += CountBindingEntriesOfType(setLayout, DescriptorType::SRV);
descriptorRangeCount += CountBindingEntriesOfType(setLayout, DescriptorType::UAV);
descriptorRangeCount += CountBindingEntriesOfType(setLayout, DescriptorType::Sampler);
}
m_rootParameters.reserve(rootParameterCount);
m_descriptorRanges.reserve(descriptorRangeCount);
uint32_t rootIndex = 0;
uint32_t nextCBVRegister = 0;
uint32_t nextSRVRegister = 0;
uint32_t nextUAVRegister = 0;
uint32_t nextSamplerRegister = 0;
for (uint32_t setIndex = 0; setIndex < setCount; ++setIndex) {
const DescriptorSetLayoutDesc& setLayout = m_desc.setLayouts[setIndex];
SetRootParameterMapping& setMapping = m_setRootParameterMappings[setIndex];
const auto cbvBindings = GatherBindingsOfTypeSorted(setLayout, DescriptorType::CBV);
for (const DescriptorSetLayoutBinding* binding : cbvBindings) {
m_rootParameters.push_back(D3D12RootSignature::CreateCBV(nextCBVRegister, ShaderVisibility::All, 0));
setMapping.constantBufferRootIndices[binding->binding] = rootIndex++;
nextCBVRegister += binding->count > 0 ? binding->count : 1u;
}
const auto appendDescriptorTable =
[this, &rootIndex](
const DescriptorSetLayoutDesc& layout,
DescriptorType type,
D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
uint32_t& nextRegister,
uint32_t& rootParameterIndex) {
const auto bindings = GatherBindingsOfTypeSorted(layout, type);
if (bindings.empty()) {
return;
}
const size_t rangeStart = m_descriptorRanges.size();
for (const DescriptorSetLayoutBinding* binding : bindings) {
const uint32_t descriptorCount = binding->count > 0 ? binding->count : 1u;
m_descriptorRanges.push_back(
D3D12RootSignature::CreateDescriptorRange(
rangeType,
nextRegister,
descriptorCount,
0));
nextRegister += descriptorCount;
}
m_rootParameters.push_back(
D3D12RootSignature::CreateDescriptorTable(
static_cast<uint32_t>(bindings.size()),
m_descriptorRanges.data() + rangeStart,
ShaderVisibility::All));
rootParameterIndex = rootIndex++;
};
appendDescriptorTable(
setLayout,
DescriptorType::SRV,
D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
nextSRVRegister,
setMapping.shaderResourceTableRootIndex);
appendDescriptorTable(
setLayout,
DescriptorType::UAV,
D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
nextUAVRegister,
setMapping.unorderedAccessTableRootIndex);
appendDescriptorTable(
setLayout,
DescriptorType::Sampler,
D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,
nextSamplerRegister,
setMapping.samplerTableRootIndex);
}
return true;
}
bool D3D12PipelineLayout::InitializeFlatRootSignature() {
const RHIPipelineLayoutDesc& normalizedDesc = m_desc; const RHIPipelineLayoutDesc& normalizedDesc = m_desc;
const uint32_t rootParameterCount = const uint32_t rootParameterCount =
@@ -143,87 +499,8 @@ bool D3D12PipelineLayout::InitializeInternal(D3D12Device* device, const RHIPipel
rootIndex++; rootIndex++;
} }
D3D12_ROOT_SIGNATURE_DESC rootSigDesc = D3D12RootSignature::CreateDesc(
m_rootParameters.empty() ? nullptr : m_rootParameters.data(),
static_cast<uint32_t>(m_rootParameters.size()),
nullptr, 0,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ID3D12Device* d3d12Device = device->GetDevice();
if (!d3d12Device) {
return false;
}
ID3DBlob* signature = nullptr;
ID3DBlob* error = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
if (FAILED(hr)) {
if (error) {
error->Release();
}
return false;
}
hr = d3d12Device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature));
signature->Release();
if (FAILED(hr)) {
return false;
}
return true; return true;
} }
void D3D12PipelineLayout::Shutdown() {
m_rootSignature.Reset();
m_desc = {};
m_rootParameters.clear();
m_descriptorRanges.clear();
m_constantBufferRootIndices.clear();
m_shaderResourceTableRootIndex = UINT32_MAX;
m_unorderedAccessTableRootIndex = UINT32_MAX;
m_samplerTableRootIndex = UINT32_MAX;
m_setLayouts.clear();
m_setLayoutBindings.clear();
m_device = nullptr;
}
bool D3D12PipelineLayout::HasConstantBufferBinding(uint32_t binding) const {
return m_constantBufferRootIndices.find(binding) != m_constantBufferRootIndices.end();
}
uint32_t D3D12PipelineLayout::GetConstantBufferRootParameterIndex(uint32_t binding) const {
auto it = m_constantBufferRootIndices.find(binding);
if (it != m_constantBufferRootIndices.end()) {
return it->second;
}
return UINT32_MAX;
}
bool D3D12PipelineLayout::HasShaderResourceTable() const {
return m_shaderResourceTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetShaderResourceTableRootParameterIndex() const {
return m_shaderResourceTableRootIndex;
}
bool D3D12PipelineLayout::HasUnorderedAccessTable() const {
return m_unorderedAccessTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetUnorderedAccessTableRootParameterIndex() const {
return m_unorderedAccessTableRootIndex;
}
bool D3D12PipelineLayout::HasSamplerTable() const {
return m_samplerTableRootIndex != UINT32_MAX;
}
uint32_t D3D12PipelineLayout::GetSamplerTableRootParameterIndex() const {
return m_samplerTableRootIndex;
}
} // namespace RHI } // namespace RHI
} // namespace XCEngine } // namespace XCEngine

View File

@@ -44,6 +44,8 @@ struct MatrixBufferData {
constexpr float kSphereRadius = 1.0f; constexpr float kSphereRadius = 1.0f;
constexpr int kSphereSegments = 32; constexpr int kSphereSegments = 32;
constexpr float kPi = 3.14159265358979323846f; constexpr float kPi = 3.14159265358979323846f;
constexpr uint32_t kSphereDescriptorFirstSet = 1;
constexpr uint32_t kSphereDescriptorSetCount = 4;
std::filesystem::path GetExecutableDirectory() { std::filesystem::path GetExecutableDirectory() {
char exePath[MAX_PATH] = {}; char exePath[MAX_PATH] = {};
@@ -452,10 +454,15 @@ void SphereTest::InitializeSphereResources() {
ASSERT_NE(mSamplerSet, nullptr); ASSERT_NE(mSamplerSet, nullptr);
mSamplerSet->UpdateSampler(0, mSampler); mSamplerSet->UpdateSampler(0, mSampler);
DescriptorSetLayoutDesc setLayouts[kSphereDescriptorSetCount] = {};
// Reserve set0 so the integration test exercises non-zero firstSet binding.
setLayouts[1] = constantLayoutDesc;
setLayouts[2] = textureLayoutDesc;
setLayouts[3] = samplerLayoutDesc;
RHIPipelineLayoutDesc pipelineLayoutDesc = {}; RHIPipelineLayoutDesc pipelineLayoutDesc = {};
pipelineLayoutDesc.constantBufferCount = 1; pipelineLayoutDesc.setLayouts = setLayouts;
pipelineLayoutDesc.textureCount = 1; pipelineLayoutDesc.setLayoutCount = kSphereDescriptorSetCount;
pipelineLayoutDesc.samplerCount = 1;
mPipelineLayout = GetDevice()->CreatePipelineLayout(pipelineLayoutDesc); mPipelineLayout = GetDevice()->CreatePipelineLayout(pipelineLayoutDesc);
ASSERT_NE(mPipelineLayout, nullptr); ASSERT_NE(mPipelineLayout, nullptr);
@@ -584,7 +591,7 @@ void SphereTest::RenderFrame() {
cmdList->SetPipelineState(mPipelineState); cmdList->SetPipelineState(mPipelineState);
RHIDescriptorSet* descriptorSets[] = { mConstantSet, mTextureSet, mSamplerSet }; RHIDescriptorSet* descriptorSets[] = { mConstantSet, mTextureSet, mSamplerSet };
cmdList->SetGraphicsDescriptorSets(0, 3, descriptorSets, mPipelineLayout); cmdList->SetGraphicsDescriptorSets(kSphereDescriptorFirstSet, 3, descriptorSets, mPipelineLayout);
cmdList->SetPrimitiveTopology(PrimitiveTopology::TriangleList); cmdList->SetPrimitiveTopology(PrimitiveTopology::TriangleList);
RHIResourceView* vertexBuffers[] = { mVertexBufferView }; RHIResourceView* vertexBuffers[] = { mVertexBufferView };

View File

@@ -283,10 +283,12 @@ TEST_P(RHITestFixture, PipelineLayout_D3D12InfersBindingClassesFromSetLayouts) {
ASSERT_NE(layout, nullptr); ASSERT_NE(layout, nullptr);
auto* d3d12Layout = static_cast<D3D12PipelineLayout*>(layout); auto* d3d12Layout = static_cast<D3D12PipelineLayout*>(layout);
EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(0)); EXPECT_TRUE(d3d12Layout->UsesSetLayouts());
EXPECT_TRUE(d3d12Layout->HasShaderResourceTable()); EXPECT_EQ(d3d12Layout->GetSetLayoutCount(), 4u);
EXPECT_TRUE(d3d12Layout->HasUnorderedAccessTable()); EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(0, 0));
EXPECT_TRUE(d3d12Layout->HasSamplerTable()); EXPECT_TRUE(d3d12Layout->HasShaderResourceTable(1));
EXPECT_TRUE(d3d12Layout->HasUnorderedAccessTable(2));
EXPECT_TRUE(d3d12Layout->HasSamplerTable(3));
EXPECT_EQ(d3d12Layout->GetDesc().constantBufferCount, 1u); EXPECT_EQ(d3d12Layout->GetDesc().constantBufferCount, 1u);
EXPECT_EQ(d3d12Layout->GetDesc().textureCount, 1u); EXPECT_EQ(d3d12Layout->GetDesc().textureCount, 1u);
EXPECT_EQ(d3d12Layout->GetDesc().uavCount, 1u); EXPECT_EQ(d3d12Layout->GetDesc().uavCount, 1u);
@@ -295,3 +297,68 @@ TEST_P(RHITestFixture, PipelineLayout_D3D12InfersBindingClassesFromSetLayouts) {
layout->Shutdown(); layout->Shutdown();
delete layout; delete layout;
} }
TEST_P(RHITestFixture, PipelineLayout_D3D12SeparatesOverlappingBindingsAcrossSetSlots) {
if (GetBackendType() != RHIType::D3D12) {
GTEST_SKIP() << "D3D12-specific root parameter verification";
}
DescriptorSetLayoutBinding set0Bindings[2] = {};
set0Bindings[0].binding = 0;
set0Bindings[0].type = static_cast<uint32_t>(DescriptorType::CBV);
set0Bindings[0].count = 1;
set0Bindings[1].binding = 0;
set0Bindings[1].type = static_cast<uint32_t>(DescriptorType::SRV);
set0Bindings[1].count = 1;
DescriptorSetLayoutBinding set1Bindings[2] = {};
set1Bindings[0].binding = 0;
set1Bindings[0].type = static_cast<uint32_t>(DescriptorType::CBV);
set1Bindings[0].count = 1;
set1Bindings[1].binding = 0;
set1Bindings[1].type = static_cast<uint32_t>(DescriptorType::SRV);
set1Bindings[1].count = 1;
DescriptorSetLayoutBinding set2Bindings[1] = {};
set2Bindings[0].binding = 0;
set2Bindings[0].type = static_cast<uint32_t>(DescriptorType::Sampler);
set2Bindings[0].count = 1;
DescriptorSetLayoutDesc setLayouts[3] = {};
setLayouts[0].bindings = set0Bindings;
setLayouts[0].bindingCount = 2;
setLayouts[1].bindings = set1Bindings;
setLayouts[1].bindingCount = 2;
setLayouts[2].bindings = set2Bindings;
setLayouts[2].bindingCount = 1;
RHIPipelineLayoutDesc desc = {};
desc.setLayouts = setLayouts;
desc.setLayoutCount = 3;
RHIPipelineLayout* layout = GetDevice()->CreatePipelineLayout(desc);
ASSERT_NE(layout, nullptr);
auto* d3d12Layout = static_cast<D3D12PipelineLayout*>(layout);
EXPECT_TRUE(d3d12Layout->UsesSetLayouts());
EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(0, 0));
EXPECT_TRUE(d3d12Layout->HasConstantBufferBinding(1, 0));
EXPECT_FALSE(d3d12Layout->HasConstantBufferBinding(2, 0));
EXPECT_NE(
d3d12Layout->GetConstantBufferRootParameterIndex(0, 0),
d3d12Layout->GetConstantBufferRootParameterIndex(1, 0));
EXPECT_TRUE(d3d12Layout->HasShaderResourceTable(0));
EXPECT_TRUE(d3d12Layout->HasShaderResourceTable(1));
EXPECT_FALSE(d3d12Layout->HasShaderResourceTable(2));
EXPECT_NE(
d3d12Layout->GetShaderResourceTableRootParameterIndex(0),
d3d12Layout->GetShaderResourceTableRootParameterIndex(1));
EXPECT_FALSE(d3d12Layout->HasSamplerTable(0));
EXPECT_FALSE(d3d12Layout->HasSamplerTable(1));
EXPECT_TRUE(d3d12Layout->HasSamplerTable(2));
layout->Shutdown();
delete layout;
}

View File

@@ -174,6 +174,7 @@ tests/RHI/integration/
- `sphere_opengl.ppm` - `sphere_opengl.ppm`
5. 两个后端都必须与同一张 `GT.ppm` 做比对。 5. 两个后端都必须与同一张 `GT.ppm` 做比对。
6. 新测试如果暴露抽象层缺口,应先补 RHI再补测试。 6. 新测试如果暴露抽象层缺口,应先补 RHI再补测试。
7. 至少保留一个场景覆盖 `firstSet != 0` 的描述符绑定路径,当前由 `sphere` 负责验证。
### 6.3 命名 ### 6.3 命名