if a separable program object (with single shader) that was successfully linked and the shader detached is asked about it’s GL_PROGRAM_BINARY_LENGTH, it returns 0.
If the shader was not detached, GL_PROGRAM_BINARY_LENGTH looks valid (not 0).
here is a minimal win32-based code that reproduces the bug:
#include <string.h>
#include <windows.h>
#include <GL/gl.h>
#include "glext.h"
void get_program_binary_test()
{
static const char fs_text[] =
"#version 330
"
"#extension GL_ARB_separate_shader_objects : enable
"
"layout(location = 0) out vec4 c0;
"
"void main() {
"
" c0 = vec4(0);
"
"}
";
const char *src = fs_text;
GLint len = sizeof(fs_text);
GLuint shader, prog;
GLenum status;
GLint bsize;
PFNGLCREATESHADERPROC _glCreateShader;
PFNGLSHADERSOURCEPROC _glShaderSource;
PFNGLCOMPILESHADERPROC _glCompileShader;
PFNGLGETSHADERIVPROC _glGetShaderiv;
PFNGLCREATEPROGRAMPROC _glCreateProgram;
PFNGLPROGRAMPARAMETERIPROC _glProgramParameteri;
PFNGLATTACHSHADERPROC _glAttachShader;
PFNGLLINKPROGRAMPROC _glLinkProgram;
PFNGLDETACHSHADERPROC _glDetachShader;
PFNGLGETPROGRAMIVPROC _glGetProgramiv;
*(void **)&_glCreateShader = (void *)wglGetProcAddress("glCreateShader");
*(void **)&_glShaderSource = (void *)wglGetProcAddress("glShaderSource");
*(void **)&_glCompileShader = (void *)wglGetProcAddress("glCompileShader");
*(void **)&_glGetShaderiv = (void *)wglGetProcAddress("glGetShaderiv");
*(void **)&_glCreateProgram = (void *)wglGetProcAddress("glCreateProgram");
*(void **)&_glProgramParameteri = (void *)wglGetProcAddress("glProgramParameteri");
*(void **)&_glAttachShader = (void *)wglGetProcAddress("glAttachShader");
*(void **)&_glLinkProgram = (void *)wglGetProcAddress("glLinkProgram");
*(void **)&_glDetachShader = (void *)wglGetProcAddress("glDetachShader");
*(void **)&_glGetProgramiv = (void *)wglGetProcAddress("glGetProgramiv");
shader = _glCreateShader(GL_FRAGMENT_SHADER);
_glShaderSource(shader, 1, &src, &len);
_glCompileShader(shader);
_glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) _asm int 3
prog = _glCreateProgram();
_glProgramParameteri(prog, GL_PROGRAM_SEPARABLE, GL_TRUE);
_glProgramParameteri(prog, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
_glAttachShader(prog, shader);
_glLinkProgram(prog);
_glDetachShader(prog, shader); // if this is commented out the program binary length is not 0
_glGetProgramiv(prog, GL_LINK_STATUS, &status);
if (status == GL_FALSE) _asm int 3
_glGetProgramiv(prog, GL_PROGRAM_BINARY_LENGTH, &bsize);
if (bsize == 0) _asm int 3 // bug!!!!
}
LRESULT CALLBACK wnd_proc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_NCCREATE: return 1;
case WM_PAINT: ValidateRect(wnd, NULL);
default: return 0;
}
}
int CALLBACK WinMain(HINSTANCE inst, HINSTANCE prev_inst, char *cl, int cs)
{
WNDCLASSA wc;
HWND wnd;
HDC dc;
PIXELFORMATDESCRIPTOR pfd;
int pf;
HGLRC rc;
memset(&wc, 0, sizeof(wc));
wc.hInstance = inst;
wc.lpfnWndProc = wnd_proc;
wc.lpszClassName = "_test_class_name";
RegisterClassA(&wc);
wnd = CreateWindowExA(0, wc.lpszClassName, NULL, WS_POPUP, 0, 0, 64, 32, NULL, NULL, NULL, NULL);
dc = GetDC(wnd);
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DEPTH_DONTCARE;
pf = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, pf, NULL);
rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
get_program_binary_test();
return 0;
}