diff --git a/framework/opengl_primitives/src/mm/opengl/instance_buffer.hpp b/framework/opengl_primitives/src/mm/opengl/instance_buffer.hpp index 7184c75..5dd2154 100644 --- a/framework/opengl_primitives/src/mm/opengl/instance_buffer.hpp +++ b/framework/opengl_primitives/src/mm/opengl/instance_buffer.hpp @@ -38,6 +38,11 @@ namespace MM::OpenGL { glBindBuffer(target, 0); } + // for transform feedback + void bindBase(GLuint index, GLenum target = GL_TRANSFORM_FEEDBACK_BUFFER) const { + glBindBufferBase(target, index, _handle); + } + void resize(size_t size, GLenum usage) { glBindBuffer(GL_ARRAY_BUFFER, _handle); glBufferData(GL_ARRAY_BUFFER, size * sizeof(TInstance), nullptr, usage); diff --git a/framework/opengl_primitives/src/mm/opengl/shader.cpp b/framework/opengl_primitives/src/mm/opengl/shader.cpp index 0f8459d..8148148 100644 --- a/framework/opengl_primitives/src/mm/opengl/shader.cpp +++ b/framework/opengl_primitives/src/mm/opengl/shader.cpp @@ -1,6 +1,10 @@ #include "./shader.hpp" -#include +#ifdef MM_OPENGL_3_GLES + #include +#else + #include +#endif #include diff --git a/framework/opengl_primitives/src/mm/opengl/shader.hpp b/framework/opengl_primitives/src/mm/opengl/shader.hpp index be131fe..b590681 100644 --- a/framework/opengl_primitives/src/mm/opengl/shader.hpp +++ b/framework/opengl_primitives/src/mm/opengl/shader.hpp @@ -6,12 +6,6 @@ #include #include -#ifdef MM_OPENGL_3_GLES - #include -#else - #include -#endif - #include #include @@ -22,7 +16,12 @@ namespace MM { namespace MM::OpenGL { + // fwd + class ShaderBuilder; + class Shader { + friend ShaderBuilder; + private: uint32_t _rendererID; std::unordered_map _uniformLocationCache; @@ -50,6 +49,7 @@ namespace MM::OpenGL { void setUniformMat4f(const std::string& name, const glm::mat4& matrix); void setUniformMat3f(const std::string& name, const glm::mat3& matrix); + // prefere ShaderBuilder static std::shared_ptr createF(Engine& engine, const char* vertexShaderPath, const char* fragmentShaderPath); static std::shared_ptr create(const std::string& vertexShader, const std::string& fragmentShader); diff --git a/framework/opengl_primitives/src/mm/opengl/shader_builder.cpp b/framework/opengl_primitives/src/mm/opengl/shader_builder.cpp new file mode 100644 index 0000000..948d37e --- /dev/null +++ b/framework/opengl_primitives/src/mm/opengl/shader_builder.cpp @@ -0,0 +1,145 @@ +#include "./shader_builder.hpp" + +#ifdef MM_OPENGL_3_GLES + #include +#else + #include +#endif + +#include + +#include +#define LOG_CRIT(...) __LOG_CRIT( "OpenGL", __VA_ARGS__) +#define LOG_ERROR(...) __LOG_ERROR("OpenGL", __VA_ARGS__) +#define LOG_WARN(...) __LOG_WARN( "OpenGL", __VA_ARGS__) +#define LOG_INFO(...) __LOG_INFO( "OpenGL", __VA_ARGS__) +#define LOG_DEBUG(...) __LOG_DEBUG("OpenGL", __VA_ARGS__) +#define LOG_TRACE(...) __LOG_TRACE("OpenGL", __VA_ARGS__) + +namespace MM::OpenGL { + +ShaderBuilder::ShaderBuilder(void) { +} + +ShaderBuilder::~ShaderBuilder(void) { + // after linking or in case of error, stages can be deleted + for (auto& stage : _stages) { + if (stage.id != 0) { + glDeleteShader(stage.id); + } + } +} + +ShaderBuilder ShaderBuilder::start(void) { + return {}; +} + +std::shared_ptr ShaderBuilder::finish(void) { + // check + for (auto& stage : _stages) { + if (stage.fail) { + // log error + return nullptr; + } + } + + uint32_t program = glCreateProgram(); + + // attach + for (auto& stage : _stages) { + if (stage.id != 0) { + glAttachShader(program, stage.id); + } + } + + // tf varying + if (!_tfs.empty()) { + std::vector tmp_vars; + + for (auto& tfv : _tfs) { + tmp_vars.push_back(tfv.var.c_str()); + } + + glTransformFeedbackVaryings(program, tmp_vars.size(), tmp_vars.data(), _tf_interleaved ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS); + + } + + glLinkProgram(program); + + // diagnostics + GLint isLinked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &isLinked); + if (isLinked == GL_FALSE) { + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + // The maxLength includes the NULL character + std::vector infoLog(maxLength); + glGetProgramInfoLog(program, maxLength, &maxLength, infoLog.data()); + LOG_ERROR("Linking Shader Programs: {}", infoLog.data()); + + // The program is useless now. So delete it. + glDeleteProgram(program); + return nullptr; + } + + glValidateProgram(program); + GLint isValid = 0; + glGetProgramiv(program, GL_VALIDATE_STATUS, &isValid); + if (isValid == GL_FALSE) { + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + // The maxLength includes the NULL character + std::vector infoLog(maxLength); + glGetProgramInfoLog(program, maxLength, &maxLength, infoLog.data()); + LOG_ERROR("Validating Shader Programs: {}", infoLog.data()); + + // The program is useless now. So delete it. + glDeleteProgram(program); + return nullptr; + + } + + return std::shared_ptr(new Shader(program)); +} + +ShaderBuilder& ShaderBuilder::addStageVertex(const std::string& shader_code) { + _stages[VERTEX].id = Shader::compile(GL_VERTEX_SHADER, shader_code); + if (_stages[VERTEX].id == 0) { + _stages[VERTEX].fail = true; + } + + return *this; +} + +ShaderBuilder& ShaderBuilder::addStageVertexF(MM::Engine& engine, const std::string& file_path) { + return addStageVertex(Shader::parse(engine, file_path)); +} + +ShaderBuilder& ShaderBuilder::addStageFragment(const std::string& shader_code) { + _stages[FRAGMENT].id = Shader::compile(GL_FRAGMENT_SHADER, shader_code); + if (_stages[FRAGMENT].id == 0) { + _stages[FRAGMENT].fail = true; + } + + return *this; +} + +ShaderBuilder& ShaderBuilder::addStageFragmentF(MM::Engine& engine, const std::string& file_path) { + return addStageFragment(Shader::parse(engine, file_path)); +} + +ShaderBuilder& ShaderBuilder::addTransformFeedbackVarying(const std::string& var_name) { + if (var_name.empty()) { + // log waring + return *this; + } + + _tfs.push_back({var_name}); + + return *this; +} + +} // MM::OpenGL + diff --git a/framework/opengl_primitives/src/mm/opengl/shader_builder.hpp b/framework/opengl_primitives/src/mm/opengl/shader_builder.hpp new file mode 100644 index 0000000..502945b --- /dev/null +++ b/framework/opengl_primitives/src/mm/opengl/shader_builder.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "./shader.hpp" + +#include + +namespace MM::OpenGL { + + class ShaderBuilder { + private: + ShaderBuilder(void); + + struct stage_t { + uint32_t id = 0; + bool fail = false; + }; + + enum stage_e { + VERTEX = 0, + FRAGMENT, + stage_e_MAX + }; + + stage_t _stages[stage_e_MAX]; + + struct transform_feedback_varying_t { + std::string var; + }; + std::vector _tfs; + + bool _tf_interleaved = false; + + public: + ~ShaderBuilder(void); + ShaderBuilder& operator=(ShaderBuilder&) = delete; + + static ShaderBuilder start(void); + std::shared_ptr finish(void); + + public: + ShaderBuilder& addStageVertex(const std::string& shader_code); + ShaderBuilder& addStageVertexF(MM::Engine& engine, const std::string& file_path); + ShaderBuilder& addStageFragment(const std::string& shader_code); + ShaderBuilder& addStageFragmentF(MM::Engine& engine, const std::string& file_path); + // TODO: geometry and tesselation stages + + ShaderBuilder& addTransformFeedbackVarying(const std::string& var_name); + ShaderBuilder& setTransformFeedbackInterleaved(bool interleaved = true); + }; + +} // MM::OpenGL +