diff --git a/CMakeLists.txt b/CMakeLists.txt index d4aa351..bc40e61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG) find_package(OpenAL REQUIRED CONFIG) find_library(physfs REQUIRED NAMES physfs-static PATHS "${CMAKE_PREFIX_PATH}/lib") + # Determine dependencies set(STATIC_LIBS dr_wav diff --git a/src/application.cpp b/src/application.cpp index b981286..edfec4f 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -49,7 +49,7 @@ application::application(): window_dimensions({0, 0}), viewport_dimensions({0, 0}), mouse_position({0, 0}), - update_rate(5.0), + update_rate(60.0), logger(nullptr), sdl_window(nullptr), sdl_gl_context(nullptr) diff --git a/src/gl/gl.hpp b/src/gl/gl.hpp index aeec32b..dada098 100644 --- a/src/gl/gl.hpp +++ b/src/gl/gl.hpp @@ -31,10 +31,10 @@ namespace gl {} #include "pixel-format.hpp" #include "pixel-type.hpp" #include "rasterizer.hpp" -#include "shader.hpp" #include "shader-input.hpp" +#include "shader-object.hpp" #include "shader-program.hpp" -#include "shader-type.hpp" +#include "shader-stage.hpp" #include "shader-variable-type.hpp" #include "texture-2d.hpp" #include "texture-cube.hpp" diff --git a/src/gl/shader-object.cpp b/src/gl/shader-object.cpp new file mode 100644 index 0000000..8733a8b --- /dev/null +++ b/src/gl/shader-object.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "gl/shader-object.hpp" +#include +#include + +namespace gl { + +static constexpr GLenum gl_shader_type_lut[] = +{ + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + GL_GEOMETRY_SHADER +}; + +shader_object::shader_object(shader_stage stage): + gl_shader_id(0), + stage(stage), + compiled(false) +{ + // Look up OpenGL shader type enumeration that corresponds to the given stage + GLenum gl_shader_type = gl_shader_type_lut[static_cast(stage)]; + + // Create an OpenGL shader object + gl_shader_id = glCreateShader(gl_shader_type); + + // Handle OpenGL errors + if (!gl_shader_id) + { + throw std::runtime_error("An error occurred while creating an OpenGL shader object."); + } +} + +shader_object::~shader_object() +{ + // Flags the OpenGL shader object for deletion + glDeleteShader(gl_shader_id); +} + +void shader_object::source(const char* buffer, std::size_t size) +{ + // Replace OpenGL shader object source code + GLint gl_length = size; + glShaderSource(gl_shader_id, 1, &buffer, &gl_length); + + // Handle OpenGL errors + switch (glGetError()) + { + case GL_INVALID_VALUE: + throw std::runtime_error("Shader object handle is not a value generated by OpenGL."); + break; + + case GL_INVALID_OPERATION: + throw std::runtime_error("Shader object handle is not a shader object."); + break; + } +} + +bool shader_object::compile() +{ + // Compile OpenGL shader object + glCompileShader(gl_shader_id); + + // Handle OpenGL errors + switch (glGetError()) + { + case GL_INVALID_VALUE: + throw std::runtime_error("Shader object handle is not a value generated by OpenGL."); + break; + + case GL_INVALID_OPERATION: + throw std::runtime_error("Shader object handle is not a shader object."); + break; + } + + // Get OpenGL shader object compilation status + GLint gl_compile_status; + glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &gl_compile_status); + compiled = (gl_compile_status == GL_TRUE); + + // Get OpenGL shader object info log length + GLint gl_info_log_length; + glGetShaderiv(gl_shader_id, GL_INFO_LOG_LENGTH, &gl_info_log_length); + + if (gl_info_log_length > 0) + { + // Resize string to accommodate OpenGL shader object info log + info_log.resize(gl_info_log_length); + + // Read OpenGL shader object info log into string + glGetShaderInfoLog(gl_shader_id, gl_info_log_length, &gl_info_log_length, info_log.data()); + + // Remove redundant null terminator from string + info_log.pop_back(); + } + else + { + // Empty info log + info_log.clear(); + } + + // Return compilation status + return compiled; +} + +} // namespace gl diff --git a/src/gl/shader-object.hpp b/src/gl/shader-object.hpp new file mode 100644 index 0000000..ff086e2 --- /dev/null +++ b/src/gl/shader-object.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_SHADER_OBJECT_HPP +#define ANTKEEPER_GL_SHADER_OBJECT_HPP + +#include "gl/shader-stage.hpp" +#include +#include + +namespace gl { + +class shader_program; + +/** + * Shader object which can be compiled and linked to a shader program. + * + * @see gl::shader_program + * @see gl::shader_stage + */ +class shader_object +{ +public: + /** + * Creates an empty shader object for the specified shader stage. + * + * @param stage Shader stage in which this shader object will be used. + * + * @exception std::runtime_error An error occurred while creating an OpenGL shader object. + */ + shader_object(shader_stage stage); + + /** + * Destroys a shader object. + */ + ~shader_object(); + + /** + * Replaces the source code of the shader object. + * + * @param buffer Buffer containing shader object source code. + * @param size Size of the source code, in bytes. + * + * @exception std::runtime_error Shader object handle is not a value generated by OpenGL. + * @exception std::runtime_error Shader object handle is not a shader object. + */ + void source(const char* buffer, std::size_t size); + + /** + * Compiles the shader object. + * + * @return `true` if the shader object was compiled successfully, `false` otherwise. If compilation fails, check the info log via shader_object::get_info_log() for more information. + * + * @exception std::runtime_error Shader object handle is not a value generated by OpenGL. + * @exception std::runtime_error Shader object handle is not a shader object. + * + * @see shader_object::get_info_log() + */ + bool compile(); + + /// Returns the shader stage of this shader object. + shader_stage get_stage() const; + + /// Returns the shader object info log, which is updated when the shader is compiled. + const std::string& get_info_log() const; + + /// Returns `true` if the shader object has been successfully compiled, `false` otherwise. + bool was_compiled() const; + + shader_object(const shader_object&) = delete; + shader_object& operator=(const shader_object&) = delete; + +private: + friend class shader_program; + + + unsigned int gl_shader_id; + shader_stage stage; + bool compiled; + std::string info_log; +}; + +inline shader_stage shader_object::get_stage() const +{ + return stage; +} + +inline const std::string& shader_object::get_info_log() const +{ + return info_log; +} + +inline bool shader_object::was_compiled() const +{ + return compiled; +} + +} // namespace gl + +#endif // ANTKEEPER_GL_SHADER_OBJECT_HPP diff --git a/src/gl/shader-program.cpp b/src/gl/shader-program.cpp index 6fdd6ce..1e3a943 100644 --- a/src/gl/shader-program.cpp +++ b/src/gl/shader-program.cpp @@ -18,7 +18,7 @@ */ #include "gl/shader-program.hpp" -#include "gl/shader.hpp" +#include "gl/shader-object.hpp" #include "gl/shader-variable-type.hpp" #include "gl/shader-input.hpp" #include @@ -26,12 +26,12 @@ namespace gl { -shader_program::shader_program(const std::list& shaders): +shader_program::shader_program(const std::list& shaders): gl_program_id(0) { gl_program_id = glCreateProgram(); - for (shader* shader: shaders) + for (shader_object* shader: shaders) { glAttachShader(gl_program_id, shader->gl_shader_id); } @@ -45,7 +45,7 @@ shader_program::shader_program(const std::list& shaders): throw std::runtime_error(get_info_log().c_str()); } - for (shader* shader: shaders) + for (shader_object* shader: shaders) { glDetachShader(gl_program_id, shader->gl_shader_id); } diff --git a/src/gl/shader-program.hpp b/src/gl/shader-program.hpp index 61b593e..672628a 100644 --- a/src/gl/shader-program.hpp +++ b/src/gl/shader-program.hpp @@ -27,7 +27,7 @@ namespace gl { -class shader; +class shader_object; class rasterizer; class shader_input; @@ -39,7 +39,7 @@ public: * * @param shaders List of shaders to be linked to the program. Note that after the shader program has been created, these shaders can be deleted if desired with no effect on the shader program. */ - explicit shader_program(const std::list& shaders); + explicit shader_program(const std::list& shaders); ~shader_program(); shader_program(const shader_program&) = delete; diff --git a/src/gl/shader-type.hpp b/src/gl/shader-stage.hpp similarity index 73% rename from src/gl/shader-type.hpp rename to src/gl/shader-stage.hpp index 648c0b7..669a854 100644 --- a/src/gl/shader-type.hpp +++ b/src/gl/shader-stage.hpp @@ -17,19 +17,27 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GL_SHADER_TYPE_HPP -#define ANTKEEPER_GL_SHADER_TYPE_HPP +#ifndef ANTKEEPER_GL_SHADER_STAGE_HPP +#define ANTKEEPER_GL_SHADER_STAGE_HPP namespace gl { -enum class shader_type +/** + * Enumerates all supported shader stages. + */ +enum class shader_stage { + /// Indicates a vertex shader stage. vertex, + + /// Indicates a fragment shader stage. fragment, + + /// Indicates a geometry shader stage. geometry }; } // namespace gl -#endif // ANTKEEPER_GL_SHADER_TYPE_HPP +#endif // ANTKEEPER_GL_SHADER_STAGE_HPP diff --git a/src/gl/shader.cpp b/src/gl/shader.cpp deleted file mode 100644 index 91a9ec5..0000000 --- a/src/gl/shader.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2021 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/shader.hpp" -#include -#include - -namespace gl { - -static constexpr GLenum shader_type_lut[] = -{ - GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER, - GL_GEOMETRY_SHADER -}; - -shader::shader(shader_type type, const std::string& source): - gl_shader_id(0), - type(type) -{ - GLenum gl_shader_type = shader_type_lut[static_cast(type)]; - const char* source_c_str = source.c_str(); - - gl_shader_id = glCreateShader(gl_shader_type); - glShaderSource(gl_shader_id, 1, &source_c_str, nullptr); - glCompileShader(gl_shader_id); - - GLint status; - glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &status); - if (status == GL_FALSE) - { - throw std::runtime_error(get_info_log().c_str()); - } -} - -shader::~shader() -{ - glDeleteShader(gl_shader_id); -} - -std::string shader::get_info_log() const -{ - GLint length; - glGetShaderiv(gl_shader_id, GL_INFO_LOG_LENGTH, &length); - - if (length > 0) - { - std::string log(length, '\0'); - glGetShaderInfoLog(gl_shader_id, length, &length, &log[0]); - return log; - } - - return std::string(); -} - -} // namespace gl diff --git a/src/gl/shader.hpp b/src/gl/shader.hpp deleted file mode 100644 index 2eeadd8..0000000 --- a/src/gl/shader.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2021 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_SHADER_HPP -#define ANTKEEPER_GL_SHADER_HPP - -#include -#include - -namespace gl { - -enum class shader_type; -class shader_program; - -class shader -{ -public: - shader(shader_type type, const std::string& source); - ~shader(); - - shader(const shader&) = delete; - shader& operator=(const shader&) = delete; - - shader_type get_type() const; - -private: - friend class shader_program; - - std::string get_info_log() const; - - unsigned int gl_shader_id; - shader_type type; -}; - -inline shader_type shader::get_type() const -{ - return type; -} - -} // namespace gl - -#endif // ANTKEEPER_GL_SHADER_HPP - diff --git a/src/renderer/passes/material-pass.cpp b/src/renderer/passes/material-pass.cpp index 0cddda5..67feb56 100644 --- a/src/renderer/passes/material-pass.cpp +++ b/src/renderer/passes/material-pass.cpp @@ -22,8 +22,6 @@ #include "resources/resource-manager.hpp" #include "gl/rasterizer.hpp" #include "gl/framebuffer.hpp" -#include "gl/shader.hpp" -#include "gl/shader-type.hpp" #include "gl/shader-program.hpp" #include "gl/shader-input.hpp" #include "gl/vertex-buffer.hpp" diff --git a/src/renderer/passes/ui-pass.cpp b/src/renderer/passes/ui-pass.cpp index f8c598f..c92735d 100644 --- a/src/renderer/passes/ui-pass.cpp +++ b/src/renderer/passes/ui-pass.cpp @@ -21,8 +21,6 @@ #include "resources/resource-manager.hpp" #include "gl/rasterizer.hpp" #include "gl/framebuffer.hpp" -#include "gl/shader.hpp" -#include "gl/shader-type.hpp" #include "gl/shader-program.hpp" #include "gl/shader-input.hpp" #include "gl/vertex-buffer.hpp" diff --git a/src/resources/shader-program-loader.cpp b/src/resources/shader-program-loader.cpp index cc86484..e3c84d0 100644 --- a/src/resources/shader-program-loader.cpp +++ b/src/resources/shader-program-loader.cpp @@ -20,10 +20,11 @@ #include "resources/resource-loader.hpp" #include "resources/resource-manager.hpp" #include "resources/text-file.hpp" -#include "gl/shader-type.hpp" -#include "gl/shader.hpp" +#include "gl/shader-stage.hpp" +#include "gl/shader-object.hpp" #include "gl/shader-program.hpp" #include +#include #include /** @@ -104,18 +105,18 @@ static void inject_debug_macro(text_file* source) /** * Injects a shader type macro definition after the `#version` directive. */ -static void inject_shader_type_macro(text_file* source, gl::shader_type type) +static void inject_shader_stage_macro(text_file* source, gl::shader_stage stage) { const char* vertex_macro = "#define _VERTEX"; const char* fragment_macro = "#define _FRAGMENT"; const char* geometry_macro = "#define _GEOMETRY"; const char* macro = nullptr; - if (type == gl::shader_type::vertex) + if (stage == gl::shader_stage::vertex) macro = vertex_macro; - else if (type == gl::shader_type::fragment) + else if (stage == gl::shader_stage::fragment) macro = fragment_macro; - else if (type == gl::shader_type::geometry) + else if (stage == gl::shader_stage::geometry) macro = geometry_macro; // For each line in the source @@ -124,7 +125,7 @@ static void inject_shader_type_macro(text_file* source, gl::shader_type type) // Tokenize line std::vector tokens = tokenize((*source)[i]); - // Inject shader type macro + // Inject shader stage macro if (!tokens.empty() && tokens[0] == "#version") { source->insert(source->begin() + i + 1, macro); @@ -133,9 +134,9 @@ static void inject_shader_type_macro(text_file* source, gl::shader_type type) } } -static std::list detect_shader_types(text_file* source) +static std::unordered_set detect_shader_stages(text_file* source) { - std::list types; + std::unordered_set types; // For each line in the source for (std::size_t i = 0; i < source->size(); ++i) @@ -148,15 +149,15 @@ static std::list detect_shader_types(text_file* source) { if (tokens[1] == "vertex") { - types.push_back(gl::shader_type::vertex); + types.insert(gl::shader_stage::vertex); } else if (tokens[1] == "fragment") { - types.push_back(gl::shader_type::fragment); + types.insert(gl::shader_stage::fragment); } else if (tokens[1] == "geometry") { - types.push_back(gl::shader_type::geometry); + types.insert(gl::shader_stage::geometry); } } } @@ -184,23 +185,28 @@ gl::shader_program* resource_loader::load(resource_manager* inject_debug_macro(source); // Detect declared shader types via the `#pragma vertex`, `#pragma fragment` and `#pragma geometry` directives - std::list shader_types = detect_shader_types(source); - + std::unordered_set shade_stages = detect_shader_stages(source); + // Load detected shaders - std::list shaders; - for (gl::shader_type type: shader_types) + std::list shaders; + for (gl::shader_stage stage: shade_stages) { - text_file type_source = *source; - inject_shader_type_macro(&type_source, type); - std::string source_buffer = generate_source_buffer(type_source); - shaders.push_back(new gl::shader(type, source_buffer)); + text_file stage_source = *source; + inject_shader_stage_macro(&stage_source, stage); + std::string source_buffer = generate_source_buffer(stage_source); + + gl::shader_object* object = new gl::shader_object(stage); + object->source(source_buffer.c_str(), source_buffer.length()); + object->compile(); + + shaders.push_back(object); } // Create shader program gl::shader_program* program = new gl::shader_program(shaders); - // Delete shaders - for (gl::shader* shader: shaders) + // Delete shaders objects + for (gl::shader_object* shader: shaders) { delete shader; }