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):

#pragma once

#include <vector>
#include "video/Shader.h"
#include "video/Types.h"
#include "video/UniformBuffer.h"
#include "core/Singleton.h"

namespace shader {

class Materialblock {
public:
 ~Materialblock() {
  shutdown();
 }

 /**
  * @brief Uniform buffer for Materialblock::Data
  */
 video::UniformBuffer _materialblock;
 /**
  * @brief layout(std140) aligned uniform block structure
  */
 #pragma pack(push, 1)
 struct Data {
  glm::vec4 materialcolor[256]; // 4096 bytes
 };
 #pragma pack(pop)

 inline bool update(const Data& var) {
  return _materialblock.update((const void*)&var, sizeof(var));
 }


 inline bool create(const Data& var) {
  return _materialblock.create((const void*)&var, sizeof(var));
 }


 inline operator const video::UniformBuffer&() const {
  return _materialblock;
 }

 void shutdown() {
  _materialblock.shutdown();

 }

};

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", "a_offset"});
  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);
  }
  const int a_offsetLocation = getAttributeLocation("a_offset");
  if (a_offsetLocation != -1) {
   setAttributeComponents(a_offsetLocation, 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 {
  core_assert(_setupCalls > 0);
  --_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 int getLocationPos() const {
  return getAttributeLocation("a_pos");
 }

 inline int getComponentsPos() const {
  return getAttributeComponents("a_pos");
 }

 inline bool setPosDivisor(uint32_t divisor) const {
  const int location = getAttributeLocation("a_pos");
  return setDivisor(location, divisor);
 }

 inline int getLocationInfo() const {
  return getAttributeLocation("a_info");
 }

 inline int getComponentsInfo() const {
  return getAttributeComponents("a_info");
 }

 inline bool setInfoDivisor(uint32_t divisor) const {
  const int location = getAttributeLocation("a_info");
  return setDivisor(location, divisor);
 }

 inline int getLocationOffset() const {
  return getAttributeLocation("a_offset");
 }

 inline int getComponentsOffset() const {
  return getAttributeComponents("a_offset");
 }


 inline bool setOffsetDivisor(uint32_t divisor) const {
  const int location = getAttributeLocation("a_offset");
  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

Beliebte Posts