Korchkidu Korchkidu - 1 month ago 8x
C++ Question

How to Practically Ship GLSL Shaders with your C++ Software

During OpenGL initialization, the program is supposed to do something like:

<Get Shader Source Code>
<Create Shader>
<Attach Source Code To Shader>
<Compile Shader>

Getting source code could be as simple as putting it in a string like:
(Example taken from SuperBible, 6th Edition)

static const char * vs_source[] =
"#version 420 core \n"
" \n"
"void main(void) \n"
"{ \n"
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0); \n"
"} \n"

The problem is that it is hard to edit, debug and maintain GLSL shaders directly in a string. So getting the source code in a string from a file is easier for development:

std::ifstream vertexShaderFile("vertex.glsl");
std::ostringstream vertexBuffer;
vertexBuffer << vertexShaderFile.rdbuf();
std::string vertexBufferStr = vertexBuffer.str();
// Warning: safe only until vertexBufferStr is destroyed or modified
const GLchar *vertexSource = vertexBufferStr.c_str();

The problem now is how to ship the shaders with your program? Indeed, shipping source code with your application may be a problem. OpenGL supports "pre-compiled binary shaders" but the Open Wiki states that:

Program binary formats are not intended to be
transmitted. It is not reasonable to expect different hardware vendors
to accept the same binary formats. It is not reasonable to expect
different hardware from the same vendor to accept the same binary
formats. [...]

How to practically ship GLSL shaders with your C++ software?


There is just "store them directly in the executable" or "store them in (a) separate file(s)", with nothing inbetween. If you want a self-contained executeable, putting them into the binary is a good idea. Note that you can add them as resources or tweak your build system to embed the shader strings from separate development files into source files to make development easier (with the possible addition of being able to directly load the separate files in development builds).

Why do you think shipping the shader sources would be a problem? There is simply no other way in the GL. The precompiled binaries are only useful for caching the compilation results on the target machine. With the fast advances of GPU technology, and changing GPU architectures, and different vendors with totally incompatible ISAs, precompiled shader binaries do not make sense at all.

Note that putting your shader sources in the executeable does not "protect" them, even if you encrypt them. A user can still hook into the GL library and intercept the sources you specify to the GL. And the GL debuggers out there do exactly that.


At SIGGRAPH 2016, the OpenGL Architecture Review Board released the GL_ARB_gl_spirv extension. This will allow a GL inmplementation to use the SPIRV binary intermediate language. This has some potential benefits:

  1. Shaders can be pre-"compiled" offline (the final compilation for the target GPU still happens by the driver later). You don't have to ship the shader source code, but only the binary intermediate representation.
  2. There is one stanadard compiler frontend (glslang) which does the pasring, so differences between the parsers of different implementations can be eliminated.
  3. More shader languages can be added, without the need to change the GL implementations.
  4. It somewhat increases portability to vulkan.

With that scheme, GL is becoming more similar to D3D and Vulkan in that regard. However, it doesn't change the greater picture. The SPIRV bytecode can still be intercepted, disassembled and decompiled. It does make reverse-engineering a little bit harder, but not by much actually. In a shader, you usually can't afford extensive obfuscuation measures, since that dramatically reduces performance - which is contrary to what the shaders are for.

Also keep in mind that this extension is not widely available right now (autumn 2016). And Apple has stopped supporting GL after 4.1, so this extension will probably never come to OSX.