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 #include <cstdio>
10 
11 #include "program.h"
12 
13 
14 // charge un fichier texte.
15 static
16 std::string read( const char *filename )
17 {
18  std::stringbuf source;
19  std::ifstream in(filename);
20  if(in.good() == false)
21  printf("[error] loading program '%s'...\n", filename);
22  else
23  printf("loading program '%s'...\n", filename);
24 
25  in.get(source, 0); // lire tout le fichier, le caractere '\0' ne peut pas se trouver dans le source de shader
26  return source.str();
27 }
28 
29 // insere les definitions apres la ligne contenant #version
30 static
31 std::string prepare_source( std::string file, const std::string& definitions )
32 {
33  if(file.empty())
34  return std::string();
35 
36  // un peu de gymnastique, #version doit rester sur la premiere ligne, meme si on insere des #define dans le source
37  std::string source;
38 
39  // recupere la ligne #version
40  std::string version;
41  size_t b= file.find("#version");
42  if(b != std::string::npos)
43  {
44  size_t e = file.find( '\n', b );
45  if(e != std::string::npos)
46  {
47  version= file.substr(0, e +1);
48  file.erase(0, e +1);
49 
50  if(file.find("#version") != std::string::npos)
51  {
52  printf("[error] found several #version directives. failed.\n");
53  return std::string();
54  }
55  }
56  }
57  else
58  {
59  printf("[error] no #version directive found. failed.\n");
60  return std::string();
61  }
62 
63  // reconstruit le source complet
64  if(definitions.empty() == false)
65  {
66  source.append(version); // insere la version
67  source.append(definitions).append("\n"); // insere les definitions
68  source.append(file); // insere le source
69  }
70  else
71  {
72  source.append(version); // re-insere la version (supprimee de file)
73  source.assign(file); // insere le source
74  }
75 
76  return source;
77 }
78 
79 static
80 GLuint compile_shader( const GLuint program, const GLenum shader_type, const std::string& source )
81 {
82  if(source.size() == 0)
83  return 0;
84 
85  GLuint shader= glCreateShader(shader_type);
86  glAttachShader(program, shader);
87 
88  const char *sources= source.c_str();
89  glShaderSource(shader, 1, &sources, NULL);
90  glCompileShader(shader);
91 
92  GLint status;
93  glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
94  return (status == GL_TRUE) ? shader : 0;
95 }
96 
97 
98 int reload_program( GLuint program, const char *filename, const char *definitions )
99 {
100  if(program == 0)
101  return -1;
102 
103  // supprime les shaders attaches au program
104  int shaders_max= 0;
105  glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
106  if(shaders_max > 0)
107  {
108  std::vector<GLuint> shaders(shaders_max, 0);
109  glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
110  for (int i = 0; i < shaders_max; i++)
111  {
112  glDetachShader(program, shaders[i]);
113  glDeleteShader(shaders[i]);
114  }
115  }
116 
117 #ifdef GL_VERSION_4_3
118  glObjectLabel(GL_PROGRAM, program, -1, filename);
119 #endif
120 
121  // prepare les sources
122  std::string common_source= read(filename);
123  std::string vertex_source= prepare_source(common_source, std::string(definitions).append("#define VERTEX_SHADER\n"));
124  std::string fragment_source= prepare_source(common_source, std::string(definitions).append("#define FRAGMENT_SHADER\n"));
125 
126  // cree et compile un vertex shader et un fragment shader
127  GLuint vertex_shader= compile_shader(program, GL_VERTEX_SHADER, vertex_source);
128  GLuint fragment_shader= compile_shader(program, GL_FRAGMENT_SHADER, fragment_source);
129  // linke les shaders
130  glLinkProgram(program);
131 
132  // verifie les erreurs
133  GLint status;
134  glGetProgramiv(program, GL_LINK_STATUS, &status);
135  if(status == GL_FALSE)
136  {
137  if(vertex_shader == 0)
138  printf("[error] compiling vertex shader...\n%s\n", definitions);
139  if(fragment_shader == 0)
140  printf("[error] compiling fragment shader...\n%s\n", definitions);
141  printf("[error] linking program %u '%s'...\n", program, filename);
142  return -1;
143  }
144 
145  // pour etre coherent avec les autres fonctions de creation, active l'objet gl qui vient d'etre cree.
146  glUseProgram(program);
147  return 0;
148 }
149 
150 GLuint read_program( const char *filename, const char *definitions )
151 {
152  GLuint program= glCreateProgram();
153  reload_program(program, filename, definitions);
154  return program;
155 }
156 
157 int release_program( const GLuint program )
158 {
159  if(program == 0)
160  return -1;
161 
162  // recupere les shaders
163  int shaders_max= 0;
164  glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
165 
166  std::vector<GLuint> shaders(shaders_max, 0);
167  glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
168  for(int i= 0; i < shaders_max; i++)
169  {
170  glDetachShader(program, shaders[i]);
171  glDeleteShader(shaders[i]);
172  }
173 
174  glDeleteProgram(program);
175  return 0;
176 }
177 
178 
179 // formatage des erreurs de compilation des shaders
180 
181 static
182 void print_line( std::string& errors, const char *source, const int begin_id, const int line_id )
183 {
184  int line= 0;
185  char last= '\n';
186  for(unsigned int i= 0; source[i] != 0; i++)
187  {
188  if(line > line_id)
189  break;
190 
191  if(last == '\n')
192  {
193  line++;
194  if(line >= begin_id && line <= line_id)
195  {
196  errors.append(" ");
197  errors.push_back('0' + (line / 1000) % 10);
198  errors.push_back('0' + (line / 100) % 10);
199  errors.push_back('0' + (line / 10) % 10);
200  errors.push_back('0' + (line / 1) % 10);
201  errors.append(" ");
202  }
203  }
204 
205  if(line >= begin_id && line <= line_id)
206  {
207  if(source[i] == '\t')
208  errors.append(" ");
209  else
210  errors.push_back(source[i]);
211  }
212  last= source[i];
213  }
214 }
215 
216 static
217 int print_errors( std::string& errors, const char *log, const char *source )
218 {
219  printf("[error log]\n%s\n", log);
220 
221  int first_error= INT_MAX;
222  int last_string= -1;
223  int last_line= -1;
224  for(int i= 0; log[i] != 0; i++)
225  {
226  // recupere la ligne assiciee a l'erreur
227  int string_id= 0, line_id= 0, position= 0;
228  if(sscanf(&log[i], "%d ( %d ) : %n", &string_id, &line_id, &position) == 2 // nvidia syntax
229  || sscanf(&log[i], "%d : %d (%*d) : %n", &string_id, &line_id, &position) == 2 // mesa syntax
230  || sscanf(&log[i], "ERROR : %d : %d : %n", &string_id, &line_id, &position) == 2 // ati syntax
231  || sscanf(&log[i], "WARNING : %d : %d : %n", &string_id, &line_id, &position) == 2) // ati syntax
232  {
233  if(string_id != last_string || line_id != last_line)
234  {
235  // conserve la premiere erreur
236  first_error= std::min(first_error, line_id);
237 
238  // extrait la ligne du source...
239  errors.append("\n");
240  print_line(errors, source, last_line +1, line_id);
241  errors.append("\n");
242  }
243  }
244  // et affiche l'erreur associee...
245  for(i+= position; log[i] != 0; i++)
246  {
247  errors.push_back(log[i]);
248  if(log[i] == '\n')
249  break;
250  }
251 
252  last_string= string_id;
253  last_line= line_id;
254  }
255  errors.append("\n");
256  print_line(errors, source, last_line +1, 1000);
257  errors.append("\n");
258 
259  return first_error;
260 }
261 
262 int program_format_errors( const GLuint program, std::string& errors )
263 {
264  errors.clear();
265 
266  if(program == 0)
267  {
268  errors.append("[error] no program...\n");
269  return -1;
270  }
271 
272  GLint status;
273  glGetProgramiv(program, GL_LINK_STATUS, &status);
274  if(status == GL_TRUE)
275  return 0;
276 
277  int first_error= INT_MAX;
278 
279  // recupere les shaders
280  int shaders_max= 0;
281  glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
282 
283  std::vector<GLuint> shaders(shaders_max, 0);
284  glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
285  for(int i= 0; i < shaders_max; i++)
286  {
287  GLint value;
288  glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &value);
289  if(value == GL_FALSE)
290  {
291  // recupere les erreurs de compilation des shaders
292  glGetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &value);
293  std::vector<char>log(value+1, 0);
294  glGetShaderInfoLog(shaders[i], (GLsizei) log.size(), NULL, &log.front());
295 
296  // recupere le source
297  glGetShaderiv(shaders[i], GL_SHADER_SOURCE_LENGTH, &value);
298  std::vector<char> source(value+1, 0);
299  glGetShaderSource(shaders[i], source.size(), NULL, &source.front());
300 
301  glGetShaderiv(shaders[i], GL_SHADER_TYPE, &value);
302  errors.append("[error] compiling ");
303  if(value == GL_VERTEX_SHADER)
304  errors.append("vertex shader...\n");
305  else if(value == GL_FRAGMENT_SHADER)
306  errors.append("fragment shader...\n");
307  else
308  errors.append("shader...\n");
309 
310  // formatte les erreurs
311  int last_error= print_errors(errors, &log.front(), &source.front());
312  first_error= std::min(first_error, last_error);
313  }
314  }
315 
316  // recupere les erreurs de link du program
317  {
318  GLint value= 0;
319  glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value);
320 
321  std::vector<char>log(value+1, 0);
322  glGetProgramInfoLog(program, (GLsizei) log.size(), NULL, &log.front());
323 
324  errors.append("[error] linking program...\n").append(log.begin(), log.end());
325  }
326 
327  return first_error;
328 }
329 
330 int program_print_errors( const GLuint program )
331 {
332  std::string errors;
333  int code= program_format_errors(program, errors);
334  if(errors.size() > 0)
335  printf("%s\n", errors.c_str());
336  return code;
337 }
338 
int program_print_errors(const GLuint program)
affiche les erreurs de compilation.
Definition: program.cpp:330
GLuint read_program(const char *filename, const char *definitions)
Definition: program.cpp:150
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
int program_format_errors(const GLuint program, std::string &errors)
renvoie les erreurs de compilation.
Definition: program.cpp:262
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
int release_program(const GLuint program)
detruit les shaders et le program.
Definition: program.cpp:157
int reload_program(GLuint program, const char *filename, const char *definitions)
recharge les sources et recompile un shader program.
Definition: program.cpp:98