fix(rhi): make opengl descriptor binding set-aware

This commit is contained in:
2026-03-26 15:10:03 +08:00
parent 9218ea20b5
commit 733b573963
8 changed files with 379 additions and 21 deletions

View File

@@ -10,6 +10,7 @@ namespace XCEngine {
namespace RHI {
class OpenGLTextureUnitAllocator;
class OpenGLPipelineLayout;
struct DescriptorBinding {
uint32_t binding;
@@ -30,6 +31,7 @@ public:
void Bind() override;
void Unbind() override;
void BindWithPipelineLayout(const OpenGLPipelineLayout* pipelineLayout, uint32_t setIndex);
void Update(uint32_t offset, RHIResourceView* view) override;
void UpdateSampler(uint32_t offset, RHISampler* sampler) override;
@@ -48,6 +50,7 @@ public:
private:
DescriptorBinding* FindBinding(uint32_t binding);
const DescriptorBinding* FindBinding(uint32_t binding) const;
void EnsureConstantBufferUploaded();
OpenGLTextureUnitAllocator* m_allocator;
std::vector<DescriptorBinding> m_bindings;

View File

@@ -1,5 +1,6 @@
#pragma once
#include <unordered_map>
#include <vector>
#include "../RHIPipelineLayout.h"
@@ -9,6 +10,13 @@ namespace RHI {
class OpenGLPipelineLayout : public RHIPipelineLayout {
public:
struct SetBindingPointMapping {
std::unordered_map<uint32_t, uint32_t> constantBufferBindingPoints;
std::unordered_map<uint32_t, uint32_t> shaderResourceBindingPoints;
std::unordered_map<uint32_t, uint32_t> unorderedAccessBindingPoints;
std::unordered_map<uint32_t, uint32_t> samplerBindingPoints;
};
OpenGLPipelineLayout() = default;
~OpenGLPipelineLayout() override = default;
@@ -16,10 +24,21 @@ public:
void Shutdown() override;
void* GetNativeHandle() override { return m_initialized ? this : nullptr; }
bool UsesSetLayouts() const { return m_desc.setLayoutCount > 0 && m_desc.setLayouts != nullptr; }
uint32_t GetSetLayoutCount() const { return m_desc.setLayoutCount; }
bool HasConstantBufferBinding(uint32_t setIndex, uint32_t binding) const;
uint32_t GetConstantBufferBindingPoint(uint32_t setIndex, uint32_t binding) const;
bool HasShaderResourceBinding(uint32_t setIndex, uint32_t binding) const;
uint32_t GetShaderResourceBindingPoint(uint32_t setIndex, uint32_t binding) const;
bool HasUnorderedAccessBinding(uint32_t setIndex, uint32_t binding) const;
uint32_t GetUnorderedAccessBindingPoint(uint32_t setIndex, uint32_t binding) const;
bool HasSamplerBinding(uint32_t setIndex, uint32_t binding) const;
uint32_t GetSamplerBindingPoint(uint32_t setIndex, uint32_t binding) const;
const RHIPipelineLayoutDesc& GetDesc() const { return m_desc; }
private:
RHIPipelineLayoutDesc m_desc = {};
std::vector<SetBindingPointMapping> m_setBindingPointMappings;
std::vector<DescriptorSetLayoutDesc> m_setLayouts;
std::vector<std::vector<DescriptorSetLayoutBinding>> m_setLayoutBindings;
bool m_initialized = false;

View File

@@ -1,6 +1,7 @@
#include "XCEngine/RHI/OpenGL/OpenGLCommandList.h"
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include "XCEngine/RHI/OpenGL/OpenGLPipelineState.h"
#include "XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h"
#include "XCEngine/RHI/OpenGL/OpenGLShader.h"
#include "XCEngine/RHI/OpenGL/OpenGLFramebuffer.h"
#include "XCEngine/RHI/OpenGL/OpenGLRenderPass.h"
@@ -717,12 +718,13 @@ void OpenGLCommandList::SetGraphicsDescriptorSets(
uint32_t count,
RHIDescriptorSet** descriptorSets,
RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)pipelineLayout;
OpenGLPipelineLayout* openGLPipelineLayout = static_cast<OpenGLPipelineLayout*>(pipelineLayout);
for (uint32_t i = 0; i < count; ++i) {
if (descriptorSets[i] != nullptr) {
descriptorSets[i]->Bind();
static_cast<OpenGLDescriptorSet*>(descriptorSets[i])->BindWithPipelineLayout(
openGLPipelineLayout,
firstSet + i);
}
}
}
@@ -732,12 +734,13 @@ void OpenGLCommandList::SetComputeDescriptorSets(
uint32_t count,
RHIDescriptorSet** descriptorSets,
RHIPipelineLayout* pipelineLayout) {
(void)firstSet;
(void)pipelineLayout;
OpenGLPipelineLayout* openGLPipelineLayout = static_cast<OpenGLPipelineLayout*>(pipelineLayout);
for (uint32_t i = 0; i < count; ++i) {
if (descriptorSets[i] != nullptr) {
descriptorSets[i]->Bind();
static_cast<OpenGLDescriptorSet*>(descriptorSets[i])->BindWithPipelineLayout(
openGLPipelineLayout,
firstSet + i);
}
}
}

View File

@@ -1,4 +1,5 @@
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h"
#include "XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h"
#include "XCEngine/RHI/OpenGL/OpenGLTextureUnitAllocator.h"
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include "XCEngine/RHI/OpenGL/OpenGLSampler.h"
@@ -7,6 +8,32 @@
namespace XCEngine {
namespace RHI {
namespace {
uint32_t ResolveBindingPoint(
const OpenGLPipelineLayout* pipelineLayout,
uint32_t setIndex,
const DescriptorBinding& binding) {
if (pipelineLayout == nullptr || !pipelineLayout->UsesSetLayouts()) {
return binding.binding;
}
switch (binding.type) {
case DescriptorType::CBV:
return pipelineLayout->GetConstantBufferBindingPoint(setIndex, binding.binding);
case DescriptorType::SRV:
return pipelineLayout->GetShaderResourceBindingPoint(setIndex, binding.binding);
case DescriptorType::UAV:
return pipelineLayout->GetUnorderedAccessBindingPoint(setIndex, binding.binding);
case DescriptorType::Sampler:
return pipelineLayout->GetSamplerBindingPoint(setIndex, binding.binding);
default:
return UINT32_MAX;
}
}
} // namespace
OpenGLDescriptorSet::OpenGLDescriptorSet()
: m_allocator(nullptr)
, m_layoutBindings(nullptr)
@@ -97,6 +124,7 @@ void OpenGLDescriptorSet::Shutdown() {
m_bindings.clear();
m_allocator = nullptr;
m_bound = false;
if (m_layoutBindings != nullptr) {
delete[] m_layoutBindings;
@@ -105,6 +133,20 @@ void OpenGLDescriptorSet::Shutdown() {
m_bindingCount = 0;
}
void OpenGLDescriptorSet::EnsureConstantBufferUploaded() {
if (!m_constantBufferDirty || m_constantBufferData.empty()) {
return;
}
if (m_constantBuffer == 0) {
glGenBuffers(1, reinterpret_cast<GLuint*>(&m_constantBuffer));
}
glBindBuffer(GL_UNIFORM_BUFFER, m_constantBuffer);
glBufferData(GL_UNIFORM_BUFFER, m_constantBufferData.size(), m_constantBufferData.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
m_constantBufferDirty = false;
}
void OpenGLDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
if (view == nullptr) {
return;
@@ -134,15 +176,7 @@ void OpenGLDescriptorSet::UpdateSampler(uint32_t offset, RHISampler* sampler) {
}
void OpenGLDescriptorSet::Bind() {
if (m_constantBufferDirty && !m_constantBufferData.empty()) {
if (m_constantBuffer == 0) {
glGenBuffers(1, reinterpret_cast<GLuint*>(&m_constantBuffer));
}
glBindBuffer(GL_UNIFORM_BUFFER, m_constantBuffer);
glBufferData(GL_UNIFORM_BUFFER, m_constantBufferData.size(), m_constantBufferData.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
m_constantBufferDirty = false;
}
EnsureConstantBufferUploaded();
if (m_constantBuffer != 0) {
for (const auto& binding : m_bindings) {
@@ -178,6 +212,56 @@ void OpenGLDescriptorSet::Bind() {
m_bound = true;
}
void OpenGLDescriptorSet::BindWithPipelineLayout(const OpenGLPipelineLayout* pipelineLayout, uint32_t setIndex) {
EnsureConstantBufferUploaded();
if (m_constantBuffer != 0) {
for (const auto& binding : m_bindings) {
if (binding.type != DescriptorType::CBV) {
continue;
}
const uint32_t baseBindingPoint = ResolveBindingPoint(pipelineLayout, setIndex, binding);
if (baseBindingPoint == UINT32_MAX) {
continue;
}
for (uint32_t i = 0; i < binding.count; ++i) {
glBindBufferBase(GL_UNIFORM_BUFFER, baseBindingPoint + i, m_constantBuffer);
}
}
}
for (const auto& binding : m_bindings) {
const uint32_t baseBindingPoint = ResolveBindingPoint(pipelineLayout, setIndex, binding);
if (baseBindingPoint == UINT32_MAX) {
continue;
}
for (size_t i = 0; i < binding.textureIds.size(); ++i) {
const uint32_t bindingPoint = baseBindingPoint + static_cast<uint32_t>(i);
const uint32_t textureId = binding.textureIds[i];
if (textureId != 0) {
if (binding.type == DescriptorType::UAV) {
glBindImageTexture(bindingPoint, textureId, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
} else if (binding.type != DescriptorType::Sampler) {
glActiveTexture(GL_TEXTURE0 + bindingPoint);
glBindTexture(GL_TEXTURE_2D, textureId);
}
}
if (binding.type == DescriptorType::Sampler && i < binding.samplerIds.size()) {
const uint32_t samplerId = binding.samplerIds[i];
if (samplerId != 0) {
glBindSampler(bindingPoint, samplerId);
}
}
}
}
m_bound = true;
}
void OpenGLDescriptorSet::Unbind() {
for (size_t i = 0; i < m_bindings.size(); ++i) {
const auto& binding = m_bindings[i];

View File

@@ -1,5 +1,7 @@
#include "XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h"
#include <algorithm>
namespace XCEngine {
namespace RHI {
@@ -27,10 +29,34 @@ void AccumulateDescriptorCounts(const DescriptorSetLayoutDesc& setLayout, RHIPip
}
}
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
bool OpenGLPipelineLayout::Initialize(const RHIPipelineLayoutDesc& desc) {
m_desc = desc;
m_setBindingPointMappings.clear();
m_setLayouts.clear();
m_setLayoutBindings.clear();
@@ -62,14 +88,164 @@ bool OpenGLPipelineLayout::Initialize(const RHIPipelineLayoutDesc& desc) {
m_desc.setLayouts = m_setLayouts.data();
m_desc.setLayoutCount = static_cast<uint32_t>(m_setLayouts.size());
m_setBindingPointMappings.assign(m_desc.setLayoutCount, SetBindingPointMapping{});
uint32_t nextCBVBindingPoint = 0;
uint32_t nextSRVBindingPoint = 0;
uint32_t nextUAVBindingPoint = 0;
uint32_t nextSamplerBindingPoint = 0;
for (uint32_t setIndex = 0; setIndex < m_desc.setLayoutCount; ++setIndex) {
const DescriptorSetLayoutDesc& setLayout = m_desc.setLayouts[setIndex];
SetBindingPointMapping& mapping = m_setBindingPointMappings[setIndex];
const auto appendBindings =
[&setLayout](
DescriptorType type,
std::unordered_map<uint32_t, uint32_t>& bindingPoints,
uint32_t& nextBindingPoint) {
const auto bindings = GatherBindingsOfTypeSorted(setLayout, type);
for (const DescriptorSetLayoutBinding* binding : bindings) {
bindingPoints[binding->binding] = nextBindingPoint;
nextBindingPoint += binding->count > 0 ? binding->count : 1u;
}
};
appendBindings(
DescriptorType::CBV,
mapping.constantBufferBindingPoints,
nextCBVBindingPoint);
appendBindings(
DescriptorType::SRV,
mapping.shaderResourceBindingPoints,
nextSRVBindingPoint);
appendBindings(
DescriptorType::UAV,
mapping.unorderedAccessBindingPoints,
nextUAVBindingPoint);
appendBindings(
DescriptorType::Sampler,
mapping.samplerBindingPoints,
nextSamplerBindingPoint);
}
}
m_initialized = true;
return true;
}
bool OpenGLPipelineLayout::HasConstantBufferBinding(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return false;
}
return m_setBindingPointMappings[setIndex].constantBufferBindingPoints.find(binding) !=
m_setBindingPointMappings[setIndex].constantBufferBindingPoints.end();
}
uint32_t OpenGLPipelineLayout::GetConstantBufferBindingPoint(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? binding : UINT32_MAX;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return UINT32_MAX;
}
const auto& bindingPoints = m_setBindingPointMappings[setIndex].constantBufferBindingPoints;
auto it = bindingPoints.find(binding);
return it != bindingPoints.end() ? it->second : UINT32_MAX;
}
bool OpenGLPipelineLayout::HasShaderResourceBinding(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return false;
}
return m_setBindingPointMappings[setIndex].shaderResourceBindingPoints.find(binding) !=
m_setBindingPointMappings[setIndex].shaderResourceBindingPoints.end();
}
uint32_t OpenGLPipelineLayout::GetShaderResourceBindingPoint(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? binding : UINT32_MAX;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return UINT32_MAX;
}
const auto& bindingPoints = m_setBindingPointMappings[setIndex].shaderResourceBindingPoints;
auto it = bindingPoints.find(binding);
return it != bindingPoints.end() ? it->second : UINT32_MAX;
}
bool OpenGLPipelineLayout::HasUnorderedAccessBinding(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return false;
}
return m_setBindingPointMappings[setIndex].unorderedAccessBindingPoints.find(binding) !=
m_setBindingPointMappings[setIndex].unorderedAccessBindingPoints.end();
}
uint32_t OpenGLPipelineLayout::GetUnorderedAccessBindingPoint(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? binding : UINT32_MAX;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return UINT32_MAX;
}
const auto& bindingPoints = m_setBindingPointMappings[setIndex].unorderedAccessBindingPoints;
auto it = bindingPoints.find(binding);
return it != bindingPoints.end() ? it->second : UINT32_MAX;
}
bool OpenGLPipelineLayout::HasSamplerBinding(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return false;
}
return m_setBindingPointMappings[setIndex].samplerBindingPoints.find(binding) !=
m_setBindingPointMappings[setIndex].samplerBindingPoints.end();
}
uint32_t OpenGLPipelineLayout::GetSamplerBindingPoint(uint32_t setIndex, uint32_t binding) const {
if (!UsesSetLayouts()) {
return setIndex == 0 ? binding : UINT32_MAX;
}
if (setIndex >= m_setBindingPointMappings.size()) {
return UINT32_MAX;
}
const auto& bindingPoints = m_setBindingPointMappings[setIndex].samplerBindingPoints;
auto it = bindingPoints.find(binding);
return it != bindingPoints.end() ? it->second : UINT32_MAX;
}
void OpenGLPipelineLayout::Shutdown() {
m_desc = {};
m_setBindingPointMappings.clear();
m_setLayouts.clear();
m_setLayoutBindings.clear();
m_initialized = false;