gKit2 light
program.cpp
1 
2 #include <fstream>
3 #include <sstream>
4 #include <string>
5 #include <vector>
6 #include <algorithm>
7 
8 #include <climits>
9 
10 #include "program.h"
11 
12 
13 // charge un fichier texte.
14 static
15 std::string read( const char *filename )
16 {
17  std::stringbuf source;
18  std::ifstream in(filename);
19  if(in.good() == false)
20  printf("[error] loading program '%s'...\n", filename);
21  else
22  printf("loading program '%s'...\n", filename);
23 
24  in.get(source, 0); // lire tout le fichier, le caractere '\0' ne peut pas se trouver dans le source de shader
25  return source.str();
26 }
27 
28 // insere les definitions apres la ligne contenant #version
29 static
30 std::string prepare_source( std::string file, const std::string& definitions )
31 {
32  if(file.empty())
33  return std::string();
34 
35  // un peu de gymnastique, #version doit rester sur la premiere ligne, meme si on insere des #define dans le source
36  std::string source;
37 
38  // recupere la ligne #version
39  std::string version;
40  size_t b= file.find("#version");
41  if(b != std::string::npos)
42  {
43  size_t e = file.find( '\n', b );
44  if(e != std::string::npos)
45  {
46  version= file.substr(0, e +1);
47  file.erase(0, e +1);
48 
49  if(file.find("#version") != std::string::npos)
50  {
51  printf("[error] found several #version directives. failed.\n");
52  return std::string();
53  }
54  }
55  }
56  else
57  {
58  printf("[error] no #version directive found. failed.\n");
59  return std::string();
60  }
61 
62  // reconstruit le source complet
63  if(definitions.empty() == false)
64  {
65  source.append(version); // insere la version
66  source.append(definitions).append("\n"); // insere les definitions
67  source.append(file); // insere le source
68  }
69  else
70  {
71  source.append(version); // re-insere la version (supprimee de file)
72  source.assign(file); // insere le source
73  }
74 
75  return source;
76 }
77 
78 
79 static
80 const char *shader_string( const GLenum type )
81 {
82  switch (type)
83  {
84  case GL_VERTEX_SHADER: return "vertex shader";
85  case GL_FRAGMENT_SHADER: return "fragment shader";
86  case GL_GEOMETRY_SHADER: return "geometry shader";
87  #ifdef GL_VERSION_4_0
88  case GL_TESS_CONTROL_SHADER: return "control shader";
89  case GL_TESS_EVALUATION_SHADER: return "evaluation shader";
90  #endif
91  #ifdef GL_VERSION_4_3
92  case GL_COMPUTE_SHADER: return "compute shader";
93  #endif
94  default: return "shader";
95  }
96 }
97 
98 static
99 const char *shader_keys[] =
100 {
101  "VERTEX_SHADER",
102  "FRAGMENT_SHADER",
103  "GEOMETRY_SHADER",
104  "TESSELATION_CONTROL",
105  "EVALUATION_CONTROL",
106  "COMPUTE_SHADER"
107 };
108 const int shader_keys_max= 6;
109 
110 static
111 GLenum shader_types[] =
112 {
113  GL_VERTEX_SHADER,
114  GL_FRAGMENT_SHADER,
115  GL_GEOMETRY_SHADER,
116 #ifdef GL_VERSION_4_0
117  GL_TESS_CONTROL_SHADER,
118  GL_TESS_EVALUATION_SHADER,
119 #else
120  0,
121  0,
122 #endif
123 #ifdef GL_VERSION_4_3
124  GL_COMPUTE_SHADER
125 #else
126  0
127 #endif
128 };
129 
130 static
131 GLuint compile_shader( const GLuint program, const GLenum shader_type, const std::string& source )
132 {
133  if(source.size() == 0 || shader_type == 0)
134  return 0;
135 
136  GLuint shader= glCreateShader(shader_type);
137  glAttachShader(program, shader);
138 
139  const char *sources= source.c_str();
140  glShaderSource(shader, 1, &sources, NULL);
141  glCompileShader(shader);
142 
143  GLint status;
144  glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
145  return (status == GL_TRUE) ? shader : 0;
146 }
147 
148 
149 int reload_program( const GLuint program, const char *filename, const char *definitions )
150 {
151  if(program == 0)
152  return -1;
153 
154  // supprime les shaders attaches au program
155  int shaders_max= 0;
156  glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
157  if(shaders_max > 0)
158  {
159  std::vector<GLuint> shaders(shaders_max, 0);
160  glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
161  for (int i = 0; i < shaders_max; i++)
162  {
163  glDetachShader(program, shaders[i]);
164  glDeleteShader(shaders[i]);
165  }
166  }
167 
168 #ifdef GL_VERSION_4_3
169  glObjectLabel(GL_PROGRAM, program, -1, filename);
170 #endif
171 
172  // prepare les sources
173  std::string common_source= read(filename);
174  for(int i = 0; i < shader_keys_max; i++)
175  {
176  if(common_source.find(shader_keys[i]) != std::string::npos)
177  {
178  // cree et compile les shaders detectes dans le source
179  std::string source= prepare_source(common_source, std::string(definitions).append("#define ").append(shader_keys[i]).append("\n"));
180  GLuint shader= compile_shader(program, shader_types[i], source);
181  //~ printf(" %s...\n", shader_string(shader_types[i]));
182  if(shader == 0)
183  printf("[error] compiling %s...\n%s\n", shader_string(shader_types[i]), definitions);
184  }
185  }
186 
187  // linke les shaders
188  glLinkProgram(program);
189 
190  // verifie les erreurs
191  GLint status;
192  glGetProgramiv(program, GL_LINK_STATUS, &status);
193  if(status == GL_FALSE)
194  {
195  printf("[error] linking program %u '%s'...\n", program, filename);
196  return -1;
197  }
198 
199  // pour etre coherent avec les autres fonctions de creation, active l'objet gl qui vient d'etre cree.
200  glUseProgram(program);
201  return 0;
202 }
203 
204 GLuint read_program( const char *filename, const char *definitions )
205 {
206  GLuint program= glCreateProgram();
207  reload_program(program, filename, definitions);
208  return program;
209 }
210 
211 int release_program( const GLuint program )
212 {
213  if(program == 0)
214  return -1;
215 
216  // recupere les shaders
217  int shaders_max= 0;
218  glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
219 
220  if(shaders_max > 0)
221  {
222  std::vector<GLuint> shaders(shaders_max, 0);
223  glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
224  for(int i= 0; i < shaders_max; i++)
225  {
226  glDetachShader(program, shaders[i]);
227  glDeleteShader(shaders[i]);
228  }
229  }
230 
231  glDeleteProgram(program);
232  return 0;
233 }
234 
235 
236 bool program_ready( const GLuint program )
237 {
238  if(program == 0)
239  return false;
240 
241  GLint status= GL_FALSE;
242  glGetProgramiv(program, GL_LINK_STATUS, &status);
243 
244 #ifndef GK_RELEASE
245  #ifdef GL_VERSION_4_3
246  char label[1024];
247  glGetObjectLabel(GL_PROGRAM, program, sizeof(label), nullptr, label);
248 
249  if(status == GL_FALSE) // n'affiche le messsage qu'en cas d'erreur...
250  printf("program %u '%s' %s...\n", program, label, (status == GL_TRUE) ? "ready" : "not ready");
251  #else
252  printf("program %u %s...\n", program, (status == GL_TRUE) ? "ready" : "not ready");
253  #endif
254 #endif
255 
256  return (status == GL_TRUE);
257 }
258 
259 bool program_errors( const GLuint program )
260 {
261  if(program == 0)
262  return true;
263 
264  GLint status;
265  glGetProgramiv(program, GL_LINK_STATUS, &status);
266 
267 #ifndef GK_RELEASE
268  #ifdef GL_VERSION_4_3
269  char label[1024];
270  glGetObjectLabel(GL_PROGRAM, program, sizeof(label), nullptr, label);
271 
272  if(status == GL_FALSE) // n'affiche le messsage qu'en cas d'erreur...
273  printf("program %u '%s' %s...\n", program, label, (status == GL_TRUE) ? "ready" : "not ready");
274  #else
275  printf("program %u %s...\n", program, (status == GL_TRUE) ? "ready" : "not ready");
276  #endif
277 #endif
278 
279  return (status == GL_FALSE);
280 }
281 
282 
283 // formatage des erreurs de compilation des shaders
284 
285 static
286 void print_line( std::string& errors, const char *source, const int begin_id, const int line_id )
287 {
288  int line= 0;
289  char last= '\n';
290  for(unsigned int i= 0; source[i] != 0; i++)
291  {
292  if(line > line_id)
293  break;
294 
295  if(last == '\n')
296  {
297  line++;
298  if(line >= begin_id && line <= line_id)
299  {
300  errors.append(" ");
301  errors.push_back('0' + (line / 1000) % 10);
302  errors.push_back('0' + (line / 100) % 10);
303  errors.push_back('0' + (line / 10) % 10);
304  errors.push_back('0' + (line / 1) % 10);
305  errors.append(" ");
306  }
307  }
308 
309  if(line >= begin_id && line <= line_id)
310  {
311  if(source[i] == '\t')
312  errors.append(" ");
313  else
314  errors.push_back(source[i]);
315  }
316  last= source[i];
317  }
318 }
319 
320 static
321 int print_errors( std::string& errors, const char *log, const char *source )
322 {
323  printf("[error log]\n%s\n", log);
324 
325  int first_error= INT_MAX;
326  int last_string= -1;
327  int last_line= -1;
328  for(int i= 0; log[i] != 0; i++)
329  {
330  // recupere la ligne assiciee a l'erreur
331  int string_id= 0, line_id= 0, position= 0;
332  if(sscanf(&log[i], "%d ( %d ) : %n", &string_id, &line_id, &position) == 2 // nvidia syntax
333  || sscanf(&log[i], "%d : %d (%*d) : %n", &string_id, &line_id, &position) == 2 // mesa syntax
334  || sscanf(&log[i], "ERROR : %d : %d : %n", &string_id, &line_id, &position) == 2 // ati syntax
335  || sscanf(&log[i], "WARNING : %d : %d : %n", &string_id, &line_id, &position) == 2) // ati syntax
336  {
337  if(string_id != last_string || line_id != last_line)
338  {
339  // conserve la premiere erreur
340  first_error= std::min(first_error, line_id);
341 
342  // extrait la ligne du source...
343  errors.append("\n");
344  print_line(errors, source, last_line +1, line_id);
345  errors.append("\n");
346  }
347  }
348  // et affiche l'erreur associee...
349  for(i+= position; log[i] != 0; i++)
350  {
351  errors.push_back(log[i]);
352  if(log[i] == '\n')
353  break;
354  }
355 
356  last_string= string_id;
357  last_line= line_id;
358  }
359  errors.append("\n");
360  print_line(errors, source, last_line +1, 1000);
361  errors.append("\n");
362 
363  return first_error;
364 }
365 
366 int program_format_errors( const GLuint program, std::string& errors )
367 {
368  errors.clear();
369 
370  if(program == 0)
371  {
372  errors.append("[error] no program...\n");
373  return -1;
374  }
375 
376  GLint status;
377  glGetProgramiv(program, GL_LINK_STATUS, &status);
378  if(status == GL_TRUE)
379  return 0;
380 
381  int first_error= INT_MAX;
382  // recupere les shaders
383  int shaders_max= 0;
384  glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
385  if(shaders_max == 0)
386  {
387  errors.append("[error] no shaders...\n");
388  return 0;
389  }
390 
391  std::vector<GLuint> shaders(shaders_max, 0);
392  glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
393  for(int i= 0; i < shaders_max; i++)
394  {
395  GLint value;
396  glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &value);
397  if(value == GL_FALSE)
398  {
399  // recupere les erreurs de compilation des shaders
400  glGetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &value);
401  std::vector<char>log(value+1, 0);
402  glGetShaderInfoLog(shaders[i], (GLsizei) log.size(), NULL, &log.front());
403 
404  // recupere le source
405  glGetShaderiv(shaders[i], GL_SHADER_SOURCE_LENGTH, &value);
406  std::vector<char> source(value+1, 0);
407  glGetShaderSource(shaders[i], (GLsizei) source.size(), NULL, &source.front());
408 
409  glGetShaderiv(shaders[i], GL_SHADER_TYPE, &value);
410  errors.append("[error] compiling ").append(shader_string(value)).append("...\n");
411 
412  // formatte les erreurs
413  int last_error= print_errors(errors, &log.front(), &source.front());
414  first_error= std::min(first_error, last_error);
415  }
416  }
417 
418  // recupere les erreurs de link du program
419  {
420  GLint value= 0;
421  glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value);
422 
423  std::vector<char>log(value+1, 0);
424  glGetProgramInfoLog(program, (GLsizei) log.size(), NULL, &log.front());
425 
426  errors.append("[error] linking program...\n").append(log.begin(), log.end());
427  }
428 
429  return first_error;
430 }
431 
432 int program_print_errors( const GLuint program )
433 {
434  std::string errors;
435  int code= program_format_errors(program, errors);
436  if(errors.size() > 0)
437  printf("%s\n", errors.c_str());
438  return code;
439 }
void label(Widgets &w, const char *format,...)
cree un texte. meme fonctionnement que printf().
Definition: widgets.cpp:142
bool value(Widgets &w, const char *label, int &value, const int value_min, const int value_max, const int value_step)
valeur editable par increment.
Definition: widgets.cpp:191
void printf(Text &text, const int px, const int py, const char *format,...)
affiche un texte a la position x, y. meme utilisation que printf().
Definition: text.cpp:140
Point min(const Point &a, const Point &b)
renvoie la plus petite composante de chaque point. x, y, z= min(a.x, b.x), min(a.y,...
Definition: vec.cpp:30
GLuint read_program(const char *filename, const char *definitions)
Definition: program.cpp:204
int program_print_errors(const GLuint program)
affiche les erreurs de compilation.
Definition: program.cpp:432
int release_program(const GLuint program)
detruit les shaders et le program.
Definition: program.cpp:211
bool program_errors(const GLuint program)
renvoie vrai si le programme n'est pas pret.
Definition: program.cpp:259
int reload_program(const GLuint program, const char *filename, const char *definitions)
Definition: program.cpp:149
bool program_ready(const GLuint program)
renvoie vrai si le programme est pret. (pas d'erreurs de compilation des shaders, pas d'erreur de lin...
Definition: program.cpp:236
int program_format_errors(const GLuint program, std::string &errors)
renvoie les erreurs de compilation.
Definition: program.cpp:366