From d32847cf046e94b975133f8817d8ff813e7e6d38 Mon Sep 17 00:00:00 2001 From: DM8AT Date: Fri, 6 Sep 2024 17:57:46 +0200 Subject: [PATCH] Compute shaders --- Makefile | 8 +- README.md | 1 + src/ObjectGL/OGL_ComputeShader.cpp | 307 +++++++++++++++++++++++++++++ src/ObjectGL/ObjectGL.hpp | 92 +++++++++ src/postFrag.fs | 2 +- 5 files changed, 407 insertions(+), 3 deletions(-) create mode 100644 src/ObjectGL/OGL_ComputeShader.cpp diff --git a/Makefile b/Makefile index 4f73f56..ede5742 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ OBJGL_FLAGS := CREATE_BIN := mkdir -p bin -OBJGL_OBJ := $(OBJ_DIR)/OGL_Instance.o $(OBJ_DIR)/OGL_Window.o $(OBJ_DIR)/OGL_BaseState.o $(OBJ_DIR)/OGL_BindableBase.o $(OBJ_DIR)/OGL_BaseFunctions.o $(OBJ_DIR)/OGL_Shader.o $(OBJ_DIR)/OGL_VertexAttributes.o $(OBJ_DIR)/OGL_UniformBuffer.o $(OBJ_DIR)/OGL_ShaderStorageBuffer.o $(OBJ_DIR)/OGL_IndexBuffer.o $(OBJ_DIR)/OGL_Texture.o $(OBJ_DIR)/OGL_Framebuffer.o -OBJGL_FIL := $(OBGL_DIR)/ObjectGL.hpp $(OBGL_DIR)/OGL_Instance.cpp $(OBGL_DIR)/OGL_Window.cpp $(OBGL_DIR)/OGL_BaseState.cpp $(OBGL_DIR)/OGL_BindableBase.cpp $(OBGL_DIR)/OGL_BaseFunctions.cpp $(OBGL_DIR)/OGL_Shader.cpp $(OBGL_DIR)/OGL_VertexAttributes.cpp $(OBGL_DIR)/OGL_UniformBuffer.cpp $(OBGL_DIR)/OGL_ShaderStorageBuffer.cpp $(OBGL_DIR)/OGL_IndexBuffer.cpp $(OBGL_DIR)/OGL_Texture.cpp $(OBGL_DIR)/OGL_Framebuffer.cpp +OBJGL_OBJ := $(OBJ_DIR)/OGL_Instance.o $(OBJ_DIR)/OGL_Window.o $(OBJ_DIR)/OGL_BaseState.o $(OBJ_DIR)/OGL_BindableBase.o $(OBJ_DIR)/OGL_BaseFunctions.o $(OBJ_DIR)/OGL_Shader.o $(OBJ_DIR)/OGL_VertexAttributes.o $(OBJ_DIR)/OGL_UniformBuffer.o $(OBJ_DIR)/OGL_ShaderStorageBuffer.o $(OBJ_DIR)/OGL_IndexBuffer.o $(OBJ_DIR)/OGL_Texture.o $(OBJ_DIR)/OGL_Framebuffer.o $(OBJ_DIR)/OGL_ComputeShader.o +OBJGL_FIL := $(OBGL_DIR)/ObjectGL.hpp $(OBGL_DIR)/OGL_Instance.cpp $(OBGL_DIR)/OGL_Window.cpp $(OBGL_DIR)/OGL_BaseState.cpp $(OBGL_DIR)/OGL_BindableBase.cpp $(OBGL_DIR)/OGL_BaseFunctions.cpp $(OBGL_DIR)/OGL_Shader.cpp $(OBGL_DIR)/OGL_VertexAttributes.cpp $(OBGL_DIR)/OGL_UniformBuffer.cpp $(OBGL_DIR)/OGL_ShaderStorageBuffer.cpp $(OBGL_DIR)/OGL_IndexBuffer.cpp $(OBGL_DIR)/OGL_Texture.cpp $(OBGL_DIR)/OGL_Framebuffer.cpp $(OBGL_DIR)/OGL_ComputeShader.cpp all: $(BIN)/$(EXECUTABLE) @@ -81,6 +81,10 @@ $(OBJ_DIR)/OGL_Framebuffer.o: $(OBGL_DIR)/OGL_Framebuffer.cpp $(OBGL_DIR)/OGL_Bi $(CREATE_BIN) $(CXX) -c $< -o $@ $(CXX_FLAGS) $(OBJGL_FLAGS) +$(OBJ_DIR)/OGL_ComputeShader.o: $(OBGL_DIR)/OGL_ComputeShader.cpp $(OBGL_DIR)/OGL_BindableBase.cpp $(OBGL_DIR)/ObjectGL.hpp $(OBGL_DIR)/OGL_BaseState.cpp + $(CREATE_BIN) + $(CXX) -c $< -o $@ $(CXX_FLAGS) $(OBJGL_FLAGS) + run: clean all clear ./$(BIN)/$(EXECUTABLE) diff --git a/README.md b/README.md index c0249f6..d24c57c 100644 --- a/README.md +++ b/README.md @@ -35,5 +35,6 @@ If you are missing a feature you'd love to have a wrapper for, create an issue u ## Changelog Here you can look up what changed accross the versions. Here is a list of all code related chages that happend after the last major release: +- added compute shaders - Added support for framebuffers - The window class supports a function that is called on the "Window Resize" event \ No newline at end of file diff --git a/src/ObjectGL/OGL_ComputeShader.cpp b/src/ObjectGL/OGL_ComputeShader.cpp new file mode 100644 index 0000000..df12898 --- /dev/null +++ b/src/ObjectGL/OGL_ComputeShader.cpp @@ -0,0 +1,307 @@ +/** + * @file OGL_ComputeShader.cpp + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2024-09-06 + * + * @copyright Copyright (c) 2024 + * + */ + + +//ask for access to the background library +#define OGL_KEEP_BG_ACCESS +//include the main header +#include "ObjectGL.hpp" + +//stdlib +#include + +//a macro to ensure that the correct window is bound +#define correctInstanceBinding() if (oglGetCurrentInstance() != this->instance) {this->instance->makeCurrent();} + +/** + * @brief attcg a shader to an existing program + * + * @param program the program to link to + * @param source the source code of the shader + * @param type the type of the shader to compile + * + * @return int the compiled shader + */ +int attachShader(GLint program, std::string source, GLenum type) +{ + //store the success tatus + GLint status = 0; + //create the vertex shader + GLuint s = glCreateShader(type); + //the program created correctly, so just guess that the shader worked too. + + //store the source code + const char* src = source.c_str(); + //store the length of the source + int len = (int)source.length(); + //attach the shader source code + glShaderSource(s, 1, &src, &len); + //compile the shader + glCompileShader(s); + + //get the compile status + glGetShaderiv(s, GL_COMPILE_STATUS, &status); + //check the status + if (status == GL_FALSE) + { + //store the size of the error log + GLint logSize = 0; + //store the error message + glGetShaderiv(s, GL_INFO_LOG_LENGTH, &logSize); + //create the error log + GLchar* buff = new GLchar[logSize]; + //get the error + glGetShaderInfoLog(s, logSize, &logSize, buff); + //print the error + std::__throw_runtime_error((std::string("Encounterd a compile error while compiling a shader. Compile Error: \n") + buff).c_str()); + //free the buffer + delete[] buff; + //delete the shader + glDeleteShader(s); + //stop the function + return 0; + } + + //attach the shader + glAttachShader(program, s); + //return the shader + return s; +} + +/** + * @brief compile a shader to an OpenGL shader on the GPU + * + * @param cs the compute shader + * @return GLuint the compiled shader on the GPU + */ +GLuint compileShader(std::string cs) +{ + //create the program + GLuint prog = glCreateProgram(); + //check if the program could be created + if (prog == 0) + { + //if not, throw an error + std::__throw_runtime_error("Failed to create shader program"); + } + + //link the vertex shader and store it + GLuint vsg = attachShader(prog, cs, GL_COMPUTE_SHADER); + + //link the whole program + glLinkProgram(prog); + + //store if the program is linked + GLint status = 0; + //get if the program is linked + glGetProgramiv(prog, GL_LINK_STATUS, &status); + //check if the shader is linked + if (status == GL_FALSE) + { + //store the size of the error log + GLint logSize = 0; + //store the error message + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logSize); + //create the error log + GLchar* buff = new GLchar[logSize]; + //get the error + glGetProgramInfoLog(prog, logSize, &logSize, buff); + //print the error + std::__throw_runtime_error((std::string("Encounterd a linknig error while linking a shader. Linker error: \n") + buff).c_str()); + //free the buffer + delete[] buff; + //delete the shader + glDeleteProgram(prog); + //stop the function + return 0; + } + + //clean the vertex shader + glDeleteShader(vsg); + + //on success, return the shader + return prog; +} + +/** + * @brief read a file into a string + * + * @param file the file to read + * @return std::string the read file + */ +std::string readFile(std::string file) +{ + //open the file + std::ifstream f(file.c_str()); + //check if the file is opend + if (!f.is_open()) + { + //else, print an error + std::__throw_runtime_error((std::string("Failed to open file ") + file).c_str()); + return ""; + } + //store the file output + std::string out; + //store a line + std::string line; + //read the file + while (getline(f, line)) + { + //store the line + out += (line + "\n"); + } + //close the file + f.close(); + //return the string + return out; +} + +OGL_ComputeShader::OGL_ComputeShader(const char* input, OGL_ShaderInput type) +{ + //just re-compile the shader + this->recompileShader(input, type); +} + +void OGL_ComputeShader::recompileShader(std::string cs, OGL_ShaderInput type) +{ + //make sure the correct instance is bound + correctInstanceBinding() + //switch over the types + switch (type) + { + //check if the data is in a file + case OGL_SHADER_INPUT_FILE: + { + //read the vertex shader + std::string css = readFile(cs); + //compile the shader + this->shader = compileShader(css); + break; + } + + //check if this is the raw data + case OGL_SHADER_INPUT_SOURCE_GLSL: + { + //just compile the data + this->shader = compileShader(cs); + break; + } + + default: + break; + } +} + +void OGL_ComputeShader::bind() +{ + //make sure to bind the correct instace + correctInstanceBinding() + + //bind the shader + glUseProgram(this->shader); + //loop over all uniforms + for (OGL_UniformInfo& ui : this->uniforms) + { + //switch over the type + switch (ui.type) + { + case OGL_TYPE_FLOAT: + //set the float + glUniform1fv(ui.location, 1, (float*)((void*)ui.data)); + break; + + case OGL_TYPE_INT: + //set the int + glUniform1iv(ui.location, 1, (int*)((void*)ui.data)); + break; + + case OGL_TYPE_UINT: + //set the unsigned int + glUniform1uiv(ui.location, 1, (unsigned int*)((void*)ui.data)); + break; + + default: + //throw an error + std::__throw_runtime_error("The requested type is not a valid type for an uniform"); + break; + } + } +} + +void OGL_ComputeShader::unbind() +{ + //make sure to bind the correct instace + correctInstanceBinding() + //unbind the shader + glUseProgram(0); +} + +OGL_UniformInfo& OGL_ComputeShader::operator[](std::string name) +{ + //store the element index + size_t idx = -1; + //iterate over all uniforms + for (size_t i = 0; i < this->uniforms.size(); ++i) + { + //check if this is the correct element + if (this->uniforms[i].name == name) + { + //if it is, store the index + idx = i; + //stop the loop + break; + } + } + //check if the element was found + if (idx != (size_t)-1) + { + //return a reference to the element + return this->uniforms[idx]; + } + //store the index of the new element + idx = this->uniforms.size(); + //create a new element + this->uniforms.push_back(OGL_UniformInfo()); + //set the name to the requested one + this->uniforms[idx].name = name; + //return a reference to the requested element + return this->uniforms[idx]; +} + +OGL_UniformInfo& OGL_ComputeShader::operator[](size_t index) +{ + //check if the element is in range + if (index < this->uniforms.size()) + { + //if it is, return a reference to it + return this->uniforms[index]; + } + //else, throw an error + std::__throw_runtime_error("The index is out of bounds for an uniform in the requested shader"); +} + +void OGL_ComputeShader::recalculateUniforms() +{ + //loop over all uniforms + for (OGL_UniformInfo& ui : this->uniforms) + { + //get the location + ui.location = glGetUniformLocation(this->shader, ui.name.c_str()); + } +} + +void OGL_ComputeShader::onDestroy() +{ + //make sure to bind the correct instance + correctInstanceBinding() + //delete the program + glDeleteProgram(this->shader); +} \ No newline at end of file diff --git a/src/ObjectGL/ObjectGL.hpp b/src/ObjectGL/ObjectGL.hpp index eec051b..73cf6ab 100644 --- a/src/ObjectGL/ObjectGL.hpp +++ b/src/ObjectGL/ObjectGL.hpp @@ -1621,6 +1621,98 @@ class OGL_Framebuffer : OGL_BindableBase std::vector attachments; }; +class OGL_ComputeShader : OGL_BindableBase +{ +public: + + OGL_ComputeShader() = default; + + OGL_ComputeShader(const char* input, OGL_ShaderInput type = OGL_SHADER_INPUT_FILE); + + + /** + * @brief change this shader to a new shader + * + * @param cs the compute shader information, in the format specified by type + * @param type the type the data is given in, can be inputed as file, source or binary + */ + void recompileShader(std::string cs, OGL_ShaderInput type = OGL_SHADER_INPUT_FILE); + + /** + * @brief Get the Compiled Shader on the GPU + * + * @return uint32_t the compiled OpenGL shader on the GPU + */ + inline uint32_t getCompiledShader() {return this->shader;} + + /** + * @brief bind the shader + */ + void bind(); + + /** + * @brief unbind the shader + */ + void unbind(); + + /** + * @brief Set the uniforms for the buffer + * + * @param uniforms the uniforms for the shader + */ + inline void setUniforms(std::vector uniforms) {this->uniforms = uniforms;} + + /** + * @brief Get a pointer to all stored uniforms + * + * @return std::vector* a pointer to the internal vector of uniforms + */ + inline std::vector* getUniformPtr() {return &this->uniforms;} + + /** + * @brief Get the amount of stored uniforms + * + * @return size_t the amount of uniforms + */ + inline size_t getUniformCount() {return this->uniforms.size();} + + /** + * @brief get a specific uniform from the shader. If it dosn't exist, create it + * + * @param name the name of the uniform + * @return OGL_UniformInfo& a reference to the uniform + */ + OGL_UniformInfo& operator[](std::string name); + + /** + * @brief get a specific uniform from the shader. If it dosn't exist, throw an error + * + * @param index the index of the + * @return OGL_UniformInfo& + */ + OGL_UniformInfo& operator[](size_t index); + + /** + * @brief update the positions of all uniforms + */ + void recalculateUniforms(); + +private: + /** + * @brief clean up the object after destruction + */ + virtual void onDestroy() override; + + /** + * @brief store the OpenGL shader + */ + GLuint shader = 0; + /** + * @brief store information about all the uniforms + */ + std::vector uniforms; +}; + //undefine the helper macros #undef correctInstanceBinding diff --git a/src/postFrag.fs b/src/postFrag.fs index 1c6e305..efdb538 100644 --- a/src/postFrag.fs +++ b/src/postFrag.fs @@ -8,5 +8,5 @@ in vec2 f_tex; void main() { - color = texture(mainBuff, f_tex); + color = vec4(vec3(1) - texture(mainBuff, f_tex).rgb,1); } \ No newline at end of file