Honor input layouts in OpenGL vertex bindings
This commit is contained in:
@@ -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,9 +193,15 @@ 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user