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 <GL/glut.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
23
24GLboolean
25ShadersSupported(void)
26{
27   const char *version = (const char *) glGetString(GL_VERSION);
28
29   /* NVIDIA binary drivers will return "3.0.0", and they clearly support
30    * shaders.
31    */
32   if (version[0] >= '2' && version[1] == '.') {
33      return GL_TRUE;
34   }
35   else if (glutExtensionSupported("GL_ARB_vertex_shader")
36            && glutExtensionSupported("GL_ARB_fragment_shader")
37            && glutExtensionSupported("GL_ARB_shader_objects")) {
38      fprintf(stderr, "Warning: Trying ARB GLSL instead of OpenGL 2.x.  This may not work.\n");
39      return GL_TRUE;
40   }
41   fprintf(stderr, "Sorry, GLSL not supported with this OpenGL.\n");
42   return GL_FALSE;
43}
44
45
46GLuint
47CompileShaderText(GLenum shaderType, const char *text)
48{
49   GLuint shader;
50   GLint stat;
51   GLdouble t0, t1;
52
53   shader = glCreateShader(shaderType);
54   glShaderSource(shader, 1, (const GLchar **) &text, NULL);
55
56   t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
57   glCompileShader(shader);
58   t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
59
60   CompileTime = t1 - t0;
61
62   glGetShaderiv(shader, GL_COMPILE_STATUS, &stat);
63   if (!stat) {
64      GLchar log[1000];
65      GLsizei len;
66      glGetShaderInfoLog(shader, 1000, &len, log);
67      fprintf(stderr, "Error: problem compiling shader: %s\n", log);
68      exit(1);
69   }
70   else {
71      /*printf("Shader compiled OK\n");*/
72   }
73   return shader;
74}
75
76
77/**
78 * Read a shader from a file.
79 */
80GLuint
81CompileShaderFile(GLenum shaderType, const char *filename)
82{
83   const int max = 100*1000;
84   int n;
85   char *buffer = (char*) malloc(max);
86   GLuint shader;
87   FILE *f;
88
89   f = fopen(filename, "r");
90   if (!f) {
91      fprintf(stderr, "Unable to open shader file %s\n", filename);
92      free(buffer);
93      return 0;
94   }
95
96   n = fread(buffer, 1, max, f);
97   /*printf("read %d bytes from shader file %s\n", n, filename);*/
98   if (n > 0) {
99      buffer[n] = 0;
100      shader = CompileShaderText(shaderType, buffer);
101   }
102   else {
103      fclose(f);
104      free(buffer);
105      return 0;
106   }
107
108   fclose(f);
109   free(buffer);
110
111   return shader;
112}
113
114
115GLuint
116LinkShaders(GLuint vertShader, GLuint fragShader)
117{
118   GLuint program = glCreateProgram();
119   GLdouble t0, t1;
120
121   assert(vertShader || fragShader);
122
123   if (fragShader)
124      glAttachShader(program, fragShader);
125   if (vertShader)
126      glAttachShader(program, vertShader);
127
128   t0 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
129   glLinkProgram(program);
130   t1 = glutGet(GLUT_ELAPSED_TIME) * 0.001;
131
132   LinkTime = t1 - t0;
133
134   /* check link */
135   {
136      GLint stat;
137      glGetProgramiv(program, GL_LINK_STATUS, &stat);
138      if (!stat) {
139         GLchar log[1000];
140         GLsizei len;
141         glGetProgramInfoLog(program, 1000, &len, log);
142         fprintf(stderr, "Shader link error:\n%s\n", log);
143         return 0;
144      }
145   }
146
147   return program;
148}
149
150
151GLboolean
152ValidateShaderProgram(GLuint program)
153{
154   GLint stat;
155   glValidateProgramARB(program);
156   glGetProgramiv(program, GL_VALIDATE_STATUS, &stat);
157
158   if (!stat) {
159      GLchar log[1000];
160      GLsizei len;
161      glGetProgramInfoLog(program, 1000, &len, log);
162      fprintf(stderr, "Program validation error:\n%s\n", log);
163      return 0;
164   }
165
166   return (GLboolean) stat;
167}
168
169
170GLdouble
171GetShaderCompileTime(void)
172{
173   return CompileTime;
174}
175
176
177GLdouble
178GetShaderLinkTime(void)
179{
180   return LinkTime;
181}
182
183
184void
185SetUniformValues(GLuint program, struct uniform_info uniforms[])
186{
187   GLuint i;
188
189   for (i = 0; uniforms[i].name; i++) {
190      uniforms[i].location
191         = glGetUniformLocation(program, uniforms[i].name);
192
193      switch (uniforms[i].type) {
194      case GL_INT:
195      case GL_SAMPLER_1D:
196      case GL_SAMPLER_2D:
197      case GL_SAMPLER_3D:
198      case GL_SAMPLER_CUBE:
199      case GL_SAMPLER_2D_RECT_ARB:
200         assert(uniforms[i].value[0] >= 0.0F);
201         glUniform1i(uniforms[i].location,
202                     (GLint) uniforms[i].value[0]);
203         break;
204      case GL_FLOAT:
205         glUniform1fv(uniforms[i].location, 1, uniforms[i].value);
206         break;
207      case GL_FLOAT_VEC2:
208         glUniform2fv(uniforms[i].location, 1, uniforms[i].value);
209         break;
210      case GL_FLOAT_VEC3:
211         glUniform3fv(uniforms[i].location, 1, uniforms[i].value);
212         break;
213      case GL_FLOAT_VEC4:
214         glUniform4fv(uniforms[i].location, 1, uniforms[i].value);
215         break;
216      default:
217         if (strncmp(uniforms[i].name, "gl_", 3) == 0) {
218            /* built-in uniform: ignore */
219         }
220         else {
221            fprintf(stderr,
222                    "Unexpected uniform data type in SetUniformValues\n");
223            abort();
224         }
225      }
226   }
227}
228
229
230/** Get list of uniforms used in the program */
231GLuint
232GetUniforms(GLuint program, struct uniform_info uniforms[])
233{
234   GLint n, max, i;
235
236   glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &n);
237   glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max);
238
239   for (i = 0; i < n; i++) {
240      GLint size, len;
241      GLenum type;
242      char name[100];
243
244      glGetActiveUniform(program, i, 100, &len, &size, &type, name);
245
246      uniforms[i].name = strdup(name);
247      uniforms[i].size = size;
248      uniforms[i].type = type;
249      uniforms[i].location = glGetUniformLocation(program, name);
250   }
251
252   uniforms[i].name = NULL; /* end of list */
253
254   return n;
255}
256
257
258void
259PrintUniforms(const struct uniform_info uniforms[])
260{
261   GLint i;
262
263   printf("Uniforms:\n");
264
265   for (i = 0; uniforms[i].name; i++) {
266      printf("  %d: %s size=%d type=0x%x loc=%d value=%g, %g, %g, %g\n",
267             i,
268             uniforms[i].name,
269             uniforms[i].size,
270             uniforms[i].type,
271             uniforms[i].location,
272             uniforms[i].value[0],
273             uniforms[i].value[1],
274             uniforms[i].value[2],
275             uniforms[i].value[3]);
276   }
277}
278
279
280/** Get list of attribs used in the program */
281GLuint
282GetAttribs(GLuint program, struct attrib_info attribs[])
283{
284   GLint n, max, i;
285
286   glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &n);
287   glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max);
288
289   for (i = 0; i < n; i++) {
290      GLint size, len;
291      GLenum type;
292      char name[100];
293
294      glGetActiveAttrib(program, i, 100, &len, &size, &type, name);
295
296      attribs[i].name = strdup(name);
297      attribs[i].size = size;
298      attribs[i].type = type;
299      attribs[i].location = glGetAttribLocation(program, name);
300   }
301
302   attribs[i].name = NULL; /* end of list */
303
304   return n;
305}
306
307
308void
309PrintAttribs(const struct attrib_info attribs[])
310{
311   GLint i;
312
313   printf("Attribs:\n");
314
315   for (i = 0; attribs[i].name; i++) {
316      printf("  %d: %s size=%d type=0x%x loc=%d\n",
317             i,
318             attribs[i].name,
319             attribs[i].size,
320             attribs[i].type,
321             attribs[i].location);
322   }
323}
324