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