diff --git a/include/uui/application.hpp b/include/uui/application.hpp index 7410bba..1a237dc 100644 --- a/include/uui/application.hpp +++ b/include/uui/application.hpp @@ -12,5 +12,8 @@ class Application public: virtual void run() = 0; virtual std::weak_ptr add_window(int width, int height, const std::string& title) = 0; + +protected: + static bool application_created; }; } // namespace uui diff --git a/include/uui/opengl/box.hpp b/include/uui/opengl/box.hpp index 07325bf..1fe09b1 100644 --- a/include/uui/opengl/box.hpp +++ b/include/uui/opengl/box.hpp @@ -16,6 +16,7 @@ public: private: GLuint gl_vbo; GLuint gl_vao; + GLuint gl_ebo; GLuint gl_program; }; diff --git a/make.py b/make.py index a68eb57..f16bda7 100644 --- a/make.py +++ b/make.py @@ -19,7 +19,7 @@ class Config: SOURCE_DIR = Path('src') COMMON_FLAGS = '-std=c++17 -fno-semantic-interposition' - COMMON_DEBUG_FLAGS = '-g' + COMMON_DEBUG_FLAGS = '-g -DDEBUG' COMMON_RELEASE_FLAGS = '-O2' # -flto' COMPILE_FLAGS = f'-Wall -I{INCLUDE_DIR} `pkg-config --cflags glfw3 vulkan gl x11`' LINK_FLAGS = '-lpthread -ldl `pkg-config --libs glfw3 vulkan gl x11`' @@ -41,7 +41,7 @@ def main(): out_path.parent.mkdir(parents=True) shutil.copy(shader_path, out_path) - Config.PRE_COMPILE_FUNCTION = pre_compile + # Config.PRE_COMPILE_FUNCTION = pre_compile make(Config) diff --git a/src/test/example_gl.cpp b/src/test/example_gl.cpp index 74e1aeb..1b8ab33 100644 --- a/src/test/example_gl.cpp +++ b/src/test/example_gl.cpp @@ -10,12 +10,21 @@ int main() try { - uui::GlApplication app; - auto window_1 = app.add_window(800, 600, "OpenGl!"); - window_1.lock()->add_box(); - auto window_2 = app.add_window(600, 600, "OpenGL 2!"); - window_2.lock()->add_box(); - app.run(); + { + uui::GlApplication app; + // uui::GlApplication app2; // exception : application already created + auto window_1 = app.add_window(800, 600, "OpenGl!"); + window_1.lock()->add_box(); + app.run(); + } + { + uui::GlApplication app; + auto window_1 = app.add_window(800, 600, "OpenGl!"); + window_1.lock()->add_box(); + auto window_2 = app.add_window(600, 600, "OpenGL 2!"); + window_2.lock()->add_box(); + app.run(); + } } catch(const std::exception& error) { diff --git a/src/uui/application.cpp b/src/uui/application.cpp new file mode 100644 index 0000000..84209ca --- /dev/null +++ b/src/uui/application.cpp @@ -0,0 +1,3 @@ +#include "uui/application.hpp" + +bool uui::Application::application_created = false; diff --git a/src/uui/opengl/application.cpp b/src/uui/opengl/application.cpp index 78931c0..5295457 100644 --- a/src/uui/opengl/application.cpp +++ b/src/uui/opengl/application.cpp @@ -5,13 +5,23 @@ uui::GlApplication::GlApplication() { - std::cout << "Creating the GlApplication" << std::endl; + if(application_created) + throw std::runtime_error("GlApplication error: the application already created"); + if constexpr(debug_mode) + std::cout << "Creating the GlApplication in debug mode" << std::endl; + else + std::cout << "Creating the GlApplication in release mode" << std::endl; // Init GLFW glfwInit(); // Set all the required options for GLFW glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + if constexpr(uui::GlApplication::debug_mode) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); + + application_created = true; } uui::GlApplication::~GlApplication() @@ -19,6 +29,7 @@ uui::GlApplication::~GlApplication() std::cout << "Destroying the GlApplication" << std::endl; windows.clear(); glfwTerminate(); + application_created = false; } void uui::GlApplication::run() diff --git a/src/uui/opengl/box.cpp b/src/uui/opengl/box.cpp index e09384f..37ff9d4 100644 --- a/src/uui/opengl/box.cpp +++ b/src/uui/opengl/box.cpp @@ -5,6 +5,42 @@ #include "uui/opengl/application.hpp" #include "uui/shader.hpp" +GLenum glCheckError_(const char* file, int line) +{ + GLenum errorCode; + while((errorCode = glGetError()) != GL_NO_ERROR) + { + std::string error; + switch(errorCode) + { + case GL_INVALID_ENUM: + error = "INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + error = "STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + error = "STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + error = "OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + error = "INVALID_FRAMEBUFFER_OPERATION"; + break; + } + std::cout << error << " | " << file << " (" << line << ")" << std::endl; + } + return errorCode; +} +#define glCheckError() glCheckError_(__FILE__, __LINE__) + uui::GlBox::GlBox() { std::cout << "Creating a GlBox" << std::endl; @@ -13,7 +49,8 @@ uui::GlBox::GlBox() GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); { auto vertex_src = readFile("bin/uui/opengl/shaders/main.vert"); - auto vertex_code = vertex_src.data(); + auto vertex_str = std::string(vertex_src.begin(), vertex_src.end()); + auto vertex_code = vertex_str.c_str(); glShaderSource(vertex_shader, 1, &vertex_code, NULL); glCompileShader(vertex_shader); if constexpr(uui::GlApplication::debug_mode) @@ -24,7 +61,7 @@ uui::GlBox::GlBox() if(!success) { glGetShaderInfoLog(vertex_shader, 512, NULL, info); - std::cout << "GlBox error : " << info << std::endl; + std::cout << "GlBox vertex shader creation error : " << info << std::endl; } } } @@ -33,7 +70,8 @@ uui::GlBox::GlBox() GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); { auto fragment_src = readFile("bin/uui/opengl/shaders/main.frag"); - auto fragment_code = fragment_src.data(); + auto fragment_str = std::string(fragment_src.begin(), fragment_src.end()); + auto fragment_code = fragment_str.c_str(); glShaderSource(fragment_shader, 1, &fragment_code, NULL); glCompileShader(fragment_shader); if constexpr(uui::GlApplication::debug_mode) @@ -44,7 +82,7 @@ uui::GlBox::GlBox() if(!success) { glGetShaderInfoLog(fragment_shader, 512, NULL, info); - std::cout << "GlBox error : " << info << std::endl; + std::cout << "GlBox fragment shader creation error : " << info << std::endl; } } } @@ -52,7 +90,11 @@ uui::GlBox::GlBox() // Creating shader program gl_program = glCreateProgram(); glAttachShader(gl_program, vertex_shader); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); glAttachShader(gl_program, fragment_shader); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); glLinkProgram(gl_program); if constexpr(uui::GlApplication::debug_mode) { @@ -68,20 +110,28 @@ uui::GlBox::GlBox() glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); - // clang-format off - float vertices[] = { - -0.5f, -0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - 0.0f, 0.5f, 0.0f - }; - // clang-format on glGenVertexArrays(1, &gl_vao); glGenBuffers(1, &gl_vbo); + glGenBuffers(1, &gl_ebo); // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). glBindVertexArray(gl_vao); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); + // clang-format off + float vertices[] = { + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f // top left + }; + // clang-format on glBindBuffer(GL_ARRAY_BUFFER, gl_vbo); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); glVertexAttribPointer( 0, // index @@ -89,10 +139,21 @@ uui::GlBox::GlBox() GL_FLOAT, // type GL_FALSE, // enable normalization, 3 * sizeof(float), // stride - nullptr // offset + (void*)0 // offset ); glEnableVertexAttribArray(0); + unsigned int indices[] = { + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle + }; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_ebo); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); + // unbinding VBO/VAO glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -102,13 +163,16 @@ uui::GlBox::~GlBox() { std::cout << "Closing a GlBox" << std::endl; glDeleteVertexArrays(1, &gl_vao); - glDeleteProgram(gl_program); glDeleteBuffers(1, &gl_vbo); + glDeleteBuffers(1, &gl_ebo); + glDeleteProgram(gl_program); } void uui::GlBox::render() { - glBindVertexArray(gl_vao); - glDrawArrays(GL_TRIANGLES, 0, 3); glUseProgram(gl_program); + glBindVertexArray(gl_vao); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)0); + if constexpr(uui::GlApplication::debug_mode) + glCheckError(); } diff --git a/src/uui/opengl/window.cpp b/src/uui/opengl/window.cpp index 5ace100..4692147 100644 --- a/src/uui/opengl/window.cpp +++ b/src/uui/opengl/window.cpp @@ -4,6 +4,63 @@ #include "uui/opengl/box.hpp" +static void APIENTRY +glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, GLsizei length, const char* message, const void* userParam) +{ + // ignore non-significant error/warning codes + if(id == 131169 || id == 131185 || id == 131218 || id == 131204) + return; + + std::cout << "---------------" << std::endl; + std::cout << "Debug message (" << id << "): " << message << std::endl; + + // clang-format off + switch (source) + { + case GL_DEBUG_SOURCE_API: std::cout << "Source: API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: std::cout << "Source: Window System"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: std::cout << "Source: Shader Compiler"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: std::cout << "Source: Third Party"; break; + case GL_DEBUG_SOURCE_APPLICATION: std::cout << "Source: Application"; break; + case GL_DEBUG_SOURCE_OTHER: std::cout << "Source: Other"; break; + } std::cout << std::endl; + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: std::cout << "Type: Error"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: std::cout << "Type: Deprecated Behaviour"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: std::cout << "Type: Undefined Behaviour"; break; + case GL_DEBUG_TYPE_PORTABILITY: std::cout << "Type: Portability"; break; + case GL_DEBUG_TYPE_PERFORMANCE: std::cout << "Type: Performance"; break; + case GL_DEBUG_TYPE_MARKER: std::cout << "Type: Marker"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: std::cout << "Type: Push Group"; break; + case GL_DEBUG_TYPE_POP_GROUP: std::cout << "Type: Pop Group"; break; + case GL_DEBUG_TYPE_OTHER: std::cout << "Type: Other"; break; + } std::cout << std::endl; + + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH: std::cout << "Severity: high"; break; + case GL_DEBUG_SEVERITY_MEDIUM: std::cout << "Severity: medium"; break; + case GL_DEBUG_SEVERITY_LOW: std::cout << "Severity: low"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: std::cout << "Severity: notification"; break; + } std::cout << std::endl; + // clang-format on + std::cout << std::endl; +} + +static void glfw_resize_callback(GLFWwindow* window, int width, int height) +{ + glfwMakeContextCurrent(window); + glViewport(0, 0, width, height); +} + +static void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); +} + uui::GlWindow::GlWindow(const uui::GlApplication& app, int width, int height, const std::string& title): app(app) { std::cout << "Creating a GlWindow" << std::endl; @@ -14,8 +71,24 @@ uui::GlWindow::GlWindow(const uui::GlApplication& app, int width, int height, co throw std::runtime_error("GLWindow : failed to create GLFW window"); glfwMakeContextCurrent(glfw_window); + glfwSetFramebufferSizeCallback(glfw_window, glfw_resize_callback); + glfwSetKeyCallback(glfw_window, glfw_key_callback); + if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) throw std::runtime_error("GLWindow : Failed to initialize OpenGL context"); + + if constexpr(uui::GlApplication::debug_mode) + { + GLint flags; + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + if(flags & GL_CONTEXT_FLAG_DEBUG_BIT) + { + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(glDebugOutput, nullptr); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); + } + } } uui::GlWindow::~GlWindow()