Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gl_program.cpp
Go to the documentation of this file.
1 #include "gl_globals.h"
2 #include "vs_globals.h"
3 #include "vegastrike.h"
4 #include "config_xml.h"
5 #include "gfxlib.h"
6 #include "lin_time.h"
7 #include <map>
8 #include <set>
9 
10 #include <boost/algorithm/string/predicate.hpp>
11 
12 using boost::algorithm::icontains;
13 
14 
15 #if _MSC_VER >= 1300
16 #define snprintf _snprintf
17 #endif
18 
19 typedef std::pair< unsigned int , std::pair< std::string, std::string > > ProgramCacheKey;
20 typedef std::map< ProgramCacheKey, int >ProgramCache;
21 typedef std::map< int, ProgramCacheKey >ProgramICache;
22 
25 
26 static ProgramCache::key_type cacheKey( const std::string &vp, const std::string &fp, const char *defines )
27 {
28  unsigned int defhash = 0;
29  if (defines != NULL) {
30  defhash = 0xBA0BAB00;
31  while (*defines)
32  defhash ^= (defhash * 127) | *(defines++);
33  }
34  return std::pair< unsigned int , std::pair< std::string, std::string > > (defhash, std::pair< std::string, std::string > ( vp, fp ));
35 }
36 
37 static bool validateLog( GLuint obj, bool shader,
38  bool allowSoftwareEmulation = false )
39 {
40  // Retrieve compiler log
41  const GLsizei LOGBUF = 1024;
42  GLsizei infologLength = 0;
43  char infoLog[LOGBUF+1]; // +1 for null terminator
44 
45  if (shader)
46  glGetShaderInfoLog_p( obj, LOGBUF, &infologLength, infoLog );
47  else
48  glGetProgramInfoLog_p( obj, LOGBUF, &infologLength, infoLog );
49 
50  if (infologLength > 0) {
51  // make sure infoLog is null-termiated;
52  assert(infologLength <= LOGBUF);
53  infoLog[infologLength] = 0;
54 
55  // search for signs of emulated execution
56  if (!allowSoftwareEmulation) {
57  if (icontains(infoLog, "run in software"))
58  return false;
59  if (icontains(infoLog, "run on software"))
60  return false;
61  }
62  }
63 
64  // No validation failed...
65  return true;
66 }
67 
68 void printLog( GLuint obj, bool shader )
69 {
70  const GLsizei LOGBUF = 1024;
71  GLsizei infologLength = 0;
72  char infoLog[LOGBUF+1]; // +1 for null terminator
73 
74  if (shader)
75  glGetShaderInfoLog_p( obj, 1024, &infologLength, infoLog );
76  else
77  glGetProgramInfoLog_p( obj, 1024, &infologLength, infoLog );
78 
79  // make sure infoLog is null-termiated;
80  assert(infologLength <= LOGBUF);
81  infoLog[infologLength] = 0;
82 
83  if (infologLength > 0)
84  fprintf( stderr, "%s\n", infoLog );
85 }
86 
87 static VSFileSystem::VSError getProgramSource(const std::string &path, std::vector<std::string> &lines, std::set<std::string> &processed_includes, char *buf, size_t buflen)
88 {
89  std::string dirname = path.substr(0,path.find_last_of('/'));
90 
92  VSFileSystem::VSError err = f.OpenReadOnly( path.c_str(), UnknownFile );
93 
94  const char *include_directive = "#include \"";
95  const size_t include_directive_len = 10;
96  size_t lineno = 0;
97 
98  if (err <= Ok) {
99  processed_includes.insert(path);
100 
101  while (Ok == f.ReadLine(buf, buflen)) {
102  ++lineno;
103  if (strncmp(buf, include_directive, include_directive_len) == 0) {
104  // Process include directives
105  char *eos = strchr(buf+include_directive_len, '\"');
106  if (eos != NULL) {
107  *eos = 0;
108  std::string includepath = dirname + "/" + std::string(buf+include_directive_len);
109  if (processed_includes.count(includepath) == 0) {
110  // Set up line numbers for include file
111  lines.push_back("#line 0\n");
112 
113  VSFileSystem::VSError ierr = getProgramSource(includepath, lines, processed_includes, buf, buflen);
114  if (ierr > Ok) {
115  f.Close();
116  VSFileSystem::vs_fprintf(stderr, "ERROR: included from %s\n", path.c_str());
117  return ierr;
118  } else {
119  // Append a blank line to avoid issues and restore line numbers
120  lines.push_back("\n");
121  snprintf(buf, buflen, "#line %lu\n", lineno);
122  lines.push_back(buf);
123  }
124  } else {
125  // Insert blank line to keep line numbers consistent
126  lines.push_back("\n");
127  }
128  } else {
129  VSFileSystem::vs_fprintf(stderr, "WARNING: broken include directive at file %s, line %d - skipping\n",
130  path.c_str(), lineno);
131  }
132  } else {
133  // Append a line to the list
134  lines.push_back(buf);
135  }
136  }
137 
138  f.Close();
139  } else {
140  VSFileSystem::vs_fprintf(stderr, "ERROR: at %s\n", path.c_str());
141  }
142  return err;
143 }
144 
145 static VSFileSystem::VSError getProgramSource(const std::string &path, std::string &source)
146 {
147  std::set<std::string> processed_includes;
148  std::vector<std::string> lines;
149  char buf[16384];
150 
151  source.clear();
152 
153  VSFileSystem::VSError err = getProgramSource(path, lines, processed_includes, buf, sizeof(buf));
154 
155  if (err <= Ok) {
156  size_t sourcelen=0;
157  for (std::vector<std::string>::const_iterator it = lines.begin(); it != lines.end(); ++it)
158  sourcelen += it->length();
159  source.reserve(sourcelen);
160  for (std::vector<std::string>::const_iterator it = lines.begin(); it != lines.end(); ++it)
161  source += *it;
162  }
163  return err;
164 }
165 
166 static std::string appendDefines( const std::string &prog, const char *extra_defines )
167 {
168  std::string::size_type nlpos = prog.find_first_of('\n');
169 
170  if (nlpos == std::string::npos)
171  nlpos = 0;
172 
173  std::string firstline = prog.substr(0, nlpos);
174 
175  if (firstline.find("#version") != std::string::npos)
176  return firstline
177  + "\n" + std::string(extra_defines)
178  + "\n#line 1"
179  + prog.substr(nlpos);
180  else
181  return std::string(extra_defines)
182  + "\n#line 0\n"
183  + prog;
184 }
185 
186 static int GFXCreateProgramNoCache( const char *vprogram, const char *fprogram, const char *extra_defines )
187 {
188  if (vprogram[0] == '\0' && fprogram[0] == '\0') return 0;
189 #ifndef __APPLE__
190  if (glGetProgramInfoLog_p == NULL || glCreateShader_p == NULL || glShaderSource_p == NULL || glCompileShader_p == NULL
191  || glAttachShader_p == NULL || glLinkProgram_p == NULL || glGetShaderiv_p == NULL || glGetProgramiv_p == NULL)
192  return 0;
193 #else
194 #ifdef OSX_LOWER_THAN_10_4
195  return 0;
196 #endif
197 #endif
198  GLenum errCode;
199  while ( ( errCode = glGetError() ) != GL_NO_ERROR )
200  printf( "Error code %s\n", gluErrorString( errCode ) );
202  std::string vpfilename = std::string("programs/") + vprogram + ".vp";
203  std::string fpfilename = std::string("programs/") + fprogram + ".fp";
204 
205  std::string vertexprg, fragprg;
206  VSFileSystem::VSError vperr = getProgramSource(vpfilename, vertexprg);
207  VSFileSystem::VSError fperr = getProgramSource(fpfilename, fragprg);
208  if ( (vperr > Ok) || (fperr > Ok) ) {
209  if (vperr > Ok)
210  fprintf( stderr, "Vertex Program Error: Failed to open file %s\n", vpfilename.c_str() );
211  if (fperr > Ok)
212  fprintf( stderr, "Fragment Program Error: Failed to open file %s\n", fpfilename.c_str() );
213  return 0;
214  }
215 
216  if (extra_defines != NULL) {
217  vertexprg = appendDefines( vertexprg, extra_defines );
218  fragprg = appendDefines( fragprg, extra_defines );
219  }
220 
221  GLint vproghandle = 0;
222  GLint fproghandle = 0;
223  GLint sp = 0;
224  if (vperr <= Ok) {
225  vproghandle = glCreateShader_p( GL_VERTEX_SHADER );
226  const char *tmp = vertexprg.c_str();
227  glShaderSource_p( vproghandle, 1, &tmp, NULL );
228  glCompileShader_p( vproghandle );
229  GLint successp = 0;
230  glGetShaderiv_p( vproghandle, GL_COMPILE_STATUS, &successp );
231  if (successp == 0) {
232  printLog( vproghandle, true );
233  fprintf( stderr, "Vertex Program Error: Failed to compile %s\n", vprogram );
234  glDeleteShader_p( vproghandle );
235  return 0;
236  } else if (!validateLog( vproghandle, true )) {
237  printLog( vproghandle, true );
238  fprintf( stderr, "Vertex Program Error: Failed log validation for %s. Inspect log above for details.\n", vprogram );
239  glDeleteShader_p( vproghandle );
240  return 0;
241  }
242  printLog( vproghandle, true );
243  }
244  if (fperr <= Ok) {
245  fproghandle = glCreateShader_p( GL_FRAGMENT_SHADER );
246  const char *tmp = fragprg.c_str();
247  glShaderSource_p( fproghandle, 1, &tmp, NULL );
248  glCompileShader_p( fproghandle );
249  GLint successp = 0;
250  glGetShaderiv_p( fproghandle, GL_COMPILE_STATUS, &successp );
251  if (successp == 0) {
252  printLog( fproghandle, true );
253  fprintf( stderr, "Fragment Program Error: Failed to compile %s\n", fprogram );
254  glDeleteShader_p( vproghandle );
255  glDeleteShader_p( fproghandle );
256  return 0;
257  } else if (!validateLog( fproghandle, true )) {
258  printLog( vproghandle, true );
259  fprintf( stderr, "Vertex Program Error: Failed log validation for %s. Inspect log above for details.\n", vprogram );
260  glDeleteShader_p( vproghandle );
261  glDeleteShader_p( fproghandle );
262  return 0;
263  }
264  printLog( fproghandle, true );
265  }
266 
267  sp = glCreateProgram_p();
268  glAttachShader_p( sp, vproghandle );
269  glAttachShader_p( sp, fproghandle );
270  glLinkProgram_p( sp );
271 
272  GLint successp = 0;
273  glGetProgramiv_p( sp, GL_LINK_STATUS, &successp );
274  if (successp == 0) {
275  printLog( sp, false );
276  fprintf( stderr, "Shader Program Error: Failed to link %s to %s\n", vprogram, fprogram );
277  return 0;
278  } else if (!validateLog( sp, false )) {
279  printLog( sp, false );
280  fprintf( stderr, "Shader Program Error: Failed log validation for vp:%s fp:%s. Inspect log above for details.\n", vprogram, fprogram );
281  glDeleteShader_p( vproghandle );
282  glDeleteShader_p( fproghandle );
283  glDeleteProgram_p( sp );
284  return 0;
285  }
286  printLog( sp, false );
287 
288  /* only for dev work
289  * glGetProgramiv_p(sp,GL_VALIDATE_STATUS,&successp);
290  * if (successp==0) {
291  * fprintf(stderr,"Shader Program Error: Failed to validate %s linking to %s\n",vprogram,fprogram);
292  * return 0;
293  * }
294  */
295  while ( ( errCode = glGetError() ) != GL_NO_ERROR ) {
296  printf( "Error code %s\n", gluErrorString( errCode ) );
297  sp = 0; //no proper vertex prog support
298  }
299  return sp;
300 }
301 
302 int GFXCreateProgram( const char *vprogram, const char *fprogram, const char *extra_defines )
303 {
304  ProgramCache::key_type key = cacheKey( vprogram, fprogram, extra_defines );
305  ProgramCache::const_iterator it = programCache.find( key );
306  if ( it != programCache.end() )
307  return it->second;
308  int rv = programCache[key] = GFXCreateProgramNoCache( vprogram, fprogram, extra_defines );
309  programICache[rv] = key;
310  return rv;
311 }
312 
313 int GFXCreateProgram( char *vprogram, char *fprogram, char *extra_defines )
314 {
315  return GFXCreateProgram( (const char*) vprogram, (const char*) fprogram, (const char*) extra_defines );
316 }
317 
318 void GFXDestroyProgram( int program )
319 {
320  // Find program
321  ProgramICache::iterator it = programICache.find( program );
322  if (it != programICache.end()) {
323  /*
324  if (glDeleteProgram_p)
325  glDeleteProgram_p( program );
326  */
327  // FIXME: Real problem here with program leakage,
328  // but cannot destroy like this, brings all kind of issues
329  // since the caller may not hold the only reference.
330  programCache.erase(it->second);
331  programICache.erase(it);
332  }
333 }
334 
335 static int programChanged = false;
336 static int programVersion = 0;
337 static int defaultprog = 0;
338 static int lowfiprog = 0;
339 static int hifiprog = 0;
340 
341 #ifdef __APPLE__
342 std::string hifiProgramName = "mac";
343 std::string lowfiProgramName = "maclite";
344 #else
345 std::string hifiProgramName = "default";
346 std::string lowfiProgramName = "lite";
347 #endif
348 
350 {
351  static bool initted = false;
352  if (!initted) {
353 #ifdef __APPLE__
354  hifiProgramName = vs_config->getVariable( "graphics", "mac_shader_name", "mac" );
355 #else
356  hifiProgramName = vs_config->getVariable( "graphics", "shader_name", "default" );
357 #endif
358  if (hifiProgramName.length() == 0) {
359  lowfiprog = hifiprog = 0;
360  } else {
361  lowfiprog = GFXCreateProgram( lowfiProgramName.c_str(), lowfiProgramName.c_str(), NULL );
362  if (lowfiprog == 0) lowfiprog = GFXCreateProgram( hifiProgramName.c_str(), hifiProgramName.c_str(), NULL );
363  hifiprog = GFXCreateProgram( hifiProgramName.c_str(), hifiProgramName.c_str(), NULL );
364  if (hifiprog == 0) hifiprog = GFXCreateProgram( lowfiProgramName.c_str(), lowfiProgramName.c_str(), NULL );
365  }
367  programChanged = true;
368  initted = true;
369  }
370  return defaultprog;
371 }
372 
374 {
375  VSFileSystem::vs_fprintf(stderr, "Reloading all shaders\n");
376 
377  // Increasing the timestamp makes all programs elsewhere recompile
378  ++programVersion;
379 
380  bool islow = (lowfiprog == defaultprog);
384  }
385  programChanged = true;
386  if (islow) {
387  hifiprog = GFXCreateProgram( hifiProgramName.c_str(), hifiProgramName.c_str(), NULL );
388  if (hifiprog == 0) hifiprog = GFXCreateProgram( lowfiProgramName.c_str(), lowfiProgramName.c_str(), NULL );
389  lowfiprog = GFXCreateProgram( lowfiProgramName.c_str(), lowfiProgramName.c_str(), NULL );
390  if (lowfiprog == 0) lowfiprog = GFXCreateProgram( hifiProgramName.c_str(), hifiProgramName.c_str(), NULL );
392  } else {
393  lowfiprog = GFXCreateProgram( lowfiProgramName.c_str(), lowfiProgramName.c_str(), NULL );
394  if (lowfiprog == 0) lowfiprog = GFXCreateProgram( hifiProgramName.c_str(), hifiProgramName.c_str(), NULL );
395  hifiprog = GFXCreateProgram( hifiProgramName.c_str(), hifiProgramName.c_str(), NULL );
396  if (hifiprog == 0) hifiprog = GFXCreateProgram( lowfiProgramName.c_str(), lowfiProgramName.c_str(), NULL );
398  }
399 }
400 
402 {
406 };
407 
408 unsigned int gpdcounter = (1<<30);
409 #define NUMFRAMESLOOK 128
411 
413 {
414  GameSpeed retval = JUSTRIGHT;
415  static double lasttime = queryTime();
416  double thistime = queryTime();
417  double framerate = 1./(thistime-lasttime);
418  static double toofast = 80;
419  static double tooslow = 29;
420  static unsigned lim = 10;
421  static int penalty = 10;
422  static float lowratio = .125;
423  static float highratio = .75;
424  GameSpeed curframe;
425  if (framerate > toofast) curframe = TOOFAST;
426  else if (framerate > tooslow)
427  curframe = JUSTRIGHT;
428  else curframe = TOOSLOW;
429  gameplaydata[( (unsigned int) gpdcounter++ )%NUMFRAMESLOOK] = curframe;
430  unsigned int i = 0;
431  if ( !( curframe == JUSTRIGHT
432  || (curframe == TOOFAST && defaultprog == hifiprog) || (curframe == TOOSLOW && defaultprog == 0) ) ) {
433  for (; i < lim; ++i)
434  if (curframe != gameplaydata[( (unsigned int) (gpdcounter-i) )%NUMFRAMESLOOK])
435  break;
436  if (i == lim) {
437  int correct = 0;
438  int incorrect = 0;
439  for (unsigned int j = 0; j < NUMFRAMESLOOK; ++j) {
440  if (gameplaydata[j] == curframe) correct++;
441  if (gameplaydata[j] != curframe) incorrect++;
442  if (curframe == TOOFAST && gameplaydata[j] == TOOSLOW)
443  incorrect += penalty;
444  }
445  double myratio = (double) correct/(double) (correct+incorrect);
446  if (curframe == TOOFAST && myratio > highratio) {
447  static int toomanyswitches = 3;
448  toomanyswitches -= 1;
449  if (toomanyswitches >= 0)
450  retval = curframe; //only switch back and forth so many times
451  } else if (myratio > lowratio) {
452  retval = curframe;
453  }
454  }
455  }
456  lasttime = thistime;
457  if (retval != JUSTRIGHT)
458  for (unsigned int i = 0; i < NUMFRAMESLOOK; ++i)
459  gameplaydata[i] = JUSTRIGHT;
460  return retval;
461 }
462 
464 {
465  bool retval = programChanged;
466  static bool framerate_changes_shader =
467  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "framerate_changes_shader", "false" ) );
468  if (framerate_changes_shader) {
469  switch ( GFXGetFramerate() )
470  {
471  case TOOSLOW:
472  if (defaultprog) {
473  retval = true;
474  if (defaultprog == hifiprog)
476  else
477  defaultprog = 0;
478  GFXActivateShader( (char*) NULL );
479  }
480  break;
481  case TOOFAST:
482  if (defaultprog != hifiprog) {
483  retval = true;
484  if (defaultprog == 0)
486  else
488  GFXActivateShader( (char*) NULL );
489  }
490  break;
491  default:
492  break;
493  }
494  }
495  programChanged = false;
496  return retval;
497 }
498 
500 {
501  return getDefaultProgram() != 0;
502 }
503 
504 int GFXActivateShader( int program )
505 {
506  static int lastprogram = 0;
507  if (program != lastprogram)
508  programChanged = true;
509  if (program != lastprogram
510 #ifndef __APPLE__
511  && glUseProgram_p
512 #endif
513  ) {
514  glUseProgram_p( program );
515  lastprogram = program;
516  } else {return 0; } return program;
517 }
518 
519 int GFXActivateShader( const char *program )
520 {
521  int defaultprogram = getDefaultProgram();
522  int curprogram = defaultprogram;
523  if (program)
524  curprogram = GFXCreateProgram( program, program, NULL );
525  return GFXActivateShader( curprogram );
526 }
527 
529 {
530  GFXActivateShader( (int) 0 );
531 }
532 
533 int GFXShaderConstant( int name, float v1, float v2, float v3, float v4 )
534 {
535  if (1
536 #ifndef __APPLE__
537  && glUniform4f_p
538 #endif
539  ) {
540  glUniform4f_p( name, v1, v2, v3, v4 );
541  return 1;
542  }
543  return 0;
544 }
545 
546 int GFXShaderConstant( int name, const float *values )
547 {
548  return GFXShaderConstant( name, values[0], values[1], values[2], values[3] );
549 }
550 
551 int GFXShaderConstant( int name, GFXColor v )
552 {
553  return GFXShaderConstant( name, v.r, v.g, v.b, v.a );
554 }
555 
556 int GFXShaderConstant( int name, Vector v )
557 {
558  return GFXShaderConstant( name, v.i, v.j, v.k, 0 );
559 }
560 
561 int GFXShaderConstant( int name, float v1 )
562 {
563  if (1
564 #ifndef __APPLE__
565  && glUniform1f_p
566 #endif
567  ) {
568  glUniform1f_p( name, v1 );
569  return 1;
570  }
571  return 0;
572 }
573 
574 int GFXShaderConstantv( int name, unsigned int count, const float *values )
575 {
576  if (1
577 #ifndef __APPLE__
578  && glUniform1fv_p
579 #endif
580  ) {
581  glUniform1fv_p( name, count, values );
582  return 1;
583  }
584  return 0;
585 }
586 
587 int GFXShaderConstant4v( int name, unsigned int count, const float *values )
588 {
589  if (1
590 #ifndef __APPLE__
591  && glUniform4fv_p
592 #endif
593  ) {
594  glUniform4fv_p( name, count, values );
595  return 1;
596  }
597  return 0;
598 }
599 
600 int GFXShaderConstanti( int name, int value )
601 {
602  if (1
603 #ifndef __APPLE__
604  && glUniform1i_p
605 #endif
606  ) {
607  glUniform1i_p( name, value );
608  return 1;
609  }
610  return 0;
611 }
612 
613 int GFXShaderConstantv( int name, unsigned int count, const int *value )
614 {
615  if (1
616 #ifndef __APPLE__
617  && glUniform1i_p
618 #endif
619  ) {
620  glUniform1iv_p( name, count, (GLint*) value );
621  return 1;
622  }
623  return 0;
624 }
625 
626 int GFXNamedShaderConstant( int progID, const char *name )
627 {
628  if (1
629 #ifndef __APPLE__
631 #endif
632  ) {
633  int varloc = glGetUniformLocation_p( progID, name );
634  return varloc;
635  }
636  return -1; //varloc cound be 0
637 }
638 
639 int GFXNamedShaderConstant( char *progID, const char *name )
640 {
641  int programname = defaultprog;
642  if (progID)
643  programname = programCache[cacheKey( progID, progID, NULL )];
644  return GFXNamedShaderConstant( programname, name );
645 }
646 
648 {
649  return programVersion;
650 }
651