Files
MobileGL/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp
T

509 lines
16 KiB
C++

#include <gtest/gtest.h>
#include "Includes.h"
#include <MG_Impl/GLImpl/Buffer/GL_Buffer.h>
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
#include <MG_Impl/GLImpl/VertexArray/GL_VertexArray.h>
#include <MG_State/GLState/Core.h>
using namespace MobileGL;
class VertexArrayTest : public ::testing::Test {
protected:
MG_State::GLState::GLContext glContext;
SharedPtr<MG_State::GLState::BufferObject> CreateTestVBO() {
auto bufferNames = glContext.GenBufferNames(1);
auto vbo = glContext.CreateBufferObject(bufferNames[0]);
glContext.GetBufferBindingSlot(BufferTarget::Vertex).Bind(vbo);
Vector<float> vertexData = {
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f
};
SizeT byteSize = vertexData.size() * sizeof(float);
vbo->Resize(byteSize);
DataPtr ptr{ .data = vertexData.data(), .size = byteSize };
vbo->UploadData(ptr, 0);
return vbo;
}
};
TEST_F(VertexArrayTest, GenerateAndBindVAO) {
auto vaoNames = glContext.GenVertexArrayNames(2);
auto vao0 = glContext.CreateVertexArrayObject(vaoNames[0]);
auto vao1 = glContext.CreateVertexArrayObject(vaoNames[1]);
glContext.BindVertexArray(vaoNames[0]);
ASSERT_EQ(glContext.GetBoundVertexArray(), vao0);
glContext.BindVertexArray(vaoNames[1]);
ASSERT_EQ(glContext.GetBoundVertexArray(), vao1);
glContext.BindVertexArray(0);
ASSERT_EQ(glContext.GetBoundVertexArray(), nullptr);
}
TEST_F(VertexArrayTest, VertexAttributeSetup) {
auto vaoNames = glContext.GenVertexArrayNames(1);
auto vao = glContext.CreateVertexArrayObject(vaoNames[0]);
glContext.BindVertexArray(vaoNames[0]);
auto vbo = CreateTestVBO();
vao->EnableAttribute(0);
vao->SetAttributeFormat(0, 4, DataType::Float32, false, 8 * sizeof(float), 0, false);
vao->BindAttributeBuffer(0, vbo);
vao->EnableAttribute(1);
vao->SetAttributeFormat(1, 4, DataType::Float32, false, 8 * sizeof(float), 4 * sizeof(float), false);
vao->BindAttributeBuffer(1, vbo);
const auto& attr0 = vao->GetAttribute(0);
ASSERT_TRUE(attr0.Enabled);
ASSERT_EQ(attr0.Size, 4);
ASSERT_EQ(attr0.Type, DataType::Float32);
ASSERT_EQ(attr0.Stride, 8 * sizeof(float));
ASSERT_EQ(attr0.Offset, 0);
ASSERT_EQ(attr0.Buffer, vbo);
const auto& attr1 = vao->GetAttribute(1);
ASSERT_TRUE(attr1.Enabled);
ASSERT_EQ(attr1.Offset, 4 * sizeof(float));
vao->DisableAttribute(1);
ASSERT_FALSE(vao->IsAttributeEnabled(1));
}
TEST_F(VertexArrayTest, IndexBufferBinding) {
auto vaoNames = glContext.GenVertexArrayNames(1);
auto vao = glContext.CreateVertexArrayObject(vaoNames[0]);
glContext.BindVertexArray(vaoNames[0]);
auto bufferNames = glContext.GenBufferNames(1);
auto ebo = glContext.CreateBufferObject(bufferNames[0]);
glContext.GetBufferBindingSlot(BufferTarget::Index).Bind(ebo);
Vector<Uint> indices = { 0, 1, 2 };
SizeT byteSize = indices.size() * sizeof(Uint);
ebo->Resize(byteSize);
DataPtr ptr{ .data = indices.data(), .size = byteSize };
ebo->UploadData(ptr, 0);
glContext.GetBufferBindingSlot(BufferTarget::Index).Bind(ebo);
ASSERT_EQ(glContext.GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(), ebo);
auto newEboNames = glContext.GenBufferNames(1);
auto newEbo = glContext.CreateBufferObject(newEboNames[0]);
glContext.GetBufferBindingSlot(BufferTarget::Index).Bind(newEbo);
ASSERT_EQ(glContext.GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(), newEbo);
}
TEST_F(VertexArrayTest, DeleteVAO) {
auto vaoNames = glContext.GenVertexArrayNames(1);
auto vao = glContext.CreateVertexArrayObject(vaoNames[0]);
glContext.BindVertexArray(vaoNames[0]);
ASSERT_EQ(glContext.GetBoundVertexArray(), vao);
glContext.MarkVertexArrayForDeletion(vaoNames[0]);
ASSERT_FALSE(glContext.ValidateVertexArrayObject(vaoNames[0]));
ASSERT_EQ(glContext.GetVertexArrayObject(vaoNames[0]), nullptr);
ASSERT_EQ(glContext.GetBoundVertexArray(), nullptr);
}
TEST_F(VertexArrayTest, ValidateNamesAndObjects) {
const Uint count = 5;
auto vaoNames = glContext.GenVertexArrayNames(count);
for (Uint i = 0; i < count; i++) {
ASSERT_TRUE(glContext.ValidateVertexArrayName(vaoNames[i]));
ASSERT_FALSE(glContext.ValidateVertexArrayObject(vaoNames[i]));
}
for (Uint i = 0; i < count; i += 2) {
glContext.CreateVertexArrayObject(vaoNames[i]);
ASSERT_TRUE(glContext.ValidateVertexArrayObject(vaoNames[i]));
}
for (Uint i = 1; i < count; i += 2) {
glContext.MarkVertexArrayForDeletion(vaoNames[i]);
ASSERT_FALSE(glContext.ValidateVertexArrayName(vaoNames[i]));
}
}
TEST_F(VertexArrayTest, MultipleAttributes) {
auto vaoNames = glContext.GenVertexArrayNames(1);
auto vao = glContext.CreateVertexArrayObject(vaoNames[0]);
glContext.BindVertexArray(vaoNames[0]);
auto vboPos = CreateTestVBO();
auto vboNormalNames = glContext.GenBufferNames(1);
auto vboNormal = glContext.CreateBufferObject(vboNormalNames[0]);
Vector<float> normals(12, 0.5f);
SizeT byteSize = normals.size() * sizeof(float);
vboNormal->Resize(byteSize);
DataPtr ptr{ .data = normals.data(), .size = byteSize };
vboNormal->UploadData(ptr, 0);
vao->EnableAttribute(0);
vao->SetAttributeFormat(0, 3, DataType::Float32, false, 3 * sizeof(float), 0, false);
vao->BindAttributeBuffer(0, vboPos);
vao->EnableAttribute(1);
vao->SetAttributeFormat(1, 3, DataType::Float32, true, 3 * sizeof(float), 0, false);
vao->BindAttributeBuffer(1, vboNormal);
vao->EnableAttribute(2);
vao->SetAttributeFormat(2, 4, DataType::Uint8, true, 4 * sizeof(Uint8), 0, false);
const auto& attr0 = vao->GetAttribute(0);
ASSERT_EQ(attr0.Size, 3);
ASSERT_EQ(attr0.Buffer, vboPos);
const auto& attr1 = vao->GetAttribute(1);
ASSERT_TRUE(attr1.Normalized);
ASSERT_EQ(attr1.Buffer, vboNormal);
const auto& attr2 = vao->GetAttribute(2);
ASSERT_EQ(attr2.Type, DataType::Uint8);
ASSERT_EQ(attr2.Buffer, nullptr);
vao->DisableAttribute(1);
vao->EnableAttribute(1);
ASSERT_TRUE(vao->IsAttributeEnabled(1));
}
TEST_F(VertexArrayTest, BoundVAOPreservesState) {
auto vaoNames = glContext.GenVertexArrayNames(2);
auto vao1 = glContext.CreateVertexArrayObject(vaoNames[0]);
auto vao2 = glContext.CreateVertexArrayObject(vaoNames[1]);
auto vbo = CreateTestVBO();
glContext.BindVertexArray(vaoNames[0]);
vao1->EnableAttribute(0);
vao1->SetAttributeFormat(0, 4, DataType::Float32, false, 0, 0, false);
vao1->BindAttributeBuffer(0, vbo);
glContext.BindVertexArray(vaoNames[1]);
vao2->EnableAttribute(1);
vao2->SetAttributeFormat(1, 3, DataType::Float32, true, 0, 0, false);
glContext.BindVertexArray(vaoNames[0]);
const auto& attr = vao1->GetAttribute(0);
ASSERT_TRUE(attr.Enabled);
ASSERT_EQ(attr.Size, 4);
ASSERT_EQ(attr.Buffer, vbo);
ASSERT_FALSE(vao2->IsAttributeEnabled(0));
}
using namespace MobileGL::MG_Impl::GLImpl;
class GeneralVertexArrayTest : public ::testing::Test {
protected:
void SetUp() override {
MG_State::pGLContext = new MG_State::GLState::GLContext();
}
void TearDown() override {
delete MG_State::pGLContext;
MG_State::pGLContext = nullptr;
}
GLuint CreateVAO() {
GLuint vao;
GenVertexArrays(1, &vao);
BindVertexArray(vao);
return vao;
}
GLuint CreateVBO(GLenum target, GLsizeiptr size, const void* data = nullptr) {
GLuint vbo;
GenBuffers(1, &vbo);
BindBuffer(target, vbo);
BufferData(target, size, data, GL_STATIC_DRAW);
return vbo;
}
};
TEST_F(GeneralVertexArrayTest, General_VAOLifecycle) {
GLuint vaos[3];
GenVertexArrays(3, vaos);
EXPECT_NE(vaos[0], 0);
EXPECT_NE(vaos[1], 0);
EXPECT_NE(vaos[2], 0);
EXPECT_NE(vaos[0], vaos[1]);
BindVertexArray(vaos[0]);
EXPECT_EQ(IsVertexArray(vaos[0]), GL_TRUE);
GLuint deleteVao = vaos[1];
DeleteVertexArrays(1, &deleteVao);
BindVertexArray(deleteVao);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
DeleteVertexArrays(1, &vaos[0]);
DeleteVertexArrays(1, &vaos[2]);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_VertexAttributeConfiguration) {
GLuint vao = CreateVAO();
GLuint vbo = CreateVBO(GL_ARRAY_BUFFER, 128);
VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
EnableVertexAttribArray(0);
VertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
EnableVertexAttribArray(1);
auto vaoObj = MG_State::pGLContext->GetVertexArrayObject(vao);
ASSERT_NE(vaoObj, nullptr);
const auto& attr0 = vaoObj->GetAttribute(0);
EXPECT_TRUE(attr0.Enabled);
EXPECT_EQ(attr0.Size, 3);
EXPECT_EQ(attr0.Type, DataType::Float32);
EXPECT_EQ(attr0.Offset, 0);
const auto& attr1 = vaoObj->GetAttribute(1);
EXPECT_TRUE(attr1.Enabled);
EXPECT_EQ(attr1.Offset, 3 * sizeof(float));
DisableVertexAttribArray(1);
EXPECT_FALSE(vaoObj->GetAttribute(1).Enabled);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_IndexBufferBinding) {
GLuint vao = CreateVAO();
GLuint ebo = CreateVBO(GL_ELEMENT_ARRAY_BUFFER, 256);
auto vaoObj = MG_State::pGLContext->GetVertexArrayObject(vao);
ASSERT_NE(vaoObj, nullptr);
GLuint newEbo;
GenBuffers(1, &newEbo);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, newEbo);
EXPECT_EQ(MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(), MG_State::pGLContext->GetBufferObject(newEbo));
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_IntegerAttributes) {
GLuint vao = CreateVAO();
GLuint vbo = CreateVBO(GL_ARRAY_BUFFER, 128);
VertexAttribIPointer(2, 4, GL_UNSIGNED_INT, sizeof(GLuint) * 8, (void*)(sizeof(GLuint) * 4));
EnableVertexAttribArray(2);
auto vaoObj = MG_State::pGLContext->GetVertexArrayObject(vao);
const auto& attr = vaoObj->GetAttribute(2);
EXPECT_TRUE(attr.Enabled);
EXPECT_EQ(attr.Size, 4);
EXPECT_EQ(attr.Type, DataType::Uint32);
EXPECT_EQ(attr.Offset, sizeof(GLuint) * 4);
EXPECT_TRUE(attr.IsInteger);
EXPECT_FALSE(attr.Normalized);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_StatePreservation) {
GLuint vao1 = CreateVAO();
GLuint vbo1 = CreateVBO(GL_ARRAY_BUFFER, 64);
VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
EnableVertexAttribArray(0);
GLuint vao2;
GenVertexArrays(1, &vao2);
BindVertexArray(vao2);
GLuint vbo2 = CreateVBO(GL_ARRAY_BUFFER, 128);
VertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 0, nullptr);
EnableVertexAttribArray(1);
BindVertexArray(vao1);
auto vaoObj1 = MG_State::pGLContext->GetVertexArrayObject(vao1);
EXPECT_TRUE(vaoObj1->IsAttributeEnabled(0));
EXPECT_FALSE(vaoObj1->IsAttributeEnabled(1));
auto vaoObj2 = MG_State::pGLContext->GetVertexArrayObject(vao2);
EXPECT_TRUE(vaoObj2->IsAttributeEnabled(1));
EXPECT_FALSE(vaoObj2->IsAttributeEnabled(0));
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_ErrorConditions) {
VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
GLuint vao = CreateVAO();
EnableVertexAttribArray(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS);
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
GLuint vbo = CreateVBO(GL_ARRAY_BUFFER, 64);
VertexAttribPointer(0, 3, 0xFFFFFFFF, GL_FALSE, 0, nullptr);
EXPECT_EQ(GetError(), GL_INVALID_ENUM);
VertexAttribPointer(0, 5, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_ComplexUsage) {
GLuint vao1 = CreateVAO();
GLuint vboPos = CreateVBO(GL_ARRAY_BUFFER, 256);
GLuint vboColor = CreateVBO(GL_ARRAY_BUFFER, 128);
GLuint ebo1 = CreateVBO(GL_ELEMENT_ARRAY_BUFFER, 64);
VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
EnableVertexAttribArray(0);
BindBuffer(GL_ARRAY_BUFFER, vboColor);
VertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr);
EnableVertexAttribArray(1);
GLuint vao2;
GenVertexArrays(1, &vao2);
BindVertexArray(vao2);
GLuint vboNormal = CreateVBO(GL_ARRAY_BUFFER, 192);
GLuint ebo2 = CreateVBO(GL_ELEMENT_ARRAY_BUFFER, 96);
VertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
EnableVertexAttribArray(2);
BindVertexArray(vao1);
auto vaoObj1 = MG_State::pGLContext->GetVertexArrayObject(vao1);
EXPECT_TRUE(vaoObj1->IsAttributeEnabled(0));
EXPECT_TRUE(vaoObj1->IsAttributeEnabled(1));
EXPECT_FALSE(vaoObj1->IsAttributeEnabled(2));
EXPECT_NE(MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(), nullptr);
BindVertexArray(vao2);
auto vaoObj2 = MG_State::pGLContext->GetVertexArrayObject(vao2);
EXPECT_TRUE(vaoObj2->IsAttributeEnabled(2));
EXPECT_FALSE(vaoObj2->IsAttributeEnabled(0));
EXPECT_NE(MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(), nullptr);
DeleteVertexArrays(1, &vao1);
DeleteVertexArrays(1, &vao2);
GLuint buffers[] = { vboPos, vboColor, ebo1, vboNormal, ebo2 };
DeleteBuffers(5, buffers);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_BindWithoutCreation) {
GLuint vao = 1;
BindVertexArray(vao);
EXPECT_EQ(GetError(), GL_NO_ERROR);
EXPECT_NE(MG_State::pGLContext->GetVertexArrayObject(vao), nullptr);
GLuint vbo = CreateVBO(GL_ARRAY_BUFFER, 64);
VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_EQ(GetError(), GL_NO_ERROR);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_DeleteBoundVAO) {
GLuint vao = CreateVAO();
GLuint vbo = CreateVBO(GL_ARRAY_BUFFER, 64);
VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
EnableVertexAttribArray(0);
DeleteVertexArrays(1, &vao);
EXPECT_EQ(MG_State::pGLContext->GetBoundVertexArray(), nullptr);
EXPECT_EQ(MG_State::pGLContext->GetVertexArrayObject(vao), nullptr);
VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_ElementBufferBindingPoint) {
GLuint vao1, vao2;
GenVertexArrays(1, &vao1);
GenVertexArrays(1, &vao2);
GLuint ebo1, ebo2;
GenBuffers(1, &ebo1);
GenBuffers(1, &ebo2);
BindVertexArray(vao1);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo1);
BindVertexArray(vao2);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo2);
auto vaoObj1 = MG_State::pGLContext->GetVertexArrayObject(vao1);
auto vaoObj2 = MG_State::pGLContext->GetVertexArrayObject(vao2);
EXPECT_EQ(vaoObj1->GetIndexBufferBindingSlot().GetBoundObject(), MG_State::pGLContext->GetBufferObject(ebo1));
EXPECT_EQ(vaoObj2->GetIndexBufferBindingSlot().GetBoundObject(), MG_State::pGLContext->GetBufferObject(ebo2));
BindVertexArray(vao1);
EXPECT_EQ(GetError(), GL_NO_ERROR);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo2);
EXPECT_EQ(MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(),
MG_State::pGLContext->GetBufferObject(ebo2));
BindVertexArray(vao2);
EXPECT_EQ(MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(),
MG_State::pGLContext->GetBufferObject(ebo2));
BindVertexArray(0);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo1);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
BindVertexArray(vao1);
DeleteVertexArrays(1, &vao1);
BindVertexArray(vao1);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
EXPECT_EQ(GetError(), GL_NO_ERROR);
EXPECT_EQ(MG_State::pGLContext->GetVertexArrayObject(vao1).get(), nullptr);
BindVertexArray(vao2);
DeleteBuffers(1, &ebo2);
EXPECT_EQ(vaoObj2->GetIndexBufferBindingSlot().GetBoundObject().get(), nullptr);
GLuint ebo3;
GenBuffers(1, &ebo3);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo3);
EXPECT_EQ(vaoObj2->GetIndexBufferBindingSlot().GetBoundObject(), MG_State::pGLContext->GetBufferObject(ebo3));
DeleteVertexArrays(1, &vao2);
DeleteBuffers(1, &ebo1);
DeleteBuffers(1, &ebo3);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}