shaderutil.c revision 7ec3b29a
1/**
2 * Utilities for OpenGL shading language
3 *
4 * Brian Paul
5 * 9 April 2008
6 */
7
8
9#include <assert.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <GL/glew.h>
14#include "glut_wrap.h"
15#include "shaderutil.h"
16
17/** time to compile previous shader */
18static GLdouble CompileTime = 0.0;
19
20/** time to linke previous program */
21static GLdouble LinkTime = 0.0;
22
23PFNGLCREATESHADERPROC CreateShader = NULL;
24PFNGLDELETESHADERPROC DeleteShader = NULL;
25PFNGLSHADERSOURCEPROC ShaderSource = NULL;
26PFNGLGETSHADERIVPROC GetShaderiv = NULL;
27PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog = NULL;
28PFNGLCREATEPROGRAMPROC CreateProgram = NULL;
29PFNGLDELETEPROGRAMPROC DeleteProgram = NULL;
30PFNGLATTACHSHADERPROC AttachShader = NULL;
31PFNGLLINKPROGRAMPROC LinkProgram = NULL;
32PFNGLUSEPROGRAMPROC UseProgram = NULL;
33PFNGLGETPROGRAMIVPROC GetProgramiv = NULL;
34PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog = NULL;
35PFNGLVALIDATEPROGRAMARBPROC ValidateProgramARB = NULL;
36PFNGLUNIFORM1IPROC Uniform1i = NULL;
37PFNGLUNIFORM1FVPROC Uniform1fv = NULL;
38PFNGLUNIFORM2FVPROC Uniform2fv = NULL;
39PFNGLUNIFORM3FVPROC Uniform3fv = NULL;
40PFNGLUNIFORM4FVPROC Uniform4fv = NULL;
41PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv = NULL;
42PFNGLGETACTIVEATTRIBPROC GetActiveAttrib = NULL;
43PFNGLGETATTRIBLOCATIONPROC GetAttribLocation = NULL;
44
45static void GLAPIENTRY
46fake_ValidateProgram(GLuint prog)
47{
48   (void) prog;
49}
50
51GLboolean
52ShadersSupported(void)
53{
54   if (GLEW_VERSION_2_0) {
55      CreateShader = glCreateShader;
56      DeleteShader = glDeleteShader;
57      ShaderSource = glShaderSource;
58      GetShaderiv = glGetShaderiv;
59      GetShaderInfoLog = glGetShaderInfoLog;
60      CreateProgram = glCreateProgram;
61      DeleteProgram = glDeleteProgram;
62      AttachShader = glAttachShader;
63      LinkProgram = glLinkProgram;
64      UseProgram = glUseProgram;
65      GetProgramiv = glGetProgramiv;
66      GetProgramInfoLog = glGetProgramInfoLog;
67      ValidateProgramARB = (GLEW_ARB_shader_objects)
68	 ? glValidateProgramARB : fake_ValidateProgram;
69      Uniform1i = glUniform1i;
70      Uniform1fv = glUniform1fv;
71      Uniform2fv = glUniform2fv;
72      Uniform3fv = glUniform3fv;
73      Uniform4fv = glUniform4fv;
74      UniformMatrix4fv = glUniformMatrix4fv;
75      GetActiveAttrib = glGetActiveAttrib;
76      GetAttribLocation = glGetAttribLocation;
77      return GL_TRUE;
78   }
79   else if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader
80	    && GLEW_ARB_shader_objects) {
81      fprintf(stderr, "Warning: Trying ARB GLSL instead of OpenGL 2.x.  This may not work.\n");
82      CreateShader = glCreateShaderObjectARB;
83      DeleteShader = glDeleteObjectARB;
84      ShaderSource = glShaderSourceARB;
85      GetShaderiv = glGetObjectParameterivARB;
86      GetShaderInfoLog = glGetInfoLogARB;
87      CreateProgram = glCreateProgramObjectARB;
88      DeleteProgram = glDeleteObjectARB;
89      AttachShader = glAttachObjectARB;
90      LinkProgram = glLinkProgramARB;
91      UseProgram = glUseProgramObjectARB;
92      GetProgramiv = glGetObjectParameterivARB;
93      GetProgramInfoLog = glGetInfoLogARB;
94      ValidateProgramARB = glValidateProgramARB;
95      Uniform1i = glUniform1iARB;
96      Uniform1fv = glUniform1fvARB;
97      Uniform2fv = glUniform2fvARB;
98      Uniform3fv = glUniform3fvARB;
99      Uniform4fv = glUniform4fvARB;
100      UniformMatrix4fv = glUniformMatrix4fvARB;
101      GetActiveAttrib = glGetActiveAttribARB;
102      GetAttribLocation = glGetAttribLocationARB;
103      return GL_TRUE;
104   }
105   fprintf(stderr, "Sorry, GLSL not supported with this OpenGL.\n");
106   return GL_FALSE;
107}
108
109
110GLuint
111CompileShaderText(GLenum shaderType, const char *text)
112{
113   GLuint shader;
114   GLint stat;
115   GLdouble t0, t1;
116
117   shader = CreateShader(shaderType);
118   ShaderSource(shader, 1, (const GLchar **) &text, NULL);
119
120   t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
121   glCompileShader(shader);
122   t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
123
124   CompileTime = t1 - t0;
125
126   GetShaderiv(shader, GL_COMPILE_STATUS, &stat);
127   if (!stat) {
128      GLchar log[1000];
129      GLsizei len;
130      GetShaderInfoLog(shader, 1000, &len, log);
131      fprintf(stderr, "Error: problem compiling shader: %s\n", log);
132      exit(1);
133   }
134   else {
135      /*printf("Shader compiled OK\n");*/
136   }
137   return shader;
138}
139
140
141/**
142 * Read a shader from a file.
143 */
144GLuint
145CompileShaderFile(GLenum shaderType, const char *filename)
146{
147   const int max = 100*1000;
148   int n;
149   char *buffer = (char*) malloc(max);
150   GLuint shader;
151   FILE *f;
152
153   f = fopen(filename, "r");
154   if (!f) {
155      fprintf(stderr, "Unable to open shader file %s\n", filename);
156      free(buffer);
157      return 0;
158   }
159
160   n = fread(buffer, 1, max, f);
161   /*printf("read %d bytes from shader file %s\n", n, filename);*/
162   if (n > 0) {
163      buffer[n] = 0;
164      shader = CompileShaderText(shaderType, buffer);
165   }
166   else {
167      fclose(f);
168      free(buffer);
169      return 0;
170   }
171
172   fclose(f);
173   free(buffer);
174
175   return shader;
176}
177
178
179GLuint
180LinkShaders(GLuint vertShader, GLuint fragShader)
181{
182   return LinkShaders3(vertShader, 0, fragShader);
183}
184
185
186GLuint
187LinkShaders3(GLuint vertShader, GLuint geomShader, GLuint fragShader)
188{
189   GLuint program = CreateProgram();
190   GLdouble t0, t1;
191
192   assert(vertShader || fragShader);
193
194   if (vertShader)
195      AttachShader(program, vertShader);
196   if (geomShader)
197      AttachShader(program, geomShader);
198   if (fragShader)
199      AttachShader(program, fragShader);
200
201   t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
202   LinkProgram(program);
203   t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
204
205   LinkTime = t1 - t0;
206
207   /* check link */
208   {
209      GLint stat;
210      GetProgramiv(program, GL_LINK_STATUS, &stat);
211      if (!stat) {
212         GLchar log[1000];
213         GLsizei len;
214         GetProgramInfoLog(program, 1000, &len, log);
215         fprintf(stderr, "Shader link error:\n%s\n", log);
216         return 0;
217      }
218   }
219
220   return program;
221}
222
223
224GLuint
225LinkShaders3WithGeometryInfo(GLuint vertShader, GLuint geomShader, GLuint fragShader,
226                             GLint verticesOut, GLenum inputType, GLenum outputType)
227{
228  GLuint program = CreateProgram();
229  GLdouble t0, t1;
230
231  assert(vertShader || fragShader);
232
233  if (vertShader)
234    AttachShader(program, vertShader);
235  if (geomShader) {
236    AttachShader(program, geomShader);
237    glProgramParameteriARB(program, GL_GEOMETRY_VERTICES_OUT_ARB, verticesOut);
238    glProgramParameteriARB(program, GL_GEOMETRY_INPUT_TYPE_ARB, inputType);
239    glProgramParameteriARB(program, GL_GEOMETRY_OUTPUT_TYPE_ARB, outputType);
240  }
241  if (fragShader)
242    AttachShader(program, fragShader);
243
244  t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
245  LinkProgram(program);
246  t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
247
248  LinkTime = t1 - t0;
249
250  /* check link */
251  {
252    GLint stat;
253    GetProgramiv(program, GL_LINK_STATUS, &stat);
254    if (!stat) {
255      GLchar log[1000];
256      GLsizei len;
257      GetProgramInfoLog(program, 1000, &len, log);
258      fprintf(stderr, "Shader link error:\n%s\n", log);
259      return 0;
260    }
261  }
262
263  return program;
264}
265
266
267GLboolean
268ValidateShaderProgram(GLuint program)
269{
270   GLint stat;
271   ValidateProgramARB(program);
272   GetProgramiv(program, GL_VALIDATE_STATUS, &stat);
273
274   if (!stat) {
275      GLchar log[1000];
276      GLsizei len;
277      GetProgramInfoLog(program, 1000, &len, log);
278      fprintf(stderr, "Program validation error:\n%s\n", log);
279      fflush(stderr);
280      return 0;
281   }
282
283   return (GLboolean) stat;
284}
285
286
287GLdouble
288GetShaderCompileTime(void)
289{
290   return CompileTime;
291}
292
293
294GLdouble
295GetShaderLinkTime(void)
296{
297   return LinkTime;
298}
299
300
301void
302SetUniformValues(GLuint program, struct uniform_info uniforms[])
303{
304   GLuint i;
305
306   for (i = 0; uniforms[i].name; i++) {
307      uniforms[i].location
308         = glGetUniformLocation(program, uniforms[i].name);
309
310      switch (uniforms[i].type) {
311      case GL_INT:
312      case GL_SAMPLER_1D:
313      case GL_SAMPLER_2D:
314      case GL_SAMPLER_3D:
315      case GL_SAMPLER_CUBE:
316      case GL_SAMPLER_2D_RECT_ARB:
317      case GL_SAMPLER_1D_SHADOW:
318      case GL_SAMPLER_2D_SHADOW:
319      case GL_SAMPLER_1D_ARRAY:
320      case GL_SAMPLER_2D_ARRAY:
321      case GL_SAMPLER_1D_ARRAY_SHADOW:
322      case GL_SAMPLER_2D_ARRAY_SHADOW:
323         assert(uniforms[i].value[0] >= 0.0F);
324         Uniform1i(uniforms[i].location,
325                     (GLint) uniforms[i].value[0]);
326         break;
327      case GL_FLOAT:
328         Uniform1fv(uniforms[i].location, 1, uniforms[i].value);
329         break;
330      case GL_FLOAT_VEC2:
331         Uniform2fv(uniforms[i].location, 1, uniforms[i].value);
332         break;
333      case GL_FLOAT_VEC3:
334         Uniform3fv(uniforms[i].location, 1, uniforms[i].value);
335         break;
336      case GL_FLOAT_VEC4:
337         Uniform4fv(uniforms[i].location, 1, uniforms[i].value);
338         break;
339      case GL_FLOAT_MAT4:
340         UniformMatrix4fv(uniforms[i].location, 1, GL_FALSE,
341                          uniforms[i].value);
342         break;
343      default:
344         if (strncmp(uniforms[i].name, "gl_", 3) == 0) {
345            /* built-in uniform: ignore */
346         }
347         else {
348            fprintf(stderr,
349                    "Unexpected uniform data type in SetUniformValues\n");
350            abort();
351         }
352      }
353   }
354}
355
356
357/** Get list of uniforms used in the program */
358GLuint
359GetUniforms(GLuint program, struct uniform_info uniforms[])
360{
361   GLint n, max, i;
362
363   GetProgramiv(program, GL_ACTIVE_UNIFORMS, &n);
364   GetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max);
365
366   for (i = 0; i < n; i++) {
367      GLint size, len;
368      GLenum type;
369      char name[100];
370
371      glGetActiveUniform(program, i, 100, &len, &size, &type, name);
372
373      uniforms[i].name = strdup(name);
374      uniforms[i].size = size;
375      uniforms[i].type = type;
376      uniforms[i].location = glGetUniformLocation(program, name);
377   }
378
379   uniforms[i].name = NULL; /* end of list */
380
381   return n;
382}
383
384
385void
386PrintUniforms(const struct uniform_info uniforms[])
387{
388   GLint i;
389
390   printf("Uniforms:\n");
391
392   for (i = 0; uniforms[i].name; i++) {
393      printf("  %d: %s size=%d type=0x%x loc=%d value=%g, %g, %g, %g\n",
394             i,
395             uniforms[i].name,
396             uniforms[i].size,
397             uniforms[i].type,
398             uniforms[i].location,
399             uniforms[i].value[0],
400             uniforms[i].value[1],
401             uniforms[i].value[2],
402             uniforms[i].value[3]);
403   }
404}
405
406
407/** Get list of attribs used in the program */
408GLuint
409GetAttribs(GLuint program, struct attrib_info attribs[])
410{
411   GLint n, max, i;
412
413   GetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &n);
414   GetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max);
415
416   for (i = 0; i < n; i++) {
417      GLint size, len;
418      GLenum type;
419      char name[100];
420
421      GetActiveAttrib(program, i, 100, &len, &size, &type, name);
422
423      attribs[i].name = strdup(name);
424      attribs[i].size = size;
425      attribs[i].type = type;
426      attribs[i].location = GetAttribLocation(program, name);
427   }
428
429   attribs[i].name = NULL; /* end of list */
430
431   return n;
432}
433
434
435void
436PrintAttribs(const struct attrib_info attribs[])
437{
438   GLint i;
439
440   printf("Attribs:\n");
441
442   for (i = 0; attribs[i].name; i++) {
443      printf("  %d: %s size=%d type=0x%x loc=%d\n",
444             i,
445             attribs[i].name,
446             attribs[i].size,
447             attribs[i].type,
448             attribs[i].location);
449   }
450}
451