+unsigned int GL_Backend_CompileProgram(int vertexstrings_count, const char **vertexstrings_list, int fragmentstrings_count, const char **fragmentstrings_list)
+{
+ GLint vertexshadercompiled, fragmentshadercompiled, programlinked;
+ GLuint vertexshaderobject, fragmentshaderobject, programobject = 0;
+ char compilelog[4096];
+ CHECKGLERROR
+
+ programobject = qglCreateProgramObjectARB();
+ CHECKGLERROR
+ if (!programobject)
+ return 0;
+
+ if (vertexstrings_count)
+ {
+ CHECKGLERROR
+ vertexshaderobject = qglCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
+ if (!vertexshaderobject)
+ {
+ qglDeleteObjectARB(programobject);
+ CHECKGLERROR
+ return 0;
+ }
+ qglShaderSourceARB(vertexshaderobject, vertexstrings_count, vertexstrings_list, NULL);
+ qglCompileShaderARB(vertexshaderobject);
+ CHECKGLERROR
+ qglGetObjectParameterivARB(vertexshaderobject, GL_OBJECT_COMPILE_STATUS_ARB, &vertexshadercompiled);
+ qglGetInfoLogARB(vertexshaderobject, sizeof(compilelog), NULL, compilelog);
+ if (compilelog[0])
+ Con_Printf("vertex shader compile log:\n%s\n", compilelog);
+ if (!vertexshadercompiled)
+ {
+ qglDeleteObjectARB(programobject);
+ qglDeleteObjectARB(vertexshaderobject);
+ CHECKGLERROR
+ return 0;
+ }
+ qglAttachObjectARB(programobject, vertexshaderobject);
+ qglDeleteObjectARB(vertexshaderobject);
+ CHECKGLERROR
+ }
+
+ if (fragmentstrings_count)
+ {
+ CHECKGLERROR
+ fragmentshaderobject = qglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
+ if (!fragmentshaderobject)
+ {
+ qglDeleteObjectARB(programobject);
+ CHECKGLERROR
+ return 0;
+ }
+ qglShaderSourceARB(fragmentshaderobject, fragmentstrings_count, fragmentstrings_list, NULL);
+ qglCompileShaderARB(fragmentshaderobject);
+ CHECKGLERROR
+ qglGetObjectParameterivARB(fragmentshaderobject, GL_OBJECT_COMPILE_STATUS_ARB, &fragmentshadercompiled);
+ qglGetInfoLogARB(fragmentshaderobject, sizeof(compilelog), NULL, compilelog);
+ if (compilelog[0])
+ Con_Printf("fragment shader compile log:\n%s\n", compilelog);
+ if (!fragmentshadercompiled)
+ {
+ qglDeleteObjectARB(programobject);
+ qglDeleteObjectARB(fragmentshaderobject);
+ CHECKGLERROR
+ return 0;
+ }
+ qglAttachObjectARB(programobject, fragmentshaderobject);
+ qglDeleteObjectARB(fragmentshaderobject);
+ CHECKGLERROR
+ }
+
+ qglLinkProgramARB(programobject);
+ CHECKGLERROR
+ qglGetObjectParameterivARB(programobject, GL_OBJECT_LINK_STATUS_ARB, &programlinked);
+ qglGetInfoLogARB(programobject, sizeof(compilelog), NULL, compilelog);
+ if (compilelog[0])
+ {
+ Con_Printf("program link log:\n%s\n", compilelog);
+ // software vertex shader is ok but software fragment shader is WAY
+ // too slow, fail program if so.
+ // NOTE: this string might be ATI specific, but that's ok because the
+ // ATI R300 chip (Radeon 9500-9800/X300) is the most likely to use a
+ // software fragment shader due to low instruction and dependent
+ // texture limits.
+ if (strstr(compilelog, "fragment shader will run in software"))
+ programlinked = false;
+ }
+ CHECKGLERROR
+ if (!programlinked)
+ {
+ qglDeleteObjectARB(programobject);
+ return 0;
+ }
+ CHECKGLERROR
+ return programobject;
+}
+
+void GL_Backend_FreeProgram(unsigned int prog)
+{
+ CHECKGLERROR
+ qglDeleteObjectARB(prog);
+ CHECKGLERROR
+}
+