From c50f9204a644100623fc8727eecdfd9649a37794 Mon Sep 17 00:00:00 2001 From: Rafal Maziejuk Date: Sat, 25 May 2024 21:21:41 +0200 Subject: [PATCH] [NOX-76] add GLShader unit tests Signed-off-by: Rafal Maziejuk --- .github/workflows/ci.yml | 2 +- src/opengl/gl_renderer.cpp | 6 +- src/opengl/gl_shader.cpp | 12 ++- src/opengl/gl_shader.h | 8 +- src/opengl/gl_swapchain.cpp | 12 +-- tests/unit_tests/opengl/CMakeLists.txt | 1 + tests/unit_tests/opengl/gl_program_tests.cpp | 83 ++++++++++++++---- tests/unit_tests/opengl/gl_shader_tests.cpp | 89 ++++++++++++++++++++ 8 files changed, 176 insertions(+), 37 deletions(-) create mode 100644 tests/unit_tests/opengl/gl_shader_tests.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efbc7b21..590b016f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: branches: [ "master" ] pull_request: - branches: [ "master" ] + branches: [ "dev" ] env: DISPLAY: ":99" diff --git a/src/opengl/gl_renderer.cpp b/src/opengl/gl_renderer.cpp index 439c1ae7..245108b9 100644 --- a/src/opengl/gl_renderer.cpp +++ b/src/opengl/gl_renderer.cpp @@ -52,10 +52,8 @@ std::unique_ptr GLRenderer::createIndexBuffer(const IndexBufferDescripto } std::unique_ptr GLRenderer::createShader(const ShaderDescriptor &descriptor, std::string_view source) { - NOX_ASSERT(GLShader::validateInput(descriptor, source)); - - auto shader = std::make_unique(descriptor); - NOX_ENSURE_RETURN_NULLPTR_MSG(shader->compile(source.data()), "Couldn't compile shader"); + auto shader = GLShader::create(descriptor, source); + NOX_ENSURE_RETURN_NULLPTR_MSG(shader != nullptr, "Couldn't create shader"); return shader; } diff --git a/src/opengl/gl_shader.cpp b/src/opengl/gl_shader.cpp index 443c5e77..79d1edef 100644 --- a/src/opengl/gl_shader.cpp +++ b/src/opengl/gl_shader.cpp @@ -19,19 +19,17 @@ GLenum mapShaderTypeToEnum(ShaderType type) { default: break; } - NOX_ASSERT(false); return GL_NONE; } } // namespace -bool GLShader::validateInput(const ShaderDescriptor &descriptor, std::string_view source) { - bool result = true; +std::unique_ptr GLShader::create(const ShaderDescriptor &descriptor, std::string_view source) { + auto shader = std::make_unique(descriptor); + NOX_ENSURE_RETURN_NULLPTR(shader->getHandle() != 0u); + NOX_ENSURE_RETURN_NULLPTR_MSG(shader->compile(source.data()), "Couldn't compile shader"); - result &= (!source.empty()); - result &= (mapShaderTypeToEnum(descriptor.type) != GL_NONE); - - return result; + return shader; } GLShader::GLShader(const ShaderDescriptor &descriptor) diff --git a/src/opengl/gl_shader.h b/src/opengl/gl_shader.h index e5f4e160..be1626e3 100644 --- a/src/opengl/gl_shader.h +++ b/src/opengl/gl_shader.h @@ -2,13 +2,14 @@ #include +#include #include namespace nox { class GLShader final : public Shader { public: - [[nodiscard]] static bool validateInput(const ShaderDescriptor &descriptor, std::string_view source); + [[nodiscard]] static std::unique_ptr create(const ShaderDescriptor &descriptor, std::string_view source); explicit GLShader(const ShaderDescriptor &descriptor); ~GLShader() override; @@ -17,10 +18,11 @@ class GLShader final : public Shader { [[nodiscard]] uint32_t getHandle() const { return m_handle; } - bool compile(const char *source) const; + private: + [[nodiscard]] bool compile(const char *source) const; private: - ShaderType m_type; + ShaderType m_type{ShaderType::NONE}; uint32_t m_handle{0u}; }; diff --git a/src/opengl/gl_swapchain.cpp b/src/opengl/gl_swapchain.cpp index 52e8a439..26570ea3 100644 --- a/src/opengl/gl_swapchain.cpp +++ b/src/opengl/gl_swapchain.cpp @@ -105,14 +105,14 @@ Vector2D GLSwapchain::getSize() const { } bool GLSwapchain::initializePresentationProgram() { - const GLShader presentVertexShader{{ShaderType::VERTEX}}; - NOX_ENSURE_RETURN_FALSE(presentVertexShader.compile(presentVertexShaderSource)); + const auto presentVertexShader = GLShader::create({ShaderType::VERTEX}, presentVertexShaderSource); + NOX_ENSURE_RETURN_FALSE(presentVertexShader != nullptr); - const GLShader presentFragmentShader{{ShaderType::FRAGMENT}}; - NOX_ENSURE_RETURN_FALSE(presentFragmentShader.compile(presentFragmentShaderSource)); + const auto presentFragmentShader = GLShader::create({ShaderType::FRAGMENT}, presentFragmentShaderSource); + NOX_ENSURE_RETURN_FALSE(presentFragmentShader != nullptr); - m_presentationProgram.attachShader(presentVertexShader.getHandle()); - m_presentationProgram.attachShader(presentFragmentShader.getHandle()); + m_presentationProgram.attachShader(presentVertexShader->getHandle()); + m_presentationProgram.attachShader(presentFragmentShader->getHandle()); NOX_ENSURE_RETURN_FALSE(m_presentationProgram.link()); return true; diff --git a/tests/unit_tests/opengl/CMakeLists.txt b/tests/unit_tests/opengl/CMakeLists.txt index 30d8f4be..04320c98 100644 --- a/tests/unit_tests/opengl/CMakeLists.txt +++ b/tests/unit_tests/opengl/CMakeLists.txt @@ -88,6 +88,7 @@ set(NOX_UNIT_TESTS_OPENGL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/gl_context_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gl_program_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/gl_shader_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gl_swapchain_tests.cpp ) diff --git a/tests/unit_tests/opengl/gl_program_tests.cpp b/tests/unit_tests/opengl/gl_program_tests.cpp index df5e0533..0ea4a9fd 100644 --- a/tests/unit_tests/opengl/gl_program_tests.cpp +++ b/tests/unit_tests/opengl/gl_program_tests.cpp @@ -35,7 +35,7 @@ TEST_F(GLProgramFixture, WhenCreatingProgramThenProgramIsSuccessfullyCreated) { EXPECT_NE(0u, program.getHandle()); } -TEST_F(GLProgramFixture, GivenCorrectShadersWhenLinkingProgramThenTrueIsReturned) { +TEST_F(GLProgramFixture, GivenCorrectShadersWhenCallingLinkProgramThenTrueIsReturned) { constexpr auto vertexShaderSource = R"( #version 460 core @@ -57,19 +57,20 @@ TEST_F(GLProgramFixture, GivenCorrectShadersWhenLinkingProgramThenTrueIsReturned } )"; - GLProgram program{}; - const GLShader vertexShader{{ShaderType::VERTEX}}; - const GLShader fragmentShader{{ShaderType::FRAGMENT}}; + const auto vertexShader = GLShader::create({ShaderType::VERTEX}, vertexShaderSource); + ASSERT_NE(nullptr, vertexShader); + + const auto fragmentShader = GLShader::create({ShaderType::FRAGMENT}, fragmentShaderSource); + ASSERT_NE(nullptr, fragmentShader); - vertexShader.compile(vertexShaderSource); - fragmentShader.compile(fragmentShaderSource); - program.attachShader(vertexShader.getHandle()); - program.attachShader(fragmentShader.getHandle()); + GLProgram program{}; + program.attachShader(vertexShader->getHandle()); + program.attachShader(fragmentShader->getHandle()); EXPECT_TRUE(program.link()); } -TEST_F(GLProgramFixture, GivenIncorrectShadersWhenLinkingProgramThenFalseIsReturned) { +TEST_F(GLProgramFixture, GivenIncorrectShadersWhenCallingLinkProgramThenFalseIsReturned) { constexpr auto vertexShaderSource = R"( #version 460 core @@ -82,14 +83,64 @@ TEST_F(GLProgramFixture, GivenIncorrectShadersWhenLinkingProgramThenFalseIsRetur } )"; - GLProgram program{}; - const GLShader vertexShader1{{ShaderType::VERTEX}}; - const GLShader vertexShader2{{ShaderType::VERTEX}}; + const auto vertexShader1 = GLShader::create({ShaderType::VERTEX}, vertexShaderSource); + ASSERT_NE(nullptr, vertexShader1); + + const auto vertexShader2 = GLShader::create({ShaderType::VERTEX}, vertexShaderSource); + ASSERT_NE(nullptr, vertexShader2); - vertexShader1.compile(vertexShaderSource); - vertexShader2.compile(vertexShaderSource); - program.attachShader(vertexShader1.getHandle()); - program.attachShader(vertexShader2.getHandle()); + GLProgram program{}; + program.attachShader(vertexShader1->getHandle()); + program.attachShader(vertexShader2->getHandle()); EXPECT_FALSE(program.link()); } + +TEST_F(GLProgramFixture, WhenCallingBindProgramThenCorrectProgramIsBound) { + constexpr auto vertexShaderSource = R"( + #version 460 core + + out gl_PerVertex { + vec4 gl_Position; + }; + + void main() { + gl_Position = vec4(1.0); + } + )"; + constexpr auto fragmentShaderSource = R"( + #version 460 core + + out vec4 fragmentColor; + + void main() { + fragmentColor = vec4(1.0); + } + )"; + + const auto vertexShader = GLShader::create({ShaderType::VERTEX}, vertexShaderSource); + ASSERT_NE(nullptr, vertexShader); + + const auto fragmentShader = GLShader::create({ShaderType::FRAGMENT}, fragmentShaderSource); + ASSERT_NE(nullptr, fragmentShader); + + GLProgram program{}; + program.attachShader(vertexShader->getHandle()); + program.attachShader(fragmentShader->getHandle()); + ASSERT_TRUE(program.link()); + + const auto handle = program.getHandle(); + EXPECT_NE(0u, handle); + + program.bind(); + + GLint currentlyBoundProgramHandle = GL_NONE; + glGetIntegerv(GL_CURRENT_PROGRAM, ¤tlyBoundProgramHandle); + + EXPECT_EQ(handle, currentlyBoundProgramHandle); + + program.unbind(); + glGetIntegerv(GL_CURRENT_PROGRAM, ¤tlyBoundProgramHandle); + + EXPECT_EQ(0u, currentlyBoundProgramHandle); +} diff --git a/tests/unit_tests/opengl/gl_shader_tests.cpp b/tests/unit_tests/opengl/gl_shader_tests.cpp new file mode 100644 index 00000000..438a202c --- /dev/null +++ b/tests/unit_tests/opengl/gl_shader_tests.cpp @@ -0,0 +1,89 @@ +#include "src/opengl/gl_context.h" +#include "src/opengl/gl_shader.h" + +#include "tests/fixtures/window_fixture.h" + +#include +#include + +using namespace nox; + +struct GLShaderFixture : public tests::WindowFixture, + public ::testing::Test { + void SetUp() override { + tests::WindowFixture::setUp(); + + SurfaceDescriptor surfaceDescriptor{}; + surfaceDescriptor.surfaceBackendDescriptor = surfaceBackendDescriptor; + surfaceDescriptor.surfaceAttributesDescriptor = OpenGLSurfaceAttributesDescriptor{}; + + context = GLContext::create(surfaceDescriptor); + ASSERT_NE(nullptr, context); + } + + void TearDown() override { + tests::WindowFixture::tearDown(); + } + + std::unique_ptr context{nullptr}; +}; + +TEST_F(GLShaderFixture, GivenValidShaderDescriptorAndSourceWhenCallingCreateShaderThenShaderIsSuccessfullyCreated) { + using TestParams = std::array, static_cast(ShaderType::MAX) - 1>; + constexpr TestParams testParams{{ + {ShaderType::VERTEX, GL_VERTEX_SHADER}, + {ShaderType::FRAGMENT, GL_FRAGMENT_SHADER}, + {ShaderType::TESS_CONTROL, GL_TESS_CONTROL_SHADER}, + {ShaderType::TESS_EVALUATION, GL_TESS_EVALUATION_SHADER}, + {ShaderType::GEOMETRY, GL_GEOMETRY_SHADER}, + {ShaderType::COMPUTE, GL_COMPUTE_SHADER}, + }}; + constexpr auto shaderSource = R"( + #version 460 core + + void main() {} + )"; + + for (const auto &[shaderType, expectedShaderType] : testParams) { + ShaderDescriptor shaderDescriptor{}; + shaderDescriptor.type = shaderType; + + const auto shader = GLShader::create(shaderDescriptor, shaderSource); + ASSERT_NE(nullptr, shader); + + GLint glShaderType = GL_NONE; + glGetShaderiv(shader->getHandle(), GL_SHADER_TYPE, &glShaderType); + + EXPECT_NE(0u, shader->getHandle()); + EXPECT_EQ(shaderType, shader->getType()); + EXPECT_EQ(expectedShaderType, glShaderType); + } +} + +TEST_F(GLShaderFixture, GivenInvalidShaderDescriptorWhenCallingCreateShaderThenNullptrIsReturned) { + constexpr auto shaderSource = R"( + #version 460 core + + void main() {} + )"; + + ShaderDescriptor shaderDescriptor{}; + shaderDescriptor.type = ShaderType::NONE; + + const auto shader = GLShader::create(shaderDescriptor, shaderSource); + EXPECT_EQ(nullptr, shader); +} + +TEST_F(GLShaderFixture, GivenInvalidShaderSourceWhenCallingCreateShaderThenNullptrIsReturned) { + constexpr auto shaderSource = R"( + #version 460 core + + main() {} + )"; + + ShaderDescriptor shaderDescriptor{}; + shaderDescriptor.type = ShaderType::VERTEX; + + const auto shader = GLShader::create(shaderDescriptor, shaderSource); + EXPECT_EQ(nullptr, shader); +}