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

View File

@@ -11,10 +11,57 @@
namespace XCEngine { namespace XCEngine {
namespace RHI { 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() OpenGLCommandList::OpenGLCommandList()
: m_primitiveType(GL_TRIANGLES) : m_primitiveType(GL_TRIANGLES)
, m_currentVAO(0) , m_currentVAO(0)
, m_currentProgram(0) , m_currentProgram(0)
, m_internalVAO(0)
, m_currentPipelineState(nullptr)
, m_currentShader(nullptr) { , m_currentShader(nullptr) {
} }
@@ -407,10 +454,17 @@ void OpenGLCommandList::PopDebugGroup() {
} }
void OpenGLCommandList::Shutdown() { void OpenGLCommandList::Shutdown() {
DisableConfiguredVertexAttributes();
if (m_internalVAO != 0) {
glDeleteVertexArrays(1, &m_internalVAO);
m_internalVAO = 0;
}
m_currentPipelineState = nullptr;
m_currentShader = nullptr; m_currentShader = nullptr;
} }
void OpenGLCommandList::Reset() { void OpenGLCommandList::Reset() {
m_currentPipelineState = nullptr;
} }
void OpenGLCommandList::Close() { void OpenGLCommandList::Close() {
@@ -598,7 +652,10 @@ void OpenGLCommandList::ClearDepthStencil(RHIResourceView* depthStencil, float d
void OpenGLCommandList::SetPipelineState(RHIPipelineState* pipelineState) { void OpenGLCommandList::SetPipelineState(RHIPipelineState* pipelineState) {
if (pipelineState) { if (pipelineState) {
m_currentPipelineState = static_cast<OpenGLPipelineState*>(pipelineState);
pipelineState->Bind(); 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) { 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++) { for (uint32_t i = 0; i < count; i++) {
if (!buffers[i]) continue; if (!buffers[i]) continue;
OpenGLResourceView* view = static_cast<OpenGLResourceView*>(buffers[i]); 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 uint32_t stride = strides[i] > 0 ? strides[i] : view->GetBufferStride();
const uint64_t offset = view->GetBufferOffset() + offsets[i]; 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))); 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); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
void OpenGLCommandList::SetIndexBuffer(RHIResourceView* buffer, uint64_t offset) { void OpenGLCommandList::SetIndexBuffer(RHIResourceView* buffer, uint64_t offset) {
if (!buffer) return; if (!buffer) return;
EnsureInternalVertexArrayBound();
OpenGLResourceView* view = static_cast<OpenGLResourceView*>(buffer); OpenGLResourceView* view = static_cast<OpenGLResourceView*>(buffer);
if (!view->IsValid()) return; if (!view->IsValid()) return;
@@ -694,5 +810,21 @@ void OpenGLCommandList::SetBlendFactor(const float factor[4]) {
glBlendColor(factor[0], factor[1], factor[2], factor[3]); 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 RHI
} // namespace XCEngine } // namespace XCEngine

View File

@@ -6,6 +6,8 @@
#include "XCEngine/RHI/RHIPipelineState.h" #include "XCEngine/RHI/RHIPipelineState.h"
#include "XCEngine/RHI/RHIRenderPass.h" #include "XCEngine/RHI/RHIRenderPass.h"
#include "XCEngine/RHI/RHIFramebuffer.h" #include "XCEngine/RHI/RHIFramebuffer.h"
#include "XCEngine/RHI/OpenGL/OpenGLDevice.h"
#include <glad/glad.h>
using namespace XCEngine::RHI; using namespace XCEngine::RHI;
@@ -521,3 +523,125 @@ TEST_P(RHITestFixture, CommandList_SetShader) {
shader->Shutdown(); shader->Shutdown();
delete shader; delete shader;
} }
TEST_P(RHITestFixture, CommandList_SetVertexBuffers_InterleavedInputLayoutUsesPipelineLayout) {
if (GetBackendType() != RHIType::OpenGL) {
GTEST_SKIP() << "OpenGL-specific vertex layout binding";
}
auto* openGLDevice = static_cast<OpenGLDevice*>(GetDevice());
ASSERT_NE(openGLDevice, nullptr);
ASSERT_TRUE(openGLDevice->MakeContextCurrent());
GraphicsPipelineDesc pipelineDesc = {};
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
InputElementDesc position = {};
position.semanticName = "POSITION";
position.semanticIndex = 0;
position.format = static_cast<uint32_t>(Format::R32G32B32A32_Float);
position.inputSlot = 0;
position.alignedByteOffset = 0;
pipelineDesc.inputLayout.elements.push_back(position);
InputElementDesc color = {};
color.semanticName = "COLOR";
color.semanticIndex = 0;
color.format = static_cast<uint32_t>(Format::R32G32B32A32_Float);
color.inputSlot = 0;
color.alignedByteOffset = sizeof(float) * 4;
pipelineDesc.inputLayout.elements.push_back(color);
static const char* vertexSource = R"(#version 430
layout(location = 0) in vec4 aPosition;
layout(location = 1) in vec4 aColor;
out vec4 vColor;
void main() {
gl_Position = aPosition;
vColor = aColor;
}
)";
static const char* fragmentSource = R"(#version 430
in vec4 vColor;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vColor;
}
)";
pipelineDesc.vertexShader.source.assign(vertexSource, vertexSource + strlen(vertexSource));
pipelineDesc.vertexShader.sourceLanguage = ShaderLanguage::GLSL;
pipelineDesc.vertexShader.profile = L"vs_4_30";
pipelineDesc.fragmentShader.source.assign(fragmentSource, fragmentSource + strlen(fragmentSource));
pipelineDesc.fragmentShader.sourceLanguage = ShaderLanguage::GLSL;
pipelineDesc.fragmentShader.profile = L"fs_4_30";
RHIPipelineState* pipelineState = GetDevice()->CreatePipelineState(pipelineDesc);
ASSERT_NE(pipelineState, nullptr);
BufferDesc bufferDesc = {};
bufferDesc.size = sizeof(float) * 8 * 3;
bufferDesc.stride = sizeof(float) * 8;
bufferDesc.bufferType = static_cast<uint32_t>(BufferType::Vertex);
RHIBuffer* buffer = GetDevice()->CreateBuffer(bufferDesc);
ASSERT_NE(buffer, nullptr);
ResourceViewDesc viewDesc = {};
viewDesc.dimension = ResourceViewDimension::Buffer;
viewDesc.structureByteStride = bufferDesc.stride;
RHIResourceView* vbv = GetDevice()->CreateVertexBufferView(buffer, viewDesc);
ASSERT_NE(vbv, nullptr);
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
RHIResourceView* views[] = { vbv };
uint64_t offsets[] = { 0 };
uint32_t strides[] = { bufferDesc.stride };
cmdList->Reset();
cmdList->SetPipelineState(pipelineState);
cmdList->SetVertexBuffers(0, 1, views, offsets, strides);
GLint enabled0 = 0;
GLint enabled1 = 0;
GLint size0 = 0;
GLint size1 = 0;
GLint stride0 = 0;
GLint stride1 = 0;
void* pointer0 = nullptr;
void* pointer1 = nullptr;
glGetVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled0);
glGetVertexAttribiv(1, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled1);
glGetVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size0);
glGetVertexAttribiv(1, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size1);
glGetVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride0);
glGetVertexAttribiv(1, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride1);
glGetVertexAttribPointerv(0, GL_VERTEX_ATTRIB_ARRAY_POINTER, &pointer0);
glGetVertexAttribPointerv(1, GL_VERTEX_ATTRIB_ARRAY_POINTER, &pointer1);
EXPECT_EQ(enabled0, GL_TRUE);
EXPECT_EQ(enabled1, GL_TRUE);
EXPECT_EQ(size0, 4);
EXPECT_EQ(size1, 4);
EXPECT_EQ(stride0, static_cast<GLint>(bufferDesc.stride));
EXPECT_EQ(stride1, static_cast<GLint>(bufferDesc.stride));
EXPECT_EQ(reinterpret_cast<uintptr_t>(pointer0), 0u);
EXPECT_EQ(reinterpret_cast<uintptr_t>(pointer1), sizeof(float) * 4u);
cmdList->Close();
cmdList->Shutdown();
delete cmdList;
vbv->Shutdown();
delete vbv;
buffer->Shutdown();
delete buffer;
pipelineState->Shutdown();
delete pipelineState;
}