132001f49Smrg/**
232001f49Smrg * Utilities for OpenGL shading language
332001f49Smrg *
432001f49Smrg * Brian Paul
532001f49Smrg * 9 April 2008
632001f49Smrg */
732001f49Smrg
832001f49Smrg
932001f49Smrg#include <assert.h>
1032001f49Smrg#include <stdio.h>
1132001f49Smrg#include <stdlib.h>
1232001f49Smrg#include <string.h>
1332001f49Smrg#include <GL/glew.h>
1432001f49Smrg#include "glut_wrap.h"
1532001f49Smrg#include "shaderutil.h"
1632001f49Smrg
1732001f49Smrg/** time to compile previous shader */
1832001f49Smrgstatic GLdouble CompileTime = 0.0;
1932001f49Smrg
2032001f49Smrg/** time to linke previous program */
2132001f49Smrgstatic GLdouble LinkTime = 0.0;
2232001f49Smrg
2332001f49SmrgPFNGLCREATESHADERPROC CreateShader = NULL;
2432001f49SmrgPFNGLDELETESHADERPROC DeleteShader = NULL;
2532001f49SmrgPFNGLSHADERSOURCEPROC ShaderSource = NULL;
2632001f49SmrgPFNGLGETSHADERIVPROC GetShaderiv = NULL;
2732001f49SmrgPFNGLGETSHADERINFOLOGPROC GetShaderInfoLog = NULL;
2832001f49SmrgPFNGLCREATEPROGRAMPROC CreateProgram = NULL;
2932001f49SmrgPFNGLDELETEPROGRAMPROC DeleteProgram = NULL;
3032001f49SmrgPFNGLATTACHSHADERPROC AttachShader = NULL;
3132001f49SmrgPFNGLLINKPROGRAMPROC LinkProgram = NULL;
3232001f49SmrgPFNGLUSEPROGRAMPROC UseProgram = NULL;
3332001f49SmrgPFNGLGETPROGRAMIVPROC GetProgramiv = NULL;
3432001f49SmrgPFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog = NULL;
3532001f49SmrgPFNGLVALIDATEPROGRAMARBPROC ValidateProgramARB = NULL;
3632001f49SmrgPFNGLUNIFORM1IPROC Uniform1i = NULL;
3732001f49SmrgPFNGLUNIFORM1FVPROC Uniform1fv = NULL;
3832001f49SmrgPFNGLUNIFORM2FVPROC Uniform2fv = NULL;
3932001f49SmrgPFNGLUNIFORM3FVPROC Uniform3fv = NULL;
4032001f49SmrgPFNGLUNIFORM4FVPROC Uniform4fv = NULL;
4132001f49SmrgPFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv = NULL;
4232001f49SmrgPFNGLGETACTIVEATTRIBPROC GetActiveAttrib = NULL;
4332001f49SmrgPFNGLGETATTRIBLOCATIONPROC GetAttribLocation = NULL;
4432001f49Smrg
4532001f49Smrgstatic void GLAPIENTRY
4632001f49Smrgfake_ValidateProgram(GLuint prog)
4732001f49Smrg{
4832001f49Smrg   (void) prog;
4932001f49Smrg}
5032001f49Smrg
5132001f49SmrgGLboolean
5232001f49SmrgShadersSupported(void)
5332001f49Smrg{
5432001f49Smrg   if (GLEW_VERSION_2_0) {
5532001f49Smrg      CreateShader = glCreateShader;
5632001f49Smrg      DeleteShader = glDeleteShader;
5732001f49Smrg      ShaderSource = glShaderSource;
5832001f49Smrg      GetShaderiv = glGetShaderiv;
5932001f49Smrg      GetShaderInfoLog = glGetShaderInfoLog;
6032001f49Smrg      CreateProgram = glCreateProgram;
6132001f49Smrg      DeleteProgram = glDeleteProgram;
6232001f49Smrg      AttachShader = glAttachShader;
6332001f49Smrg      LinkProgram = glLinkProgram;
6432001f49Smrg      UseProgram = glUseProgram;
6532001f49Smrg      GetProgramiv = glGetProgramiv;
6632001f49Smrg      GetProgramInfoLog = glGetProgramInfoLog;
6732001f49Smrg      ValidateProgramARB = (GLEW_ARB_shader_objects)
6832001f49Smrg	 ? glValidateProgramARB : fake_ValidateProgram;
6932001f49Smrg      Uniform1i = glUniform1i;
7032001f49Smrg      Uniform1fv = glUniform1fv;
7132001f49Smrg      Uniform2fv = glUniform2fv;
7232001f49Smrg      Uniform3fv = glUniform3fv;
7332001f49Smrg      Uniform4fv = glUniform4fv;
7432001f49Smrg      UniformMatrix4fv = glUniformMatrix4fv;
7532001f49Smrg      GetActiveAttrib = glGetActiveAttrib;
7632001f49Smrg      GetAttribLocation = glGetAttribLocation;
7732001f49Smrg      return GL_TRUE;
7832001f49Smrg   }
7932001f49Smrg   else if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader
8032001f49Smrg	    && GLEW_ARB_shader_objects) {
8132001f49Smrg      fprintf(stderr, "Warning: Trying ARB GLSL instead of OpenGL 2.x.  This may not work.\n");
8232001f49Smrg      CreateShader = glCreateShaderObjectARB;
8332001f49Smrg      DeleteShader = glDeleteObjectARB;
8432001f49Smrg      ShaderSource = glShaderSourceARB;
8532001f49Smrg      GetShaderiv = glGetObjectParameterivARB;
8632001f49Smrg      GetShaderInfoLog = glGetInfoLogARB;
8732001f49Smrg      CreateProgram = glCreateProgramObjectARB;
8832001f49Smrg      DeleteProgram = glDeleteObjectARB;
8932001f49Smrg      AttachShader = glAttachObjectARB;
9032001f49Smrg      LinkProgram = glLinkProgramARB;
9132001f49Smrg      UseProgram = glUseProgramObjectARB;
9232001f49Smrg      GetProgramiv = glGetObjectParameterivARB;
9332001f49Smrg      GetProgramInfoLog = glGetInfoLogARB;
9432001f49Smrg      ValidateProgramARB = glValidateProgramARB;
9532001f49Smrg      Uniform1i = glUniform1iARB;
9632001f49Smrg      Uniform1fv = glUniform1fvARB;
9732001f49Smrg      Uniform2fv = glUniform2fvARB;
9832001f49Smrg      Uniform3fv = glUniform3fvARB;
9932001f49Smrg      Uniform4fv = glUniform4fvARB;
10032001f49Smrg      UniformMatrix4fv = glUniformMatrix4fvARB;
10132001f49Smrg      GetActiveAttrib = glGetActiveAttribARB;
10232001f49Smrg      GetAttribLocation = glGetAttribLocationARB;
10332001f49Smrg      return GL_TRUE;
10432001f49Smrg   }
10532001f49Smrg   fprintf(stderr, "Sorry, GLSL not supported with this OpenGL.\n");
10632001f49Smrg   return GL_FALSE;
10732001f49Smrg}
10832001f49Smrg
10932001f49Smrg
11032001f49SmrgGLuint
11132001f49SmrgCompileShaderText(GLenum shaderType, const char *text)
11232001f49Smrg{
11332001f49Smrg   GLuint shader;
11432001f49Smrg   GLint stat;
11532001f49Smrg   GLdouble t0, t1;
11632001f49Smrg
11732001f49Smrg   shader = CreateShader(shaderType);
11832001f49Smrg   ShaderSource(shader, 1, (const GLchar **) &text, NULL);
11932001f49Smrg
12032001f49Smrg   t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
12132001f49Smrg   glCompileShader(shader);
12232001f49Smrg   t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
12332001f49Smrg
12432001f49Smrg   CompileTime = t1 - t0;
12532001f49Smrg
12632001f49Smrg   GetShaderiv(shader, GL_COMPILE_STATUS, &stat);
12732001f49Smrg   if (!stat) {
12832001f49Smrg      GLchar log[1000];
12932001f49Smrg      GLsizei len;
13032001f49Smrg      GetShaderInfoLog(shader, 1000, &len, log);
13132001f49Smrg      fprintf(stderr, "Error: problem compiling shader: %s\n", log);
13232001f49Smrg      exit(1);
13332001f49Smrg   }
13432001f49Smrg   else {
13532001f49Smrg      /*printf("Shader compiled OK\n");*/
13632001f49Smrg   }
13732001f49Smrg   return shader;
13832001f49Smrg}
13932001f49Smrg
14032001f49Smrg
14132001f49Smrg/**
14232001f49Smrg * Read a shader from a file.
14332001f49Smrg */
14432001f49SmrgGLuint
14532001f49SmrgCompileShaderFile(GLenum shaderType, const char *filename)
14632001f49Smrg{
14732001f49Smrg   const int max = 100*1000;
14832001f49Smrg   int n;
14932001f49Smrg   char *buffer = (char*) malloc(max);
15032001f49Smrg   GLuint shader;
15132001f49Smrg   FILE *f;
15232001f49Smrg
15332001f49Smrg   f = fopen(filename, "r");
15432001f49Smrg   if (!f) {
15532001f49Smrg      fprintf(stderr, "Unable to open shader file %s\n", filename);
15632001f49Smrg      free(buffer);
15732001f49Smrg      return 0;
15832001f49Smrg   }
15932001f49Smrg
16032001f49Smrg   n = fread(buffer, 1, max, f);
16132001f49Smrg   /*printf("read %d bytes from shader file %s\n", n, filename);*/
16232001f49Smrg   if (n > 0) {
16332001f49Smrg      buffer[n] = 0;
16432001f49Smrg      shader = CompileShaderText(shaderType, buffer);
16532001f49Smrg   }
16632001f49Smrg   else {
16732001f49Smrg      fclose(f);
16832001f49Smrg      free(buffer);
16932001f49Smrg      return 0;
17032001f49Smrg   }
17132001f49Smrg
17232001f49Smrg   fclose(f);
17332001f49Smrg   free(buffer);
17432001f49Smrg
17532001f49Smrg   return shader;
17632001f49Smrg}
17732001f49Smrg
17832001f49Smrg
17932001f49SmrgGLuint
18032001f49SmrgLinkShaders(GLuint vertShader, GLuint fragShader)
18132001f49Smrg{
18232001f49Smrg   return LinkShaders3(vertShader, 0, fragShader);
18332001f49Smrg}
18432001f49Smrg
18532001f49Smrg
18632001f49SmrgGLuint
18732001f49SmrgLinkShaders3(GLuint vertShader, GLuint geomShader, GLuint fragShader)
18832001f49Smrg{
18932001f49Smrg   GLuint program = CreateProgram();
19032001f49Smrg   GLdouble t0, t1;
19132001f49Smrg
19232001f49Smrg   assert(vertShader || fragShader);
19332001f49Smrg
19432001f49Smrg   if (vertShader)
19532001f49Smrg      AttachShader(program, vertShader);
19632001f49Smrg   if (geomShader)
19732001f49Smrg      AttachShader(program, geomShader);
19832001f49Smrg   if (fragShader)
19932001f49Smrg      AttachShader(program, fragShader);
20032001f49Smrg
20132001f49Smrg   t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
20232001f49Smrg   LinkProgram(program);
20332001f49Smrg   t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
20432001f49Smrg
20532001f49Smrg   LinkTime = t1 - t0;
20632001f49Smrg
20732001f49Smrg   /* check link */
20832001f49Smrg   {
20932001f49Smrg      GLint stat;
21032001f49Smrg      GetProgramiv(program, GL_LINK_STATUS, &stat);
21132001f49Smrg      if (!stat) {
21232001f49Smrg         GLchar log[1000];
21332001f49Smrg         GLsizei len;
21432001f49Smrg         GetProgramInfoLog(program, 1000, &len, log);
21532001f49Smrg         fprintf(stderr, "Shader link error:\n%s\n", log);
21632001f49Smrg         return 0;
21732001f49Smrg      }
21832001f49Smrg   }
21932001f49Smrg
22032001f49Smrg   return program;
22132001f49Smrg}
22232001f49Smrg
22332001f49Smrg
22432001f49SmrgGLuint
22532001f49SmrgLinkShaders3WithGeometryInfo(GLuint vertShader, GLuint geomShader, GLuint fragShader,
22632001f49Smrg                             GLint verticesOut, GLenum inputType, GLenum outputType)
22732001f49Smrg{
22832001f49Smrg  GLuint program = CreateProgram();
22932001f49Smrg  GLdouble t0, t1;
23032001f49Smrg
23132001f49Smrg  assert(vertShader || fragShader);
23232001f49Smrg
23332001f49Smrg  if (vertShader)
23432001f49Smrg    AttachShader(program, vertShader);
23532001f49Smrg  if (geomShader) {
23632001f49Smrg    AttachShader(program, geomShader);
23732001f49Smrg    glProgramParameteriARB(program, GL_GEOMETRY_VERTICES_OUT_ARB, verticesOut);
23832001f49Smrg    glProgramParameteriARB(program, GL_GEOMETRY_INPUT_TYPE_ARB, inputType);
23932001f49Smrg    glProgramParameteriARB(program, GL_GEOMETRY_OUTPUT_TYPE_ARB, outputType);
24032001f49Smrg  }
24132001f49Smrg  if (fragShader)
24232001f49Smrg    AttachShader(program, fragShader);
24332001f49Smrg
24432001f49Smrg  t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
24532001f49Smrg  LinkProgram(program);
24632001f49Smrg  t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
24732001f49Smrg
24832001f49Smrg  LinkTime = t1 - t0;
24932001f49Smrg
25032001f49Smrg  /* check link */
25132001f49Smrg  {
25232001f49Smrg    GLint stat;
25332001f49Smrg    GetProgramiv(program, GL_LINK_STATUS, &stat);
25432001f49Smrg    if (!stat) {
25532001f49Smrg      GLchar log[1000];
25632001f49Smrg      GLsizei len;
25732001f49Smrg      GetProgramInfoLog(program, 1000, &len, log);
25832001f49Smrg      fprintf(stderr, "Shader link error:\n%s\n", log);
25932001f49Smrg      return 0;
26032001f49Smrg    }
26132001f49Smrg  }
26232001f49Smrg
26332001f49Smrg  return program;
26432001f49Smrg}
26532001f49Smrg
26632001f49Smrg
26732001f49SmrgGLboolean
26832001f49SmrgValidateShaderProgram(GLuint program)
26932001f49Smrg{
27032001f49Smrg   GLint stat;
27132001f49Smrg   ValidateProgramARB(program);
27232001f49Smrg   GetProgramiv(program, GL_VALIDATE_STATUS, &stat);
27332001f49Smrg
27432001f49Smrg   if (!stat) {
27532001f49Smrg      GLchar log[1000];
27632001f49Smrg      GLsizei len;
27732001f49Smrg      GetProgramInfoLog(program, 1000, &len, log);
27832001f49Smrg      fprintf(stderr, "Program validation error:\n%s\n", log);
2797ec3b29aSmrg      fflush(stderr);
28032001f49Smrg      return 0;
28132001f49Smrg   }
28232001f49Smrg
28332001f49Smrg   return (GLboolean) stat;
28432001f49Smrg}
28532001f49Smrg
28632001f49Smrg
28732001f49SmrgGLdouble
28832001f49SmrgGetShaderCompileTime(void)
28932001f49Smrg{
29032001f49Smrg   return CompileTime;
29132001f49Smrg}
29232001f49Smrg
29332001f49Smrg
29432001f49SmrgGLdouble
29532001f49SmrgGetShaderLinkTime(void)
29632001f49Smrg{
29732001f49Smrg   return LinkTime;
29832001f49Smrg}
29932001f49Smrg
30032001f49Smrg
30132001f49Smrgvoid
30232001f49SmrgSetUniformValues(GLuint program, struct uniform_info uniforms[])
30332001f49Smrg{
30432001f49Smrg   GLuint i;
30532001f49Smrg
30632001f49Smrg   for (i = 0; uniforms[i].name; i++) {
30732001f49Smrg      uniforms[i].location
30832001f49Smrg         = glGetUniformLocation(program, uniforms[i].name);
30932001f49Smrg
31032001f49Smrg      switch (uniforms[i].type) {
31132001f49Smrg      case GL_INT:
31232001f49Smrg      case GL_SAMPLER_1D:
31332001f49Smrg      case GL_SAMPLER_2D:
31432001f49Smrg      case GL_SAMPLER_3D:
31532001f49Smrg      case GL_SAMPLER_CUBE:
31632001f49Smrg      case GL_SAMPLER_2D_RECT_ARB:
31732001f49Smrg      case GL_SAMPLER_1D_SHADOW:
31832001f49Smrg      case GL_SAMPLER_2D_SHADOW:
31932001f49Smrg      case GL_SAMPLER_1D_ARRAY:
32032001f49Smrg      case GL_SAMPLER_2D_ARRAY:
32132001f49Smrg      case GL_SAMPLER_1D_ARRAY_SHADOW:
32232001f49Smrg      case GL_SAMPLER_2D_ARRAY_SHADOW:
32332001f49Smrg         assert(uniforms[i].value[0] >= 0.0F);
32432001f49Smrg         Uniform1i(uniforms[i].location,
32532001f49Smrg                     (GLint) uniforms[i].value[0]);
32632001f49Smrg         break;
32732001f49Smrg      case GL_FLOAT:
32832001f49Smrg         Uniform1fv(uniforms[i].location, 1, uniforms[i].value);
32932001f49Smrg         break;
33032001f49Smrg      case GL_FLOAT_VEC2:
33132001f49Smrg         Uniform2fv(uniforms[i].location, 1, uniforms[i].value);
33232001f49Smrg         break;
33332001f49Smrg      case GL_FLOAT_VEC3:
33432001f49Smrg         Uniform3fv(uniforms[i].location, 1, uniforms[i].value);
33532001f49Smrg         break;
33632001f49Smrg      case GL_FLOAT_VEC4:
33732001f49Smrg         Uniform4fv(uniforms[i].location, 1, uniforms[i].value);
33832001f49Smrg         break;
33932001f49Smrg      case GL_FLOAT_MAT4:
34032001f49Smrg         UniformMatrix4fv(uniforms[i].location, 1, GL_FALSE,
34132001f49Smrg                          uniforms[i].value);
34232001f49Smrg         break;
34332001f49Smrg      default:
34432001f49Smrg         if (strncmp(uniforms[i].name, "gl_", 3) == 0) {
34532001f49Smrg            /* built-in uniform: ignore */
34632001f49Smrg         }
34732001f49Smrg         else {
34832001f49Smrg            fprintf(stderr,
34932001f49Smrg                    "Unexpected uniform data type in SetUniformValues\n");
35032001f49Smrg            abort();
35132001f49Smrg         }
35232001f49Smrg      }
35332001f49Smrg   }
35432001f49Smrg}
35532001f49Smrg
35632001f49Smrg
35732001f49Smrg/** Get list of uniforms used in the program */
35832001f49SmrgGLuint
35932001f49SmrgGetUniforms(GLuint program, struct uniform_info uniforms[])
36032001f49Smrg{
36132001f49Smrg   GLint n, max, i;
36232001f49Smrg
36332001f49Smrg   GetProgramiv(program, GL_ACTIVE_UNIFORMS, &n);
36432001f49Smrg   GetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max);
36532001f49Smrg
36632001f49Smrg   for (i = 0; i < n; i++) {
36732001f49Smrg      GLint size, len;
36832001f49Smrg      GLenum type;
36932001f49Smrg      char name[100];
37032001f49Smrg
37132001f49Smrg      glGetActiveUniform(program, i, 100, &len, &size, &type, name);
37232001f49Smrg
37332001f49Smrg      uniforms[i].name = strdup(name);
37432001f49Smrg      uniforms[i].size = size;
37532001f49Smrg      uniforms[i].type = type;
37632001f49Smrg      uniforms[i].location = glGetUniformLocation(program, name);
37732001f49Smrg   }
37832001f49Smrg
37932001f49Smrg   uniforms[i].name = NULL; /* end of list */
38032001f49Smrg
38132001f49Smrg   return n;
38232001f49Smrg}
38332001f49Smrg
38432001f49Smrg
38532001f49Smrgvoid
38632001f49SmrgPrintUniforms(const struct uniform_info uniforms[])
38732001f49Smrg{
38832001f49Smrg   GLint i;
38932001f49Smrg
39032001f49Smrg   printf("Uniforms:\n");
39132001f49Smrg
39232001f49Smrg   for (i = 0; uniforms[i].name; i++) {
39332001f49Smrg      printf("  %d: %s size=%d type=0x%x loc=%d value=%g, %g, %g, %g\n",
39432001f49Smrg             i,
39532001f49Smrg             uniforms[i].name,
39632001f49Smrg             uniforms[i].size,
39732001f49Smrg             uniforms[i].type,
39832001f49Smrg             uniforms[i].location,
39932001f49Smrg             uniforms[i].value[0],
40032001f49Smrg             uniforms[i].value[1],
40132001f49Smrg             uniforms[i].value[2],
40232001f49Smrg             uniforms[i].value[3]);
40332001f49Smrg   }
40432001f49Smrg}
40532001f49Smrg
40632001f49Smrg
40732001f49Smrg/** Get list of attribs used in the program */
40832001f49SmrgGLuint
40932001f49SmrgGetAttribs(GLuint program, struct attrib_info attribs[])
41032001f49Smrg{
41132001f49Smrg   GLint n, max, i;
41232001f49Smrg
41332001f49Smrg   GetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &n);
41432001f49Smrg   GetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max);
41532001f49Smrg
41632001f49Smrg   for (i = 0; i < n; i++) {
41732001f49Smrg      GLint size, len;
41832001f49Smrg      GLenum type;
41932001f49Smrg      char name[100];
42032001f49Smrg
42132001f49Smrg      GetActiveAttrib(program, i, 100, &len, &size, &type, name);
42232001f49Smrg
42332001f49Smrg      attribs[i].name = strdup(name);
42432001f49Smrg      attribs[i].size = size;
42532001f49Smrg      attribs[i].type = type;
42632001f49Smrg      attribs[i].location = GetAttribLocation(program, name);
42732001f49Smrg   }
42832001f49Smrg
42932001f49Smrg   attribs[i].name = NULL; /* end of list */
43032001f49Smrg
43132001f49Smrg   return n;
43232001f49Smrg}
43332001f49Smrg
43432001f49Smrg
43532001f49Smrgvoid
43632001f49SmrgPrintAttribs(const struct attrib_info attribs[])
43732001f49Smrg{
43832001f49Smrg   GLint i;
43932001f49Smrg
44032001f49Smrg   printf("Attribs:\n");
44132001f49Smrg
44232001f49Smrg   for (i = 0; attribs[i].name; i++) {
44332001f49Smrg      printf("  %d: %s size=%d type=0x%x loc=%d\n",
44432001f49Smrg             i,
44532001f49Smrg             attribs[i].name,
44632001f49Smrg             attribs[i].size,
44732001f49Smrg             attribs[i].type,
44832001f49Smrg             attribs[i].location);
44932001f49Smrg   }
45032001f49Smrg}
451