RHI: Add DescriptorSet abstraction for D3D12 and OpenGL backends

- Add RHIDescriptorSet base class with Update/UpdateSampler/GetNativeHandle
- Add RHIDescriptorPool with AllocateSet/FreeSet methods
- Add SetGraphicsDescriptorSets/SetComputeDescriptorSets to RHICommandList
- Implement D3D12DescriptorSet using descriptor heap allocation
- Implement OpenGLDescriptorSet using TextureUnitAllocator
- Add CreateDescriptorPool/CreateDescriptorSet factory methods to RHIDevice
- Fix unit test SetVertexBuffer -> SetVertexBuffers API
- Add SetVertexBuffer convenience method for D3D12 backward compatibility
- Update CMakeLists.txt with new source files
This commit is contained in:
2026-03-25 00:26:16 +08:00
parent c5c43ae7aa
commit c6fe9547aa
22 changed files with 688 additions and 1 deletions

View File

@@ -5,6 +5,7 @@
#include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h"
#include "XCEngine/RHI/D3D12/D3D12RenderPass.h"
#include "XCEngine/RHI/D3D12/D3D12Framebuffer.h"
#include "XCEngine/RHI/D3D12/D3D12DescriptorSet.h"
namespace XCEngine {
namespace RHI {
@@ -214,6 +215,56 @@ void D3D12CommandList::SetPipelineState(ID3D12PipelineState* pso) {
SetPipelineStateInternal(pso);
}
void D3D12CommandList::SetGraphicsDescriptorSets(
uint32_t firstSet,
uint32_t count,
RHIDescriptorSet** descriptorSets,
RHIPipelineLayout* pipelineLayout) {
if (pipelineLayout == nullptr || descriptorSets == nullptr) {
return;
}
D3D12PipelineLayout* d3d12Layout = static_cast<D3D12PipelineLayout*>(pipelineLayout);
SetPipelineLayout(d3d12Layout);
for (uint32_t i = 0; i < count; ++i) {
if (descriptorSets[i] == nullptr) {
continue;
}
D3D12DescriptorSet* d3d12Set = static_cast<D3D12DescriptorSet*>(descriptorSets[i]);
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandle();
uint32_t rootIndex = firstSet + i;
SetGraphicsRootDescriptorTable(rootIndex, gpuHandle);
}
}
void D3D12CommandList::SetComputeDescriptorSets(
uint32_t firstSet,
uint32_t count,
RHIDescriptorSet** descriptorSets,
RHIPipelineLayout* pipelineLayout) {
if (pipelineLayout == nullptr || descriptorSets == nullptr) {
return;
}
D3D12PipelineLayout* d3d12Layout = static_cast<D3D12PipelineLayout*>(pipelineLayout);
SetPipelineLayout(d3d12Layout);
for (uint32_t i = 0; i < count; ++i) {
if (descriptorSets[i] == nullptr) {
continue;
}
D3D12DescriptorSet* d3d12Set = static_cast<D3D12DescriptorSet*>(descriptorSets[i]);
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandle();
uint32_t rootIndex = firstSet + i;
m_commandList->SetComputeRootDescriptorTable(rootIndex, gpuHandle);
}
}
void D3D12CommandList::TransitionBarrierInternal(ID3D12Resource* resource, ResourceStates stateBefore, ResourceStates stateAfter, uint32_t subresource) {
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
@@ -434,6 +485,14 @@ void D3D12CommandList::SetVertexBuffersInternal(uint32_t startSlot, uint32_t cou
m_commandList->IASetVertexBuffers(startSlot, count, views);
}
void D3D12CommandList::SetVertexBuffer(uint32_t slot, ID3D12Resource* resource, uint64_t offset, uint32_t stride) {
D3D12_VERTEX_BUFFER_VIEW view = {};
view.BufferLocation = resource->GetGPUVirtualAddress() + offset;
view.SizeInBytes = static_cast<UINT>(resource->GetDesc().Width) - static_cast<UINT>(offset);
view.StrideInBytes = stride;
SetVertexBuffersInternal(slot, 1, &view);
}
void D3D12CommandList::SetIndexBuffer(RHIResourceView* buffer, uint64_t offset) {
if (!buffer || !buffer->IsValid()) return;
D3D12ResourceView* view = static_cast<D3D12ResourceView*>(buffer);

View File

@@ -1,4 +1,5 @@
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
#include "XCEngine/RHI/D3D12/D3D12DescriptorSet.h"
namespace XCEngine {
namespace RHI {
@@ -88,5 +89,41 @@ D3D12_DESCRIPTOR_HEAP_DESC D3D12DescriptorHeap::CreateDesc(DescriptorHeapType ty
return desc;
}
RHIDescriptorSet* D3D12DescriptorHeap::AllocateSet(const DescriptorSetLayoutDesc& layout) {
uint32_t requiredDescriptors = 0;
for (uint32_t i = 0; i < layout.bindingCount; ++i) {
requiredDescriptors += layout.bindings[i].count;
}
if (m_allocatedSets.size() >= m_numDescriptors) {
return nullptr;
}
D3D12DescriptorSet* newSet = new D3D12DescriptorSet();
uint32_t offset = static_cast<uint32_t>(m_allocatedSets.size());
if (!newSet->Initialize(this, offset, requiredDescriptors, layout)) {
delete newSet;
return nullptr;
}
m_allocatedSets.push_back(newSet);
return newSet;
}
void D3D12DescriptorHeap::FreeSet(RHIDescriptorSet* set) {
if (set == nullptr) {
return;
}
for (auto it = m_allocatedSets.begin(); it != m_allocatedSets.end(); ++it) {
if (*it == set) {
m_allocatedSets.erase(it);
delete set;
return;
}
}
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,69 @@
#include "XCEngine/RHI/D3D12/D3D12DescriptorSet.h"
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
namespace XCEngine {
namespace RHI {
D3D12DescriptorSet::D3D12DescriptorSet()
: m_heap(nullptr)
, m_offset(0)
, m_count(0)
, m_bindingCount(0)
, m_bindings(nullptr) {
}
D3D12DescriptorSet::~D3D12DescriptorSet() {
Shutdown();
}
bool D3D12DescriptorSet::Initialize(D3D12DescriptorHeap* heap, uint32_t offset, uint32_t count, const DescriptorSetLayoutDesc& layout) {
m_heap = heap;
m_offset = offset;
m_count = count;
m_bindingCount = layout.bindingCount;
if (layout.bindingCount > 0 && layout.bindings != nullptr) {
m_bindings = new DescriptorSetLayoutBinding[layout.bindingCount];
for (uint32_t i = 0; i < layout.bindingCount; ++i) {
m_bindings[i] = layout.bindings[i];
}
}
return true;
}
void D3D12DescriptorSet::Shutdown() {
m_heap = nullptr;
m_offset = 0;
m_count = 0;
m_bindingCount = 0;
if (m_bindings != nullptr) {
delete[] m_bindings;
m_bindings = nullptr;
}
}
void D3D12DescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
(void)offset;
(void)view;
}
void D3D12DescriptorSet::UpdateSampler(uint32_t offset, RHISampler* sampler) {
(void)offset;
(void)sampler;
}
void* D3D12DescriptorSet::GetNativeHandle() {
return this;
}
D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorSet::GetGPUHandle(uint32_t index) const {
if (m_heap == nullptr) {
return D3D12_GPU_DESCRIPTOR_HANDLE{0};
}
GPUDescriptorHandle handle = m_heap->GetGPUDescriptorHandle(m_offset + index);
return D3D12_GPU_DESCRIPTOR_HANDLE{ handle.ptr };
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -4,6 +4,7 @@
#include "XCEngine/RHI/D3D12/D3D12CommandAllocator.h"
#include "XCEngine/RHI/D3D12/D3D12Fence.h"
#include "XCEngine/RHI/D3D12/D3D12DescriptorHeap.h"
#include "XCEngine/RHI/D3D12/D3D12DescriptorSet.h"
#include "XCEngine/RHI/D3D12/D3D12QueryHeap.h"
#include "XCEngine/RHI/D3D12/D3D12RootSignature.h"
#include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h"
@@ -324,6 +325,24 @@ RHISampler* D3D12Device::CreateSampler(const SamplerDesc& desc) {
return nullptr;
}
RHIDescriptorPool* D3D12Device::CreateDescriptorPool(const DescriptorPoolDesc& desc) {
auto* pool = new D3D12DescriptorHeap();
DescriptorPoolDesc poolDesc = desc;
poolDesc.device = m_device.Get();
if (pool->Initialize(poolDesc)) {
return pool;
}
delete pool;
return nullptr;
}
RHIDescriptorSet* D3D12Device::CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) {
if (pool == nullptr) {
return nullptr;
}
return pool->AllocateSet(layout);
}
RHIFence* D3D12Device::CreateFence(const FenceDesc& desc) {
auto* fence = new D3D12Fence();
if (fence->Initialize(m_device.Get(), desc.initialValue)) {

View File

@@ -4,6 +4,7 @@
#include "XCEngine/RHI/OpenGL/OpenGLShader.h"
#include "XCEngine/RHI/OpenGL/OpenGLFramebuffer.h"
#include "XCEngine/RHI/OpenGL/OpenGLRenderPass.h"
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h"
#include <glad/glad.h>
namespace XCEngine {
@@ -650,6 +651,38 @@ void OpenGLCommandList::SetPipelineState(RHIPipelineState* pipelineState) {
}
}
void OpenGLCommandList::SetGraphicsDescriptorSets(
uint32_t firstSet,
uint32_t count,
RHIDescriptorSet** descriptorSets,
RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)pipelineLayout;
for (uint32_t i = 0; i < count; ++i) {
if (descriptorSets[i] != nullptr) {
OpenGLDescriptorSet* glSet = static_cast<OpenGLDescriptorSet*>(descriptorSets[i]);
glSet->Bind();
}
}
}
void OpenGLCommandList::SetComputeDescriptorSets(
uint32_t firstSet,
uint32_t count,
RHIDescriptorSet** descriptorSets,
RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)pipelineLayout;
for (uint32_t i = 0; i < count; ++i) {
if (descriptorSets[i] != nullptr) {
OpenGLDescriptorSet* glSet = static_cast<OpenGLDescriptorSet*>(descriptorSets[i]);
glSet->Bind();
}
}
}
void OpenGLCommandList::SetVertexBuffers(uint32_t startSlot, uint32_t count, RHIResourceView** buffers, const uint64_t* offsets, const uint32_t* strides) {
for (uint32_t i = 0; i < count; i++) {
if (!buffers[i]) continue;

View File

@@ -0,0 +1,62 @@
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorPool.h"
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h"
#include "XCEngine/RHI/OpenGL/OpenGLTextureUnitAllocator.h"
namespace XCEngine {
namespace RHI {
OpenGLDescriptorPool::OpenGLDescriptorPool()
: m_type(DescriptorHeapType::CBV_SRV_UAV)
, m_maxSets(0)
, m_textureUnitAllocator(nullptr) {
}
OpenGLDescriptorPool::~OpenGLDescriptorPool() {
Shutdown();
}
bool OpenGLDescriptorPool::Initialize(const DescriptorPoolDesc& desc) {
m_type = desc.type;
m_maxSets = desc.descriptorCount;
return true;
}
void OpenGLDescriptorPool::Shutdown() {
for (auto* set : m_allocatedSets) {
delete set;
}
m_allocatedSets.clear();
m_textureUnitAllocator = nullptr;
}
RHIDescriptorSet* OpenGLDescriptorPool::AllocateSet(const DescriptorSetLayoutDesc& layout) {
if (m_allocatedSets.size() >= m_maxSets) {
return nullptr;
}
OpenGLDescriptorSet* newSet = new OpenGLDescriptorSet();
if (!newSet->Initialize(m_textureUnitAllocator, layout.bindingCount, layout)) {
delete newSet;
return nullptr;
}
m_allocatedSets.push_back(newSet);
return newSet;
}
void OpenGLDescriptorPool::FreeSet(RHIDescriptorSet* set) {
if (set == nullptr) {
return;
}
for (auto it = m_allocatedSets.begin(); it != m_allocatedSets.end(); ++it) {
if (*it == set) {
m_allocatedSets.erase(it);
delete set;
return;
}
}
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,146 @@
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h"
#include "XCEngine/RHI/OpenGL/OpenGLTextureUnitAllocator.h"
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include "XCEngine/RHI/OpenGL/OpenGLSampler.h"
#include <glad/glad.h>
namespace XCEngine {
namespace RHI {
OpenGLDescriptorSet::OpenGLDescriptorSet()
: m_allocator(nullptr)
, m_layoutBindings(nullptr)
, m_bindingCount(0)
, m_bound(false) {
}
OpenGLDescriptorSet::~OpenGLDescriptorSet() {
Shutdown();
}
bool OpenGLDescriptorSet::Initialize(OpenGLTextureUnitAllocator* allocator, uint32_t count, const DescriptorSetLayoutDesc& layout) {
m_allocator = allocator;
m_bindingCount = layout.bindingCount;
if (layout.bindingCount > 0 && layout.bindings != nullptr) {
m_layoutBindings = new DescriptorSetLayoutBinding[layout.bindingCount];
for (uint32_t i = 0; i < layout.bindingCount; ++i) {
m_layoutBindings[i] = layout.bindings[i];
}
}
m_bindings.resize(layout.bindingCount);
for (uint32_t i = 0; i < layout.bindingCount; ++i) {
m_bindings[i].binding = layout.bindings[i].binding;
m_bindings[i].type = layout.bindings[i].type;
m_bindings[i].count = layout.bindings[i].count;
m_bindings[i].textureUnits.resize(layout.bindings[i].count);
m_bindings[i].textureIds.resize(layout.bindings[i].count, 0);
m_bindings[i].samplerIds.resize(layout.bindings[i].count, 0);
for (uint32_t j = 0; j < layout.bindings[i].count; ++j) {
int32_t unit = m_allocator->Allocate();
if (unit < 0) {
Shutdown();
return false;
}
m_bindings[i].textureUnits[j] = static_cast<uint32_t>(unit);
}
}
return true;
}
void OpenGLDescriptorSet::Shutdown() {
if (m_allocator != nullptr) {
for (auto& binding : m_bindings) {
for (uint32_t i = 0; i < binding.textureUnits.size(); ++i) {
m_allocator->Free(static_cast<int32_t>(binding.textureUnits[i]));
}
}
}
m_bindings.clear();
m_allocator = nullptr;
if (m_layoutBindings != nullptr) {
delete[] m_layoutBindings;
m_layoutBindings = nullptr;
}
m_bindingCount = 0;
}
void OpenGLDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
if (view == nullptr) {
return;
}
uint32_t bindingIndex = offset;
if (bindingIndex >= m_bindings.size()) {
return;
}
OpenGLResourceView* glView = static_cast<OpenGLResourceView*>(view);
uint32_t textureId = glView->GetTexture();
if (offset < m_bindings[bindingIndex].textureIds.size()) {
m_bindings[bindingIndex].textureIds[offset] = textureId;
}
}
void OpenGLDescriptorSet::UpdateSampler(uint32_t offset, RHISampler* sampler) {
if (sampler == nullptr) {
return;
}
uint32_t bindingIndex = offset;
if (bindingIndex >= m_bindings.size()) {
return;
}
OpenGLSampler* glSampler = static_cast<OpenGLSampler*>(sampler);
uint32_t samplerId = glSampler->GetID();
if (offset < m_bindings[bindingIndex].samplerIds.size()) {
m_bindings[bindingIndex].samplerIds[offset] = samplerId;
}
}
void OpenGLDescriptorSet::Bind() {
for (size_t i = 0; i < m_bindings.size(); ++i) {
const auto& binding = m_bindings[i];
for (size_t j = 0; j < binding.textureUnits.size(); ++j) {
uint32_t unit = binding.textureUnits[j];
uint32_t textureId = binding.textureIds[j];
uint32_t samplerId = binding.samplerIds[j];
if (textureId != 0) {
glActiveTexture(GL_TEXTURE0 + unit);
if (binding.type == DescriptorType::Sampler) {
glBindTexture(GL_SAMPLER, textureId);
} else {
glBindTexture(GL_TEXTURE_2D, textureId);
}
}
if (samplerId != 0) {
glBindSampler(unit, samplerId);
}
}
}
m_bound = true;
}
uint32_t OpenGLDescriptorSet::GetBindingPoint(uint32_t binding) const {
for (size_t i = 0; i < m_bindings.size(); ++i) {
if (m_bindings[i].binding == binding && !m_bindings[i].textureUnits.empty()) {
return m_bindings[i].textureUnits[0];
}
}
return 0;
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -18,6 +18,8 @@
#include "XCEngine/RHI/OpenGL/OpenGLUniformBufferManager.h"
#include "XCEngine/RHI/OpenGL/OpenGLFramebuffer.h"
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorPool.h"
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h"
#include "XCEngine/Debug/Logger.h"
typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc);
@@ -384,6 +386,23 @@ RHISampler* OpenGLDevice::CreateSampler(const SamplerDesc& desc) {
return sampler;
}
RHIDescriptorPool* OpenGLDevice::CreateDescriptorPool(const DescriptorPoolDesc& desc) {
auto* pool = new OpenGLDescriptorPool();
if (pool->Initialize(desc)) {
pool->SetTextureUnitAllocator(m_textureUnitAllocator.get());
return pool;
}
delete pool;
return nullptr;
}
RHIDescriptorSet* OpenGLDevice::CreateDescriptorSet(RHIDescriptorPool* pool, const DescriptorSetLayoutDesc& layout) {
if (pool == nullptr) {
return nullptr;
}
return pool->AllocateSet(layout);
}
RHIResourceView* OpenGLDevice::CreateRenderTargetView(RHITexture* texture, const ResourceViewDesc& desc) {
if (!texture) {
return nullptr;