C++/GLSL code generator
While doing my voxel engine experiments, I stumbled quite often about errors in the GLSL/C++ bindings.
Some of the errors e.g. where:
unsigned versus signed for attributes and uniforms
integer versus float for attributes and uniforms
removed, renamed or added uniforms
As debugging this stuff is a quite lame task while rewriting the render and/or the shaders, I decided to write a tool that parses the GLSL shaders and generates C++ shader classes for me. The advantage is that whenever I rename a uniform in the shader, the generated setter name would change and I can't even compile the engine. That way I ensure that the shaders are always in sync with the C++ code that does all the binding.
Also for attributes I get some warnings or errors, if something is out of sync - but for obvious reasons the attribute handling is a lot harder to check at compile time as this is the case for uniforms.
As the tool is able to read array sizes I also get warnings or errors if I change the size in the shader, but not in the c++ code.
Looking back it was a good idea to write this tool - it saved so much time since I started to use it - and also reduced the lines of code that I have to maintain.
The code will be available on github soon.
An example of the generated code is here (notice that the uniform buffer handling is also done automagically):
Using this code is also quite straighforward (but of course uses a lot of code from the Shader class itself):
On disabling the shader I also perform a debug check that all defined uniforms were really set.
Some of the errors e.g. where:
unsigned versus signed for attributes and uniforms
integer versus float for attributes and uniforms
removed, renamed or added uniforms
As debugging this stuff is a quite lame task while rewriting the render and/or the shaders, I decided to write a tool that parses the GLSL shaders and generates C++ shader classes for me. The advantage is that whenever I rename a uniform in the shader, the generated setter name would change and I can't even compile the engine. That way I ensure that the shaders are always in sync with the C++ code that does all the binding.
Also for attributes I get some warnings or errors, if something is out of sync - but for obvious reasons the attribute handling is a lot harder to check at compile time as this is the case for uniforms.
As the tool is able to read array sizes I also get warnings or errors if I change the size in the shader, but not in the c++ code.
Looking back it was a good idea to write this tool - it saved so much time since I started to use it - and also reduced the lines of code that I have to maintain.
The code will be available on github soon.
An example of the generated code is here (notice that the uniform buffer handling is also done automagically):
/** * @file */ #pragma once #include <vector> #include "video/Shader.h" #include "video/Types.h" #include "video/UniformBuffer.h" #include "core/Singleton.h" #include "Materialblock.h" namespace shader { /** * @ingroup Video * @brief Shader wrapper for shaders/world */ class WorldShader : public video::Shader { private: using Super = video::Shader; int _setupCalls = 0; public: static inline WorldShader& getInstance() { return core::Singleton<WorldShader>::getInstance(); } /** * @brief Load the vertex and fragment shaders and verifies that its attributes and uniforms are used. * @note If an attribute or an uniform isn't active, a message will be printed about that fact - but * the setup process won't fail. */ bool setup() override { ++_setupCalls; if (_initialized) { return true; } if (!loadProgram("shaders/world")) { return false; } checkAttributes({"a_pos", "a_info"}); const int a_posLocation = getAttributeLocation("a_pos"); if (a_posLocation != -1) { setAttributeComponents(a_posLocation, 3); } const int a_infoLocation = getAttributeLocation("a_info"); if (a_infoLocation != -1) { setAttributeComponents(a_infoLocation, 3); } checkUniforms({"u_viewprojection", "u_shadowmap", "u_depthsize", "u_distances", "u_cascades", "u_lightdir", "u_diffuse_color", "u_ambient_color", "u_fogcolor", "u_viewdistance", "u_fogrange", "u_model", "u_texture"}); setUniformArraySize("u_viewprojection", 0); setUniformArraySize("u_shadowmap", 0); setUniformArraySize("u_depthsize", 0); setUniformArraySize("u_distances", 0); setUniformArraySize("u_cascades", 4); setUniformArraySize("u_lightdir", 0); setUniformArraySize("u_diffuse_color", 0); setUniformArraySize("u_ambient_color", 0); setUniformArraySize("u_fogcolor", 0); setUniformArraySize("u_viewdistance", 0); setUniformArraySize("u_fogrange", 0); setUniformArraySize("u_model", 0); setUniformArraySize("u_texture", 0); return true; } void shutdown() override { if (_setupCalls == 0) { return; } --_setupCalls; if (_setupCalls == 0) { Super::shutdown(); } } inline bool setViewprojection(const glm::mat4& u_viewprojection) const { const int location = getUniformLocation("u_viewprojection"); if (location == -1) { return false; } setUniformMatrix(location, u_viewprojection); return true; } inline bool setShadowmap(video::TextureUnit u_shadowmap) const { const int location = getUniformLocation("u_shadowmap"); if (location == -1) { return false; } setUniform(location, u_shadowmap); return true; } inline bool setDepthsize(const glm::vec2& u_depthsize) const { const int location = getUniformLocation("u_depthsize"); if (location == -1) { return false; } setUniformVec2(location, u_depthsize); return true; } inline bool setDepthsize(const std::vector<float>& var) const { const int location = getUniformLocation("u_depthsize"); if (location == -1) { return false; } core_assert(int(var.size()) % 2 == 0); setUniformfv(location, &var.front(), 2, 2); return true; } inline bool setDistances(const glm::vec4& u_distances) const { const int location = getUniformLocation("u_distances"); if (location == -1) { return false; } setUniformVec4(location, u_distances); return true; } inline bool setDistances(const std::vector<float>& var) const { const int location = getUniformLocation("u_distances"); if (location == -1) { return false; } core_assert(int(var.size()) % 4 == 0); setUniformfv(location, &var.front(), 4, 4); return true; } inline bool setCascades(const glm::mat4 (&u_cascades)[4]) const { const int location = getUniformLocation("u_cascades"); if (location == -1) { return false; } setUniformMatrixv(location, u_cascades, 4); return true; } inline bool setCascades(const std::vector<glm::mat4>& var) const { const int location = getUniformLocation("u_cascades"); if (location == -1) { return false; } core_assert((int)var.size() == 4); setUniformMatrixv(location, &var.front(), var.size()); return true; } inline bool setLightdir(const glm::vec3& u_lightdir) const { const int location = getUniformLocation("u_lightdir"); if (location == -1) { return false; } setUniformVec3(location, u_lightdir); return true; } inline bool setLightdir(const std::vector<float>& var) const { const int location = getUniformLocation("u_lightdir"); if (location == -1) { return false; } core_assert(int(var.size()) % 3 == 0); setUniformfv(location, &var.front(), 3, 3); return true; } inline bool setDiffuseColor(const glm::vec3& u_diffuse_color) const { const int location = getUniformLocation("u_diffuse_color"); if (location == -1) { return false; } setUniformVec3(location, u_diffuse_color); return true; } inline bool setDiffuseColor(const std::vector<float>& var) const { const int location = getUniformLocation("u_diffuse_color"); if (location == -1) { return false; } core_assert(int(var.size()) % 3 == 0); setUniformfv(location, &var.front(), 3, 3); return true; } inline bool setAmbientColor(const glm::vec3& u_ambient_color) const { const int location = getUniformLocation("u_ambient_color"); if (location == -1) { return false; } setUniformVec3(location, u_ambient_color); return true; } inline bool setAmbientColor(const std::vector<float>& var) const { const int location = getUniformLocation("u_ambient_color"); if (location == -1) { return false; } core_assert(int(var.size()) % 3 == 0); setUniformfv(location, &var.front(), 3, 3); return true; } inline bool setFogcolor(const glm::vec3& u_fogcolor) const { const int location = getUniformLocation("u_fogcolor"); if (location == -1) { return false; } setUniformVec3(location, u_fogcolor); return true; } inline bool setFogcolor(const std::vector<float>& var) const { const int location = getUniformLocation("u_fogcolor"); if (location == -1) { return false; } core_assert(int(var.size()) % 3 == 0); setUniformfv(location, &var.front(), 3, 3); return true; } inline bool setViewdistance(float u_viewdistance) const { const int location = getUniformLocation("u_viewdistance"); if (location == -1) { return false; } setUniformf(location, u_viewdistance); return true; } inline bool setFogrange(float u_fogrange) const { const int location = getUniformLocation("u_fogrange"); if (location == -1) { return false; } setUniformf(location, u_fogrange); return true; } inline bool setModel(const glm::mat4& u_model) const { const int location = getUniformLocation("u_model"); if (location == -1) { return false; } setUniformMatrix(location, u_model); return true; } inline bool setTexture(video::TextureUnit u_texture) const { const int location = getUniformLocation("u_texture"); if (location == -1) { return false; } setUniform(location, u_texture); return true; } inline bool initPosCustom(size_t stride = sizeof(glm::ivec3), const void* pointer = nullptr, video::DataType type = video::DataType::Int, int size = 3, bool isInt = true, bool normalize = false) const { const int loc = enableVertexAttributeArray("a_pos"); if (loc == -1) { return false; } if (isInt) { setVertexAttributeInt(loc, size, type, stride, pointer); } else { setVertexAttribute(loc, size, type, normalize, stride, pointer); } return true; } inline int getLocationPos() const { return getAttributeLocation("a_pos"); } inline int getComponentsPos() const { return getAttributeComponents("a_pos"); } inline bool initPos() const { const int loc = enableVertexAttributeArray("a_pos"); if (loc == -1) { return false; } const size_t stride = sizeof(glm::ivec3); const void* pointer = nullptr; const video::DataType type = video::DataType::Int; const int size = getAttributeComponents(loc); setVertexAttributeInt(loc, size, type, stride, pointer); return true; } inline bool setPosDivisor(uint32_t divisor) const { const int location = getAttributeLocation("a_pos"); return setDivisor(location, divisor); } inline bool initInfoCustom(size_t stride = sizeof(glm::uvec3), const void* pointer = nullptr, video::DataType type = video::DataType::Float, int size = 3, bool isInt = false, bool normalize = false) const { const int loc = enableVertexAttributeArray("a_info"); if (loc == -1) { return false; } if (isInt) { setVertexAttributeInt(loc, size, type, stride, pointer); } else { setVertexAttribute(loc, size, type, normalize, stride, pointer); } return true; } inline int getLocationInfo() const { return getAttributeLocation("a_info"); } inline int getComponentsInfo() const { return getAttributeComponents("a_info"); } inline bool initInfo() const { const int loc = enableVertexAttributeArray("a_info"); if (loc == -1) { return false; } const size_t stride = sizeof(glm::uvec3); const void* pointer = nullptr; const video::DataType type = video::DataType::Float; const int size = getAttributeComponents(loc); setVertexAttribute(loc, size, type, false, stride, pointer); return true; } inline bool setInfoDivisor(uint32_t divisor) const { const int location = getAttributeLocation("a_info"); return setDivisor(location, divisor); } /** * @brief The the uniform buffer for the uniform block u_materialblock */ inline bool setMaterialblock(const video::UniformBuffer& buf) { return setUniformBuffer("u_materialblock", buf); } }; typedef std::shared_ptr<WorldShader> WorldShaderPtr; };
Using this code is also quite straighforward (but of course uses a lot of code from the Shader class itself):
if (!_worldShader.setup()) { Log::error("Failed to initialize the color shader"); return false; } shader::Materialblock::Data materialBlock; memcpy(materialBlock.materialcolor, &voxel::getMaterialColors().front(), sizeof(materialBlock.materialcolor)); _materialBlock.create(materialBlock); video::ScopedShader scoped(_worldShader); _worldShader.setMaterialblock(_materialBlock); _worldShader.setModel(glm::mat4()); _worldShader.setTexture(video::TextureUnit::Zero); _worldShader.setShadowmap(video::TextureUnit::One); _worldShader.setFogrange(250.0f); _worldShader.setDiffuseColor(_diffuseColor); _worldShader.setAmbientColor(_ambientColor); _worldShader.setFogcolor(core::Color::LightBlue);
On disabling the shader I also perform a debug check that all defined uniforms were really set.
Kommentare
Kommentar veröffentlichen