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
technique.cpp
Go to the documentation of this file.
1 //
2 //C++ Implementation: Technique
3 //
4 
5 #include "technique.h"
6 #include "config.h"
7 
8 #include "XMLDocument.h"
9 #include "VSFileXMLSerializer.h"
10 #include "vsfilesystem.h"
11 #include "gfxlib.h"
12 #include "aux_texture.h"
13 
14 #include <exception>
15 #include <map>
16 #include <boost/smart_ptr.hpp>
17 
18 #include "xml_support.h"
19 
20 #include "options.h"
21 #include "gldrv/gl_globals.h"
22 #include "audio/Exceptions.h"
23 
24 using namespace XMLDOM;
25 using std::map;
26 using std::auto_ptr;
27 
28 #ifdef _MSC_VER
29 //Undefine those nasty MS macros - why god why!?
30 #undef max
31 #undef min
32 #endif
33 
34 namespace __impl
35 {
36 //
37 
38 class Exception : public std::exception
39 {
40 private:
41  std::string _message;
42 public:
43  virtual ~Exception() throw () {}
44  Exception() {}
45  Exception( const Exception &other ) : _message( other._message ) {}
46  explicit Exception( const std::string &message ) : _message( message ) {}
47  virtual const char * what() const throw ()
48  {
49  return _message.c_str();
50  }
51 };
52 
54 {
55 public: InvalidParameters() {}
56  InvalidParameters( const string &msg ) : Exception( msg ) {}
57 };
58 
60 {
61 public: ProgramCompileError() {}
62  ProgramCompileError( const string &msg ) : Exception( msg ) {}
63 };
64 
65 template < typename T >
66 static T parseEnum( const string &s, const map< string, T > &enumMap )
67 {
68  typename map< string, T >::const_iterator it = enumMap.find( s );
69  if ( it != enumMap.end() )
70  return it->second;
71  else throw InvalidParameters( "Enumerated value \""+s+"\" not recognized" );
72 }
73 
74 template < typename T >
75 static T parseEnum( const string &s, const map< string, T > &enumMap, T deflt )
76 {
77  typename map< string, T >::const_iterator it = enumMap.find( s );
78  if ( it != enumMap.end() )
79  return it->second;
80  else
81  return deflt;
82 }
83 
84 static Technique::Pass::TextureUnit::SourceType parseSourceType( const string &s, string::size_type &sep )
85 {
86  static map< string, Technique::Pass::TextureUnit::SourceType >enumMap;
87  if ( enumMap.empty() ) {
88  enumMap["decal"] = Technique::Pass::TextureUnit::Decal;
89  enumMap["file"] = Technique::Pass::TextureUnit::File;
90  enumMap["environment"] = Technique::Pass::TextureUnit::Environment;
91  enumMap["detail"] = Technique::Pass::TextureUnit::Detail;
92  }
93  return parseEnum( s.substr( 0, sep = s.find_first_of( ':' ) ), enumMap, Technique::Pass::TextureUnit::None );
94 }
95 
97 {
98  static map< string, Technique::Pass::TextureUnit::Kind >enumMap;
99  if ( enumMap.empty() ) {
100  enumMap["default"] = Technique::Pass::TextureUnit::TexDefault;
101  enumMap["2d"] = Technique::Pass::TextureUnit::Tex2D;
102  enumMap["3d"] = Technique::Pass::TextureUnit::Tex3D;
103  enumMap["cube"] = Technique::Pass::TextureUnit::TexCube;
104  enumMap["separatedCube"] = Technique::Pass::TextureUnit::TexSepCube;
105  }
107 }
108 
109 static Technique::Pass::Type parsePassType( const std::string &s )
110 {
111  static map< string, Technique::Pass::Type >enumMap;
112  if ( enumMap.empty() ) {
113  enumMap["fixed"] = Technique::Pass::FixedPass;
114  enumMap["shader"] = Technique::Pass::ShaderPass;
115  }
116  return parseEnum( s, enumMap );
117 }
118 
119 static bool parseBool( const std::string &s )
120 {
121  if ( s.empty() ) throw InvalidParameters( "Missing required attribute" );
122  else
123  return XMLSupport::parse_bool( s );
124 }
125 
126 static Technique::Pass::Tristate parseTristate( const std::string &s )
127 {
128  static map< string, Technique::Pass::Tristate >enumMap;
129  if ( enumMap.empty() ) {
130  enumMap["true"] = Technique::Pass::True;
131  enumMap["false"] = Technique::Pass::False;
132  enumMap["auto"] = Technique::Pass::Auto;
133  }
134  return parseEnum( s, enumMap );
135 }
136 
137 static Technique::Pass::BlendMode parseBlendMode(const std::string &s)
138 {
139  static map<string, Technique::Pass::BlendMode> enumMap;
140  if (enumMap.empty()) {
141  enumMap["default"] = Technique::Pass::Default;
142  enumMap["add"] = Technique::Pass::Add;
143  enumMap["multiply"] = Technique::Pass::Multiply;
144  enumMap["alpha_blend"] = Technique::Pass::AlphaBlend;
145  enumMap["decal"] = Technique::Pass::Decal;
146  enumMap["premult_alpha"]=Technique::Pass::PremultAlphaBlend;
147  enumMap["multi_alpha_blend"]=Technique::Pass::MultiAlphaBlend;
148  }
149  return parseEnum(s, enumMap);
150 }
151 
152 static Technique::Pass::Face parseFace( const std::string &s )
153 {
154  static map< string, Technique::Pass::Face >enumMap;
155  if ( enumMap.empty() ) {
156  enumMap["none"] = Technique::Pass::None;
157  enumMap["back"] = Technique::Pass::Back;
158  enumMap["front"] = Technique::Pass::Front;
159  enumMap["both"] = Technique::Pass::FrontAndBack;
160  enumMap["default"] = Technique::Pass::DefaultFace;
161  }
162  return parseEnum( s, enumMap );
163 }
164 
166 {
167  static map< string, Technique::Pass::DepthFunction >enumMap;
168  if ( enumMap.empty() ) {
169  enumMap["less"] = Technique::Pass::Less;
170  enumMap["lequal"] = Technique::Pass::LEqual;
171  enumMap["greater"] = Technique::Pass::Greater;
172  enumMap["gequal"] = Technique::Pass::GEqual;
173  enumMap["equal"] = Technique::Pass::Equal;
174  enumMap["always"] = Technique::Pass::Always;
175  enumMap["never"] = Technique::Pass::Never;
176  }
177  return parseEnum(s, enumMap);
178 }
179 
180 static Technique::Pass::PolyMode parsePolyMode(const std::string &s)
181 {
182  static map<string, Technique::Pass::PolyMode> enumMap;
183  if (enumMap.empty()) {
184  enumMap["point"] = Technique::Pass::Point;
185  enumMap["line"] = Technique::Pass::Line;
186  enumMap["fill"] = Technique::Pass::Fill;
187  }
188  return parseEnum(s, enumMap);
189 }
190 
192 {
193  static map< string, Technique::Pass::ShaderParam::Semantic >enumMap;
194  if ( enumMap.empty() ) {
195  enumMap["EnvColor"] = Technique::Pass::ShaderParam::EnvColor;
196  enumMap["CloakingPhase"] = Technique::Pass::ShaderParam::CloakingPhase;
197  enumMap["Damage"] = Technique::Pass::ShaderParam::Damage;
198  enumMap["Damage4"] = Technique::Pass::ShaderParam::Damage4;
199  enumMap["DetailPlane0"] = Technique::Pass::ShaderParam::DetailPlane0;
200  enumMap["DetailPlane1"] = Technique::Pass::ShaderParam::DetailPlane1;
201  enumMap["NumLights"] = Technique::Pass::ShaderParam::NumLights;
202  enumMap["ActiveLightsArray"] = Technique::Pass::ShaderParam::ActiveLightsArray;
203  enumMap["ApparentLightSizeArray"] =
205  enumMap["GameTime"] = Technique::Pass::ShaderParam::GameTime;
206  }
207  return parseEnum( s, enumMap );
208 }
209 
210 static int parseIteration( const std::string &s )
211 {
212  static string once( "once" );
213  if (s == once)
214  return 0;
215  else if ( s.empty() ) throw InvalidParameters( "Invalid iteration attribute" );
216  else
217  return XMLSupport::parse_int( s );
218 }
219 
220 static int parseInt( const std::string &s )
221 {
222  if ( s.empty() ) throw InvalidParameters( "Invalid integer attribute" );
223  else
224  return XMLSupport::parse_int( s );
225 }
226 
227 static int parseInt( const std::string &s, int deflt )
228 {
229  if ( s.empty() )
230  return deflt;
231  else
232  return XMLSupport::parse_int( s );
233 }
234 
235 static float parseFloat( const std::string &s )
236 {
237  if ( s.empty() ) throw InvalidParameters( "Invalid float attribute" );
238  else
239  return XMLSupport::parse_floatf( s );
240 }
241 
242 static void parseFloat4( const std::string &s, float value[4] )
243 {
244  string::size_type ini = 0, end;
245  int i = 0;
246  while (i < 4 && ini != string::npos) {
247  value[i++] = parseFloat( s.substr( ini, end = s.find_first_of( ',', ini ) ) );
248  ini = ( (end == string::npos) ? end : (end+1) );
249  }
250  if (i >= 4 && ini != string::npos)
251  VSFileSystem::vs_dprintf(1, "WARNING: invalid float4: %s\n", s.c_str());
252  while (i < 4)
253  value[i++] = 0;
254 }
255 
256 //end namespace
257 };
258 
259 using namespace __impl;
260 
262  : program( 0 )
263  , type( FixedPass )
264  , colorWrite( true )
265  , zWrite( True )
266  , perLightIteration( 0 )
267  , maxIterations( 0 )
268  , blendMode( Default )
269  , depthFunction( LEqual )
270  , cullMode( DefaultFace )
271  , polyMode( Fill )
272  , offsetFactor( 0 )
273  , offsetUnits( 0 )
274  , lineWidth( 1 )
275  , sequence( 0 )
276 {}
277 
279 {
280  //Should deallocate the program... but... GFX doesn't have that API.
281 }
282 
283 void Technique::Pass::setProgram( const string &vertex, const string &fragment )
284 {
285  vertexProgram = vertex;
286  fragmentProgram = fragment;
287  program = 0;
288 }
289 
290 void Technique::Pass::addTextureUnit( const string &source,
291  int target,
292  const string &deflt,
293  const string &paramName,
295 {
296  textureUnits.resize( textureUnits.size()+1 );
297  TextureUnit &newTU = textureUnits.back();
298 
299  string::size_type ssep = string::npos, dsep = string::npos;
300  newTU.sourceType = parseSourceType( source, ssep );
301  newTU.defaultType = parseSourceType( deflt, dsep );
302  newTU.targetIndex =
303  newTU.origTargetIndex = target;
304  newTU.targetParamName = paramName;
305  newTU.targetParamId = -1;
306  newTU.texKind = texKind;
307  switch (newTU.sourceType)
308  {
309  case TextureUnit::Decal:
310  case TextureUnit::Detail:
311  if (ssep == string::npos) throw InvalidParameters( "Decal/Detail reference missing source index" );
312  newTU.sourceIndex = atoi( source.c_str()+ssep+1 );
313  break;
314  case TextureUnit::File:
315  if (ssep == string::npos) throw InvalidParameters( "File reference missing path" );
316  newTU.sourcePath.assign( source, ssep+1, string::npos );
317  break;
318  case TextureUnit::Environment:
319  break;
320  default: throw InvalidParameters( "Missing source" );
321  }
322  switch (newTU.defaultType)
323  {
324  case TextureUnit::Decal:
325  case TextureUnit::Detail:
326  if (dsep == string::npos) throw InvalidParameters( "Decal/Detail reference missing source index" );
327  newTU.defaultIndex = atoi( deflt.c_str()+dsep+1 );
328  break;
329  case TextureUnit::File:
330  if (dsep == string::npos) throw InvalidParameters( "File reference missing path" );
331  newTU.defaultPath.assign( deflt, dsep+1, string::npos );
332  break;
333  case TextureUnit::None: //FIXME added by chuck_starchaser; please verify correctness
334  case TextureUnit::Environment: //FIXME added by chuck_starchaser; please verify correctness
335  default: //FIXME added by chuck_starchaser; please verify correctness
336  break; //FIXME added by chuck_starchaser; please verify correctness
337  }
338 }
339 
340 void Technique::Pass::addShaderParam( const string &name, float value[4], bool optional )
341 {
342  shaderParams.resize( shaderParams.size()+1 );
343  ShaderParam &newSP = shaderParams.back();
344 
345  newSP.name = name;
346  newSP.id = -1;
347  newSP.semantic = ShaderParam::Constant;
348  newSP.optional = optional;
349  for (int i = 0; i < 4; ++i)
350  newSP.value[i] = value[i];
351 }
352 
353 void Technique::Pass::addShaderParam( const string &name, ShaderParam::Semantic semantic, bool optional )
354 {
355  shaderParams.resize( shaderParams.size()+1 );
356  ShaderParam &newSP = shaderParams.back();
357 
358  newSP.name = name;
359  newSP.id = -1;
360  newSP.semantic = semantic;
361  newSP.optional = optional;
362 }
363 
366 {
367  if (type == ShaderPass) {
368  int prog = program; // BEGIN TRANSACTION
369 
370  if (prog != 0 && programVersion != GFXGetProgramVersion()) {
371  GFXDestroyProgram(program);
372  prog = 0;
373  }
374 
375  if (prog == 0) {
376  std::string defines;
377 
378  // Automatic defines
379  if (sRGBAware) {
381  defines += "#define SRGB_FRAMEBUFFER 1\n";
382  else
383  defines += "#define SRGB_FRAMEBUFFER 0\n";
384  }
385  if (gl_options.nv_fp2)
386  defines += "#define VGL_NV_fragment_program2 1\n";
387 
388  // Compile program
389  prog = GFXCreateProgram( vertexProgram.c_str(), fragmentProgram.c_str(),
390  (defines.empty() ? NULL : defines.c_str()) );
391  if (prog == 0)
392  throw ProgramCompileError(
393  "Error compiling program vp:\""+vertexProgram
394  +"\" fp:\""+fragmentProgram+"\"" );
395  else
396  VSFileSystem::vs_dprintf( 1, "Successfully compiled and linked program \"%s+%s\"\n",
397  vertexProgram.c_str(), fragmentProgram.c_str() );
398  }
399 
400  for (ShaderParamList::iterator it = shaderParams.begin(); it != shaderParams.end(); ++it) {
401  it->id = GFXNamedShaderConstant( prog, it->name.c_str() );
402  if (it->id < 0) {
403  if (!it->optional)
404  throw ProgramCompileError( "Cannot resolve shader constant \""+it->name+"\"" );
405  else
406  VSFileSystem::vs_dprintf( 1, "Cannot resolve <<optional>> shader constant \"%s\" in program \"%s+%s\"\n",
407  it->name.c_str(), vertexProgram.c_str(), fragmentProgram.c_str() );
408  }
409  }
410  int lastTU = -1;
411  for (TextureUnitList::iterator tit = textureUnits.begin(); tit != textureUnits.end(); ++tit) {
412  if (tit->sourceType == TextureUnit::File) {
413  // Yep, we don't want to reload textures
414  if (tit->texture.get() == 0) {
415  tit->texture.reset( new Texture( tit->sourcePath.c_str() ) );
416  if ( !tit->texture->LoadSuccess() ) throw InvalidParameters(
417  "Cannot load texture file \""+tit->sourcePath+"\"" );
418  }
419  } else if (tit->defaultType == TextureUnit::File) {
420  // Yep, we don't want to reload textures
421  if (tit->texture.get() == 0) {
422  tit->texture.reset( new Texture( tit->defaultPath.c_str() ) );
423  if ( !tit->texture->LoadSuccess() ) throw InvalidParameters(
424  "Cannot load texture file \""+tit->defaultPath+"\"" );
425  }
426  }
427  if (!tit->targetParamName.empty()) {
428  tit->targetParamId = GFXNamedShaderConstant( prog, tit->targetParamName.c_str() );
429  if (tit->targetParamId < 0) {
430  if (tit->origTargetIndex >= 0)
431  throw ProgramCompileError(
432  "Cannot resolve shader constant \""+tit->targetParamName+"\"" );
433  else
434  tit->targetIndex = -1;
435  } else {
436  if (tit->origTargetIndex < 0)
437  tit->targetIndex = lastTU+1;
438  lastTU = std::max( tit->targetIndex, lastTU );
439  }
440  }
441  }
442 
443  // COMMIT ;-)
444  program = prog;
445  programVersion = GFXGetProgramVersion();
446  }
447 }
448 
451 {
452  return (type != ShaderPass) || (program != 0);
453 }
454 
456 bool Technique::Pass::isCompiled(int programVersion) const
457 {
458  return (type != ShaderPass) || (program != 0 && this->programVersion == programVersion);
459 }
460 
461 Technique::Technique( const string &nam ) :
462  name( nam )
463  , compiled( false )
464  , programVersion( 0 )
465 {
466  static string passTag( "pass" );
467  static string techniqueTag( "technique" );
468  static string vpTag( "vertex_program" );
469  static string fpTag( "fragment_program" );
470  static string tuTag( "texture_unit" );
471  static string paramTag( "param" );
472  static string autoParamTag( "auto_param" );
473 
474  VSFileXMLSerializer serializer;
475  serializer.options = 0; //only tags interest us
476  serializer.initialise();
477 
478  try {
479  // Try a specialized version
480  serializer.importXML(
483  +name+".technique" );
484  } catch(Audio::FileOpenException e) {
485  VSFileSystem::vs_dprintf(1, "Cannot find specialized technique, trying generic: %s\n", e.what());
486  // Else try a default
487  serializer.importXML(
489  +name+".technique" );
490  }
491 
492  auto_ptr< XMLDOM::XMLDocument >doc( serializer.close() );
493 
494  //Search for the <technique> tag
495  XMLElement *techniqueNode = 0;
496  {
497  for (XMLElement::const_child_iterator it = doc->root.childrenBegin(); it != doc->root.childrenEnd(); ++it) {
498  XMLElement *el = *it;
499  if (el->type() == XMLElement::XET_TAG && el->tagName() == techniqueTag) {
500  techniqueNode = el;
501  break;
502  }
503  }
504  if (techniqueNode == 0) throw InvalidParameters( "No technique tag!" );
505  }
506 
507  fallback = techniqueNode->getAttributeValue( "fallback", "" );
508 
509  unsigned int nextSequence = 0;
510  for (XMLElement::const_child_iterator it = techniqueNode->childrenBegin(); it != techniqueNode->childrenEnd(); ++it) {
511  XMLElement *el = *it;
512  if (el->type() == XMLElement::XET_TAG) {
513  if (el->tagName() == passTag) {
514  passes.resize( passes.size()+1 );
515  Pass &pass = passes.back();
516 
517  pass.type = parsePassType( el->getAttributeValue( "type", "" ) );
518  pass.colorWrite = parseBool( el->getAttributeValue( "cwrite", "true" ) );
519  pass.zWrite = parseTristate( el->getAttributeValue( "zwrite", "auto" ) );
520  pass.perLightIteration = parseIteration( el->getAttributeValue( "iteration", "once" ) );
521  pass.maxIterations = parseInt( el->getAttributeValue( "maxiterations", "0" ) );
522  pass.blendMode = parseBlendMode( el->getAttributeValue( "blend", "default" ) );
523  pass.sequence = parseInt( el->getAttributeValue( "sequence", "" ), nextSequence );
524  pass.depthFunction = parseDepthFunction( el->getAttributeValue( "depth_function", "lequal" ) );
525  pass.cullMode = parseFace( el->getAttributeValue( "cull", "default" ) );
526  pass.polyMode = parsePolyMode( el->getAttributeValue( "polygon_mode", "fill" ) );
527  pass.offsetUnits = parseFloat( el->getAttributeValue( "polygon_offset_units", "0" ) );
528  pass.offsetFactor = parseFloat( el->getAttributeValue( "polygon_offset_factor", "0" ) );
529  pass.lineWidth = parseFloat( el->getAttributeValue( "line_width", "1" ) );
530  pass.sRGBAware = parseBool( el->getAttributeValue( "srgb_aware", "false" ) );
531  nextSequence = pass.sequence+1;
532 
533  string vp, fp;
534  for (XMLElement::const_child_iterator cit = el->childrenBegin(); cit != el->childrenEnd(); ++cit) {
535  XMLElement *el = *cit;
536  if (el->type() == XMLElement::XET_TAG) {
537  if (el->tagName() == vpTag) {
538  if ( !vp.empty() ) throw InvalidParameters(
539  "Duplicate vertex program reference in technique \""+name+"\"" );
540  vp = el->getAttributeValue( "src", "" );
541  } else if (el->tagName() == fpTag) {
542  if ( !fp.empty() ) throw InvalidParameters(
543  "Duplicate fragment program reference in technique \""+name+"\"" );
544  fp = el->getAttributeValue( "src", "" );
545  } else if (el->tagName() == tuTag) {
546  int target;
547  if (pass.type == Pass::ShaderPass)
548  target = parseInt( el->getAttributeValue( "target", "" ), -1 );
549  else
550  target = parseInt( el->getAttributeValue( "target", "" ) );
551  pass.addTextureUnit(
552  el->getAttributeValue( "src", "" ),
553  target,
554  el->getAttributeValue( "default", "" ),
555  el->getAttributeValue( "name", "" ),
556  parseTexKind( el->getAttributeValue( "kind", "" ) ) );
557  VSFileSystem::vs_dprintf(2, "Added texture unit #%d \"%s\"\n",
558  pass.getNumTextureUnits(),
559  el->getAttributeValue( "name","" ).c_str());
560  } else if (el->tagName() == paramTag) {
561  float value[4];
562  parseFloat4( el->getAttributeValue( "value", "" ), value );
563  pass.addShaderParam(
564  el->getAttributeValue( "name", "" ),
565  value,
566  parseBool( el->getAttributeValue( "optional", "false" ) ) );
567  VSFileSystem::vs_dprintf(2, "Added constant #%d \"%s\" with value (%.2f,%.2f,%.2f,%.2f) as %s\n",
568  pass.getNumShaderParams(),
569  el->getAttributeValue( "name","" ).c_str(),
570  value[0], value[1], value[2], value[3],
571  (parseBool( el->getAttributeValue( "optional", "false" ) ) ? "optional" : "required"));
572  } else if (el->tagName() == autoParamTag) {
573  pass.addShaderParam(
574  el->getAttributeValue( "name", "" ),
575  parseAutoParamSemantic( el->getAttributeValue( "semantic", "" ) ),
576  parseBool( el->getAttributeValue( "optional", "false" ) ) );
577  VSFileSystem::vs_dprintf(2, "Added param #%d \"%s\" with semantic %s as %s\n",
578  pass.getNumShaderParams(),
579  el->getAttributeValue( "name","" ).c_str(),
580  el->getAttributeValue( "semantic", "" ).c_str(),
581  (parseBool( el->getAttributeValue( "optional", "false" ) ) ? "optional" : "required"));
582  } else {
583  //TODO: Warn about unrecognized (hence ignored) tag
584  }
585  }
586  }
587  if (pass.type == Pass::ShaderPass) {
588  if ( vp.empty() )
589  throw InvalidParameters( "Missing vertex program reference in technique \""+name+"\"" );
590  if ( fp.empty() )
591  throw InvalidParameters( "Missing fragment program reference in technique \""+name+"\"" );
592  pass.setProgram( vp, fp );
593  }
594  } else {
595  //TODO: Warn about unrecognized (hence ignored) tag
596  }
597  }
598  }
599 }
600 
602  name( src.name )
603  , fallback( src.fallback )
604  , compiled( false )
605  , programVersion( 0 )
606  , passes( src.passes )
607 {
608  // Not much else to do
609  // Compiled techniques are still valid, and setting any parameter
610  // that would invalidate it would result in recompilation
611  // FIXME: should result in recompilation, not necessarily true now
612 }
613 
615 {
616 }
617 
619 {
620  if (!compiled || (GFXGetProgramVersion() != programVersion)) {
621  for (PassList::iterator it = passes.begin(); it != passes.end(); ++it)
622  it->compile();
623  compiled = true;
624  programVersion = GFXGetProgramVersion();
625  }
626 }
627 
628 typedef map< string, TechniquePtr >TechniqueMap;
630 
631 TechniquePtr Technique::getTechnique( const std::string &name )
632 {
633  TechniqueMap::const_iterator it = techniqueCache.find( name );
634  if ( it != techniqueCache.end() ) {
635  return it->second;
636  } else {
637  TechniquePtr ptr( new Technique( name ) );
638  while ( !ptr->isCompiled() ) {
639  try {
640  ptr->compile();
641  VSFileSystem::vs_fprintf( stdout,
642  "Compilation of technique %s successful\n",
643  ptr->getName().c_str() );
644  }
645  catch (ProgramCompileError e) {
646  std::string fallback = ptr->getFallback();
647  VSFileSystem::vs_fprintf( stderr,
648  "Compilation of technique %s failed... trying %s\n"
649  "Cause: %s\n",
650  ptr->getName().c_str(),
651  fallback.c_str(),
652  e.what() );
653  if (!fallback.empty() && fallback != name)
654  ptr = getTechnique( fallback );
655  else
656  break;
657  }
658  }
659  if ( ptr->isCompiled() )
660  techniqueCache[name] = ptr;
661  else
662  throw InvalidParameters( "Could not compile any technique for \""+name+"\"" );
663  return ptr;
664  }
665 }
666