Honor input layouts in OpenGL vertex bindings

This commit is contained in:
2026-03-25 23:24:06 +08:00
parent 1597181458
commit 2470451d96
3 changed files with 264 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ class OpenGLBuffer;
class OpenGLVertexArray;
class OpenGLShader;
class OpenGLTexture;
class OpenGLPipelineState;
enum class PrimitiveType {
Points,
@@ -192,11 +193,17 @@ public:
void DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) override;
private:
void EnsureInternalVertexArrayBound();
void DisableConfiguredVertexAttributes();
unsigned int m_primitiveType;
unsigned int m_currentVAO;
unsigned int m_currentProgram;
unsigned int m_internalVAO;
OpenGLPipelineState* m_currentPipelineState;
std::vector<unsigned int> m_enabledVertexAttributes;
OpenGLShader* m_currentShader;
};
} // namespace RHI
} // namespace XCEngine
} // namespace XCEngine

View File

@@ -11,10 +11,57 @@
namespace XCEngine {
namespace RHI {
namespace {
struct OpenGLVertexAttribFormat {
GLint componentCount = 0;
GLenum type = GL_FLOAT;
GLboolean normalized = GL_FALSE;
bool integer = false;
};
bool GetOpenGLVertexAttribFormat(Format format, OpenGLVertexAttribFormat& attributeFormat) {
switch (format) {
case Format::R8_UNorm:
attributeFormat = { 1, GL_UNSIGNED_BYTE, GL_TRUE, false };
return true;
case Format::R8G8_UNorm:
attributeFormat = { 2, GL_UNSIGNED_BYTE, GL_TRUE, false };
return true;
case Format::R8G8B8A8_UNorm:
attributeFormat = { 4, GL_UNSIGNED_BYTE, GL_TRUE, false };
return true;
case Format::R16_Float:
attributeFormat = { 1, GL_HALF_FLOAT, GL_FALSE, false };
return true;
case Format::R16G16B16A16_Float:
attributeFormat = { 4, GL_HALF_FLOAT, GL_FALSE, false };
return true;
case Format::R32_Float:
attributeFormat = { 1, GL_FLOAT, GL_FALSE, false };
return true;
case Format::R32G32B32A32_Float:
attributeFormat = { 4, GL_FLOAT, GL_FALSE, false };
return true;
case Format::R32_UInt:
attributeFormat = { 1, GL_UNSIGNED_INT, GL_FALSE, true };
return true;
case Format::R32G32B32A32_UInt:
attributeFormat = { 4, GL_UNSIGNED_INT, GL_FALSE, true };
return true;
default:
return false;
}
}
} // namespace
OpenGLCommandList::OpenGLCommandList()
: m_primitiveType(GL_TRIANGLES)
, m_currentVAO(0)
, m_currentProgram(0)
, m_internalVAO(0)
, m_currentPipelineState(nullptr)
, m_currentShader(nullptr) {
}
@@ -407,10 +454,17 @@ void OpenGLCommandList::PopDebugGroup() {
}
void OpenGLCommandList::Shutdown() {
DisableConfiguredVertexAttributes();
if (m_internalVAO != 0) {
glDeleteVertexArrays(1, &m_internalVAO);
m_internalVAO = 0;
}
m_currentPipelineState = nullptr;
m_currentShader = nullptr;
}
void OpenGLCommandList::Reset() {
m_currentPipelineState = nullptr;
}
void OpenGLCommandList::Close() {
@@ -598,7 +652,10 @@ void OpenGLCommandList::ClearDepthStencil(RHIResourceView* depthStencil, float d
void OpenGLCommandList::SetPipelineState(RHIPipelineState* pipelineState) {
if (pipelineState) {
m_currentPipelineState = static_cast<OpenGLPipelineState*>(pipelineState);
pipelineState->Bind();
} else {
m_currentPipelineState = nullptr;
}
}
@@ -633,6 +690,63 @@ void OpenGLCommandList::SetComputeDescriptorSets(
}
void OpenGLCommandList::SetVertexBuffers(uint32_t startSlot, uint32_t count, RHIResourceView** buffers, const uint64_t* offsets, const uint32_t* strides) {
EnsureInternalVertexArrayBound();
DisableConfiguredVertexAttributes();
if (m_currentPipelineState != nullptr) {
const InputLayoutDesc& inputLayout = m_currentPipelineState->GetInputLayout();
if (!inputLayout.elements.empty()) {
for (uint32_t attributeIndex = 0; attributeIndex < inputLayout.elements.size(); ++attributeIndex) {
const InputElementDesc& element = inputLayout.elements[attributeIndex];
if (element.inputSlot < startSlot || element.inputSlot >= startSlot + count) {
continue;
}
const uint32_t bindingIndex = element.inputSlot - startSlot;
if (buffers[bindingIndex] == nullptr) {
continue;
}
OpenGLResourceView* view = static_cast<OpenGLResourceView*>(buffers[bindingIndex]);
if (!view->IsValid()) {
continue;
}
OpenGLVertexAttribFormat attributeFormat = {};
if (!GetOpenGLVertexAttribFormat(static_cast<Format>(element.format), attributeFormat)) {
continue;
}
const GLuint glBuffer = view->GetBuffer();
const uint32_t stride = strides[bindingIndex] > 0 ? strides[bindingIndex] : view->GetBufferStride();
const uint64_t offset = view->GetBufferOffset() + offsets[bindingIndex] + element.alignedByteOffset;
glBindBuffer(GL_ARRAY_BUFFER, glBuffer);
glEnableVertexAttribArray(attributeIndex);
if (attributeFormat.integer) {
glVertexAttribIPointer(
attributeIndex,
attributeFormat.componentCount,
attributeFormat.type,
static_cast<GLsizei>(stride),
reinterpret_cast<void*>(static_cast<uintptr_t>(offset)));
} else {
glVertexAttribPointer(
attributeIndex,
attributeFormat.componentCount,
attributeFormat.type,
attributeFormat.normalized,
static_cast<GLsizei>(stride),
reinterpret_cast<void*>(static_cast<uintptr_t>(offset)));
}
m_enabledVertexAttributes.push_back(attributeIndex);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
return;
}
}
for (uint32_t i = 0; i < count; i++) {
if (!buffers[i]) continue;
OpenGLResourceView* view = static_cast<OpenGLResourceView*>(buffers[i]);
@@ -644,12 +758,14 @@ void OpenGLCommandList::SetVertexBuffers(uint32_t startSlot, uint32_t count, RHI
const uint32_t stride = strides[i] > 0 ? strides[i] : view->GetBufferStride();
const uint64_t offset = view->GetBufferOffset() + offsets[i];
glVertexAttribPointer(startSlot + i, stride / sizeof(float), GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(static_cast<uintptr_t>(offset)));
m_enabledVertexAttributes.push_back(startSlot + i);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void OpenGLCommandList::SetIndexBuffer(RHIResourceView* buffer, uint64_t offset) {
if (!buffer) return;
EnsureInternalVertexArrayBound();
OpenGLResourceView* view = static_cast<OpenGLResourceView*>(buffer);
if (!view->IsValid()) return;
@@ -694,5 +810,21 @@ void OpenGLCommandList::SetBlendFactor(const float factor[4]) {
glBlendColor(factor[0], factor[1], factor[2], factor[3]);
}
void OpenGLCommandList::EnsureInternalVertexArrayBound() {
if (m_internalVAO == 0) {
glGenVertexArrays(1, &m_internalVAO);
}
m_currentVAO = m_internalVAO;
glBindVertexArray(m_internalVAO);
}
void OpenGLCommandList::DisableConfiguredVertexAttributes() {
for (unsigned int attributeIndex : m_enabledVertexAttributes) {
glDisableVertexAttribArray(attributeIndex);
}
m_enabledVertexAttributes.clear();
}
} // namespace RHI
} // namespace XCEngine