#ifdef GLSHADER_TEST #include <iostream> #include <iomanip> #include <stdexcept> #include <GL/glew.h> #include <GLFW/glfw3.h> #include "str.hpp" #include "shader.hpp" static auto debugMessageDisable = false; // Only check the first line, the others are from the driver dependent OpenGL // info log. #define EXPECT_EXCEPTION(CODE, WHAT) \ { \ std::string what; \ debugMessageDisable = true; \ try { \ CODE; \ } catch (std::exception const & e) { \ what = e.what(); \ what = what.substr(0, what.find("\n")); \ } \ debugMessageDisable = false; \ if (what.empty()) \ throw std::runtime_error{STR( \ "Expected exception \"" << WHAT << "\", got none." \ )}; \ if (what != WHAT) \ throw std::runtime_error{STR( \ "Expected exception \"" << WHAT << "\", got \"" << what \ << "\"." \ )}; \ } void test() { EXPECT_EXCEPTION( Shader({ "bad_noextension" }), "Failed to infer shader type of 'bad_noextension' of shader program 'bad_noextension'; no file extension." ) EXPECT_EXCEPTION( Shader({ "bad.unknownextension" }), "Failed to infer shader type of 'bad.unknownextension' of shader program 'bad.unknownextension'; unknown file extension 'unknownextension'." ) EXPECT_EXCEPTION( Shader({ "bad_nonexistent.vert" }), "Failed to open vert shader 'bad_nonexistent.vert' of shader program 'bad_nonexistent.vert'." ) EXPECT_EXCEPTION( Shader({ "bad_compile.vert" }), "Failed to compile vert shader 'bad_compile.vert' of shader program 'bad_compile.vert'." // "0:4(5): error: main() must return void" // "0:4(1): error: function `main' has non-void return type int, but no return statement" ) EXPECT_EXCEPTION( Shader({ "bad_link.vert", "good.frag" }), "Failed to link shader program 'bad_link.vert', 'good.frag'." // "error: vertex shader does not write to `gl_Position'. " ) { Shader shader{std::move(Shader({ "good.vert", "good.frag", }))}; shader.use(); shader = Shader({ "good.vert", "good.frag", }); shader.use(); } Shader shader({ "good.vert", "good.frag", }); Shader shaderMove; Shader shaderCopy; std::cout << "Moving shader" << std::endl; shaderMove = Shader({"test.vert", "test.frag"}); std::cout << "Copying shader" << std::endl; shaderCopy = shaderMove; shader.use(); shader.validate(); glm::vec4 color(1, 0, 0, 1); EXPECT_EXCEPTION( Shader({ "good.vert", "good.frag" }).uniform("noncurrent", color), "Failed to set uniform 'noncurrent' of shader program 'good.vert', 'good.frag'; program is not current." ) EXPECT_EXCEPTION( shader.uniform("nonexistent", color), "Failed to get location of uniform 'nonexistent' of shader program 'good.vert', 'good.frag'." ) shader.uniform("color", color); struct Matrices { glm::mat4 view; glm::mat4 projection; } matrices; EXPECT_EXCEPTION( Shader({ "good.vert", "good.frag" }).uniform("noncurrent", matrices), "Failed to set uniform block 'noncurrent' of shader program 'good.vert', 'good.frag'; program is not current." ) EXPECT_EXCEPTION( shader.uniform("nonexistent", matrices), "Failed to get index of uniform block 'nonexistent' in shader program 'good.vert', 'good.frag'." ) // (Allocate), set data and bind UBO implicitly. // shader.uniform("matrices", matrices); // (Allocate), set data and bind UBO explicitly. Shader::uniform_buffer("matrices", matrices); // shader.uniform("matrices", "matrices"); EXPECT_EXCEPTION( Shader::uniform_buffer("matrices", matrices, GL_STATIC_DRAW), "Failed to set data of uniform buffer 'matrices'; data has usage GL_STATIC_DRAW but buffer has usage GL_DYNAMIC_DRAW." ) EXPECT_EXCEPTION( Shader::uniform_buffer("matrices", color), "Failed to set data of uniform buffer 'matrices'; data has size 16 but buffer has size 128." ) EXPECT_EXCEPTION( Shader::uniform_buffer_delete("nonexistent"), "Failed to delete uniform buffer 'nonexistent'; does not exist." ) // Delete UBO. // Shader::uniform_buffer_delete("matrices"); } void errorCallback(int /* error */, char const * description) { std::cerr << "GLFW ERROR: " << description << std::endl; exit(1); } void APIENTRY debugMessageCallback( GLenum /* source */, GLenum /* type */, GLuint /* id */, GLenum severity, GLsizei /* length */, const GLchar *message, const void * /* userParam */ ) { if (debugMessageDisable) return; std::cerr << "GL DEBUG MESSAGE: " << message << std::endl; if ( severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM ) exit(1); } int main() { try { glfwInit(); glfwSetErrorCallback(errorCallback); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); auto window = glfwCreateWindow( 640, 480, "glshader test", nullptr, nullptr ); glfwMakeContextCurrent(window); glfwSwapInterval(0); glewInit(); #define GLSHADER_TEST_PRINT(NAME) \ std::cout << std::setw(24+2) << std::left << #NAME ": " << \ glGetString(GL_##NAME) << std::endl; GLSHADER_TEST_PRINT(VENDOR); GLSHADER_TEST_PRINT(RENDERER); GLSHADER_TEST_PRINT(VERSION); GLSHADER_TEST_PRINT(SHADING_LANGUAGE_VERSION); glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE ); glDebugMessageCallback(debugMessageCallback, 0); std::cout << "---\n"; int frame = 0; glfwSetTime(0); while (glfwGetTime() < 1) { ++frame; test(); glBegin(GL_TRIANGLE_STRIP); glVertex3f(-0.5, -0.5, 0); glVertex3f(-0.5, +0.5, 0); glVertex3f(+0.5, +0.5, 0); glVertex3f(+0.5, -0.5, 0); glVertex3f(-0.5, -0.5, 0); glEnd(); glfwSwapBuffers(window); glfwPollEvents(); } std::cout << "---\n"; std::cout << "Frames run: " << frame << "\n"; glfwDestroyWindow(window); glfwTerminate(); } catch (std::exception const & e) { std::cerr << e.what() << std::endl; return 1; } } #endif // GLSHADER_TEST