gKit2 light
Loading...
Searching...
No Matches
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.
14static
15std::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
29static
30std::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
79static
80const 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 #ifdef GL_EXT_mesh_shader
95 case GL_MESH_SHADER_EXT: return "mesh shader";
96 case GL_TASK_SHADER_EXT: return "task mesh shader";
97 #endif
98
99 default: return "shader";
100 }
101}
102
103static
104const char *shader_keys[] =
105{
106 "VERTEX_SHADER",
107 "FRAGMENT_SHADER",
108 "GEOMETRY_SHADER",
109 "TESSELATION_CONTROL",
110 "EVALUATION_CONTROL",
111 "COMPUTE_SHADER",
112 "MESH_SHADER",
113 "TASK_SHADER"
114};
115const int shader_keys_max= 8;
116
117static
118GLenum shader_types[] =
119{
120 GL_VERTEX_SHADER,
121 GL_FRAGMENT_SHADER,
122 GL_GEOMETRY_SHADER,
123#ifdef GL_VERSION_4_0
124 GL_TESS_CONTROL_SHADER,
125 GL_TESS_EVALUATION_SHADER,
126#else
127 0,
128 0,
129#endif
130#ifdef GL_VERSION_4_3
131 GL_COMPUTE_SHADER,
132#else
133 0,
134#endif
135#ifdef GL_EXT_mesh_shader
136 GL_MESH_SHADER_EXT,
137 GL_TASK_SHADER_EXT
138#else
139 0,
140 0
141#endif
142};
143
144static
145GLuint compile_shader( const GLuint program, const GLenum shader_type, const std::string& source )
146{
147 if(source.size() == 0 || shader_type == 0)
148 return 0;
149
150 GLuint shader= glCreateShader(shader_type);
151 glAttachShader(program, shader);
152
153 const char *sources= source.c_str();
154 glShaderSource(shader, 1, &sources, NULL);
155 glCompileShader(shader);
156
157 GLint status;
158 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
159 return (status == GL_TRUE) ? shader : 0;
160}
161
162
163int reload_program( const GLuint program, const char *filename, const char *definitions )
164{
165 if(program == 0)
166 return -1;
167
168 // supprime les shaders attaches au program
169 int shaders_max= 0;
170 glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
171 if(shaders_max > 0)
172 {
173 std::vector<GLuint> shaders(shaders_max, 0);
174 glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
175 for (int i = 0; i < shaders_max; i++)
176 {
177 glDetachShader(program, shaders[i]);
178 glDeleteShader(shaders[i]);
179 }
180 }
181
182#ifdef GL_VERSION_4_3
183 glObjectLabel(GL_PROGRAM, program, -1, filename);
184#endif
185
186 // prepare les sources
187 std::string common_source= read(filename);
188 for(int i = 0; i < shader_keys_max; i++)
189 {
190 if(common_source.find(shader_keys[i]) != std::string::npos)
191 {
192 // cree et compile les shaders detectes dans le source
193 std::string source= prepare_source(common_source, std::string(definitions).append("#define ").append(shader_keys[i]).append("\n"));
194 GLuint shader= compile_shader(program, shader_types[i], source);
195 //~ printf(" %s...\n", shader_string(shader_types[i]));
196 if(shader == 0)
197 printf("[error] compiling %s...\n%s\n", shader_string(shader_types[i]), definitions);
198 }
199 }
200
201 // linke les shaders
202 glLinkProgram(program);
203
204 // verifie les erreurs
205 GLint status;
206 glGetProgramiv(program, GL_LINK_STATUS, &status);
207 if(status == GL_FALSE)
208 {
209 printf("[error] linking program %u '%s'...\n", program, filename);
210 return -1;
211 }
212
213 // pour etre coherent avec les autres fonctions de creation, active l'objet gl qui vient d'etre cree.
214 glUseProgram(program);
215 return 0;
216}
217
218GLuint read_program( const char *filename, const char *definitions )
219{
220 GLuint program= glCreateProgram();
221 reload_program(program, filename, definitions);
222 return program;
223}
224
225int release_program( const GLuint program )
226{
227 if(program == 0)
228 return -1;
229
230 // recupere les shaders
231 int shaders_max= 0;
232 glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
233
234 if(shaders_max > 0)
235 {
236 std::vector<GLuint> shaders(shaders_max, 0);
237 glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
238 for(int i= 0; i < shaders_max; i++)
239 {
240 glDetachShader(program, shaders[i]);
241 glDeleteShader(shaders[i]);
242 }
243 }
244
245 glDeleteProgram(program);
246 return 0;
247}
248
249
250bool program_ready( const GLuint program )
251{
252 if(program == 0)
253 return false;
254
255 GLint status= GL_FALSE;
256 glGetProgramiv(program, GL_LINK_STATUS, &status);
257
258#ifndef GK_RELEASE
259 #ifdef GL_VERSION_4_3
260 char label[1024];
261 glGetObjectLabel(GL_PROGRAM, program, sizeof(label), nullptr, label);
262
263 if(status == GL_FALSE) // n'affiche le messsage qu'en cas d'erreur...
264 printf("program %u '%s' %s...\n", program, label, (status == GL_TRUE) ? "ready" : "not ready");
265 #else
266 printf("program %u %s...\n", program, (status == GL_TRUE) ? "ready" : "not ready");
267 #endif
268#endif
269
270 return (status == GL_TRUE);
271}
272
273bool program_errors( const GLuint program )
274{
275 if(program == 0)
276 return true;
277
278 GLint status;
279 glGetProgramiv(program, GL_LINK_STATUS, &status);
280
281#ifndef GK_RELEASE
282 #ifdef GL_VERSION_4_3
283 char label[1024];
284 glGetObjectLabel(GL_PROGRAM, program, sizeof(label), nullptr, label);
285
286 if(status == GL_FALSE) // n'affiche le messsage qu'en cas d'erreur...
287 printf("program %u '%s' %s...\n", program, label, (status == GL_TRUE) ? "ready" : "not ready");
288 #else
289 printf("program %u %s...\n", program, (status == GL_TRUE) ? "ready" : "not ready");
290 #endif
291#endif
292
293 return (status == GL_FALSE);
294}
295
296
297// formatage des erreurs de compilation des shaders
298
299static
300void print_line( std::string& errors, const char *source, const int begin_id, const int line_id )
301{
302 int line= 0;
303 char last= '\n';
304 for(unsigned int i= 0; source[i] != 0; i++)
305 {
306 if(line > line_id)
307 break;
308
309 if(last == '\n')
310 {
311 line++;
312 if(line >= begin_id && line <= line_id)
313 {
314 errors.append(" ");
315 errors.push_back('0' + (line / 1000) % 10);
316 errors.push_back('0' + (line / 100) % 10);
317 errors.push_back('0' + (line / 10) % 10);
318 errors.push_back('0' + (line / 1) % 10);
319 errors.append(" ");
320 }
321 }
322
323 if(line >= begin_id && line <= line_id)
324 {
325 if(source[i] == '\t')
326 errors.append(" ");
327 else
328 errors.push_back(source[i]);
329 }
330 last= source[i];
331 }
332}
333
334static
335int print_errors( std::string& errors, const char *log, const char *source )
336{
337 printf("[error log]\n%s\n", log);
338
339 int first_error= INT_MAX;
340 int last_string= -1;
341 int last_line= -1;
342 for(int i= 0; log[i] != 0; i++)
343 {
344 // recupere la ligne assiciee a l'erreur
345 int string_id= 0, line_id= 0, position= 0;
346 if(sscanf(&log[i], "%d ( %d ) : %n", &string_id, &line_id, &position) == 2 // nvidia syntax
347 || sscanf(&log[i], "%d : %d (%*d) : %n", &string_id, &line_id, &position) == 2 // mesa syntax
348 || sscanf(&log[i], "ERROR : %d : %d : %n", &string_id, &line_id, &position) == 2 // ati syntax
349 || sscanf(&log[i], "WARNING : %d : %d : %n", &string_id, &line_id, &position) == 2) // ati syntax
350 {
351 if(string_id != last_string || line_id != last_line)
352 {
353 // conserve la premiere erreur
354 first_error= std::min(first_error, line_id);
355
356 // extrait la ligne du source...
357 errors.append("\n");
358 print_line(errors, source, last_line +1, line_id);
359 errors.append("\n");
360 }
361 }
362 // et affiche l'erreur associee...
363 for(i+= position; log[i] != 0; i++)
364 {
365 errors.push_back(log[i]);
366 if(log[i] == '\n')
367 break;
368 }
369
370 last_string= string_id;
371 last_line= line_id;
372 }
373 errors.append("\n");
374 print_line(errors, source, last_line +1, 1000);
375 errors.append("\n");
376
377 return first_error;
378}
379
380int program_format_errors( const GLuint program, std::string& errors )
381{
382 errors.clear();
383
384 if(program == 0)
385 {
386 errors.append("[error] no program...\n");
387 return -1;
388 }
389
390 GLint status;
391 glGetProgramiv(program, GL_LINK_STATUS, &status);
392 if(status == GL_TRUE)
393 return 0;
394
395 int first_error= INT_MAX;
396 // recupere les shaders
397 int shaders_max= 0;
398 glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max);
399 if(shaders_max == 0)
400 {
401 errors.append("[error] no shaders...\n");
402 return 0;
403 }
404
405 std::vector<GLuint> shaders(shaders_max, 0);
406 glGetAttachedShaders(program, shaders_max, NULL, &shaders.front());
407 for(int i= 0; i < shaders_max; i++)
408 {
409 GLint value;
410 glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &value);
411 if(value == GL_FALSE)
412 {
413 // recupere les erreurs de compilation des shaders
414 glGetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &value);
415 std::vector<char>log(value+1, 0);
416 glGetShaderInfoLog(shaders[i], (GLsizei) log.size(), NULL, &log.front());
417
418 // recupere le source
419 glGetShaderiv(shaders[i], GL_SHADER_SOURCE_LENGTH, &value);
420 std::vector<char> source(value+1, 0);
421 glGetShaderSource(shaders[i], (GLsizei) source.size(), NULL, &source.front());
422
423 glGetShaderiv(shaders[i], GL_SHADER_TYPE, &value);
424 errors.append("[error] compiling ").append(shader_string(value)).append("...\n");
425
426 // formatte les erreurs
427 int last_error= print_errors(errors, &log.front(), &source.front());
428 first_error= std::min(first_error, last_error);
429 }
430 }
431
432 // recupere les erreurs de link du program
433 {
434 GLint value= 0;
435 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value);
436
437 std::vector<char>log(value+1, 0);
438 glGetProgramInfoLog(program, (GLsizei) log.size(), NULL, &log.front());
439
440 errors.append("[error] linking program...\n").append(log.begin(), log.end());
441 }
442
443 return first_error;
444}
445
446int program_print_errors( const GLuint program )
447{
448 std::string errors;
449 int code= program_format_errors(program, errors);
450 if(errors.size() > 0)
451 printf("%s\n", errors.c_str());
452 return code;
453}
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
GLuint read_program(const char *filename, const char *definitions)
Definition program.cpp:218
int program_print_errors(const GLuint program)
affiche les erreurs de compilation.
Definition program.cpp:446
int release_program(const GLuint program)
detruit les shaders et le program.
Definition program.cpp:225
bool program_errors(const GLuint program)
renvoie vrai si le programme n'est pas pret.
Definition program.cpp:273
int reload_program(const GLuint program, const char *filename, const char *definitions)
Definition program.cpp:163
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:250
int program_format_errors(const GLuint program, std::string &errors)
renvoie les erreurs de compilation.
Definition program.cpp:380