OpenGL Runtime Recompile Of Shaders

Good evening, running into a bit of a snag the last few hours in attempting to setup an OpenGL shader live recompile for the first time. The code so far successfully recognizes a newer FILETIME on any previous compiled shader source files and recompiles. The problem when it re-compiles is that the OpenGL context just seems to shut down. The code is still running, but the swap buffer doesn't display anything different and I lose all key and mouse input to the window.

Debugging, I've checked that the recompile itself works with no errors passed through OpenGL and the same OpenGL program Id persists so that it continues carrying on to the Render Commands.

Any thoughts on what to look for next would be greatly appreciated.

Some related code is below.

Thanks in advance for any thoughts, ideas, help.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
for(uint32 ShaderId = 0;
          ShaderId < GActiveShaderCount;
          ++ShaderId)
      {
          FActiveShader Shader = GActiveShaders[ShaderId];
          FILETIME VertexWriteTime = Win32GetLastWriteTime(Shader.VertexFilename);
          FILETIME FragmentWriteTime = Win32GetLastWriteTime(Shader.FragmentFilename);
                    
          bool32 VertexDirty = CompareFileTime(&VertexWriteTime, &Shader.VertexLastWriteTime);
          bool32 FragmentDirty = CompareFileTime(&FragmentWriteTime, &Shader.FragmentLastWriteTime);
          if(VertexDirty || FragmentDirty)
          {
               RecompileShader(&Shader, ShaderId, VertexWriteTime, FragmentWriteTime);
          }
      }


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
InternalFunc void RecompileShader(FActiveShader* Shader, uint32 ShaderId, FILETIME VertexWrite, FILETIME FragmentWrite)
{
    char* VertexShaderSource = (char*)DEBUGPlatformReadEntireFile(Shader->VertexFilename).Contents;
    char* FragmentShaderSource = (char*)DEBUGPlatformReadEntireFile(Shader->FragmentFilename).Contents;
    
    uint32 VertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(VertexShader, 1, &VertexShaderSource, 0);
    glCompileShader(VertexShader);
    int32 Success;
    uint8 InfoLog[1024];
    glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &Success);
    if(!Success)
    {
        glGetShaderInfoLog(VertexShader, 1024, NULL, InfoLog);
        printf("Compile Error");
    }
    
    uint32 FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(FragmentShader, 1, &FragmentShaderSource, NULL);
    glCompileShader(FragmentShader);
    glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &Success);
    if(!Success)
    {
        glGetShaderInfoLog(FragmentShader, 1024, NULL, InfoLog);
        printf("Compile Error");
    }
    
    glAttachShader(Shader->ShaderId, VertexShader);
    glAttachShader(Shader->ShaderId, FragmentShader);
    glLinkProgram(Shader->ShaderId);
    glDeleteShader(VertexShader);
    glDeleteShader(FragmentShader);
    
    GActiveShaders[ShaderId].VertexLastWriteTime = Win32GetLastWriteTime(Shader->VertexFilename);
    GActiveShaders[ShaderId].FragmentLastWriteTime = Win32GetLastWriteTime(Shader->FragmentFilename);
}

Please check GL_LINK_STATUS (and glGetProgramInfoLog if its GL_FALSE) after linking program. Similar to how you check GL_COMPILE_STATUS after compiling shaders.

My guess would be on one these situations:

1) you are not creating new program - you are reusing previous Shader->ShaderId program, which is already linked, and are attaching new shader objects. I don't remember if this is allowed (need to lookup this in specs), but it may be valid operation.

2) if nr1 is allowed, then it may be because you are attaching new shader objects to existing program, but you are not detaching previous objects. So when glLinkProgram is called the program will contain two entry points for fragment shader and two for vertex shader, which probably is an error. Check GL_LINK_STATUS!

3) something else, like not querying new locations of uniforms, or new attribute locations. Which is not visible in this code fragment.

And please, please use glGetError after EVERY call to OpenGL function. I'm not joking or exaggerating. Stick it everywhere. Or use GL_ARB_debug_output extension. It will make catching mistakes with GL so much easier and faster. Trust me, it will. Here's how you use both of them: https://git.handmade.network/snippets/17

Edited by Mārtiņš Možeiko on
EDIT: Also reviewed the snippet you posted, I'll will definitely be taking your advice on logging all GL calls.

Martins, thank you! I completely missed the GL_LINK_STATUS call and that was indeed the issue. I didn't realize you couldn't re-link to an existing Shader program and I wasn't catching this error as you pointed out.

I've abstracted a bit down one layer now with an enum list of ShaderTypes, ie ShaderTypeTerrain, ShaderTypeStaticModel, etc. This maps to a OpenGL program Id that now gets updated after deleting the OpenGL program and recreating a new ProgramId on recompile. The RenderCommands don't store the Program Id any longer they store the ShaderType. Working as intended, phew.

Thanks again for the help and very quick follow-up.

Regards,
Scott

Edited by Scott Hunt on
How is it that Martins seems to know *everything*
Hah. I don't really know everything. I'm just really good with figuring out technical stuff. Especially if there is a code (or math). Simplified view on how I do it is - you just go line by line and pretend to be CPU. Simulate execution and enumerate as much as possible edge cases that can happen. With time your intuition gets good and you start finding errors really fast and easy.
mmozeiko
Simplified view on how I do it is - you just go line by line and pretend to be CPU.

This is so handmade I can't even...
abnercoimbre
mmozeiko
Simplified view on how I do it is - you just go line by line and pretend to be CPU.

This is so handmade I can't even...


Where's the upvote button?
abnercoimbre
mmozeiko
Simplified view on how I do it is - you just go line by line and pretend to be CPU.

This is so handmade I can't even...


Become One with the CPU. Oooooooommmmm.