Developer Documentation
ShaderCache.cc
1 /*===========================================================================*\
2  * *
3  * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40 \*===========================================================================*/
41 
42 /*===========================================================================*\
43  * *
44  * $Revision$ *
45  * $Author$ *
46  * $Date$ *
47  * *
48 \*===========================================================================*/
49 
50 
51 #include "ShaderCache.hh"
52 
53 #include <cstdio>
54 #include <iostream>
55 #include <fstream>
56 
57 
58 #include <QFile>
59 #include <QFileInfo>
60 #include <QDir>
61 #include <QTextStream>
62 
63 #include <ACG/GL/GLError.hh>
64 #include <ACG/ShaderUtils/GLSLShader.hh>
65 
66 
67 namespace ACG
68 {
69 
70 ShaderCache::ShaderCache():
71  cache_(),
72  cacheStatic_(),
73  timeCheck_(false)
74 {
75 }
76 
78 {
79  // free all glsl programs in cache
80  for (CacheList::iterator it = cache_.begin(); it != cache_.end(); ++it)
81  delete it->second;
82 
83  for (CacheList::iterator it = cacheStatic_.begin(); it != cacheStatic_.end(); ++it)
84  delete it->second;
85 
86  for (CacheList::iterator it = cacheComputeShaders_.begin(); it != cacheComputeShaders_.end(); ++it)
87  delete it->second;
88 }
89 
91 {
92  static ShaderCache singleton;
93  return &singleton;
94 }
95 
96 
98 {
99  std::vector<unsigned int> dummy;
100  return getProgram(_desc, dummy);
101 }
102 
103 //***********************************************************************
104 // TODO implement binary search eventually (if cache access is getting too slow)
105 // - modify compareShaderGenDescs s.t. it defines an order
106 // or generate a hash key from ShaderGenDesc
107 
108 GLSL::Program* ACG::ShaderCache::getProgram( const ShaderGenDesc* _desc, const std::vector<unsigned int>& _mods )
109 {
110  CacheEntry newEntry;
111  newEntry.desc = *_desc;
112  newEntry.mods = _mods;
113 
114  if (!_desc->fragmentTemplateFile.isEmpty())
115  {
116  newEntry.strFragmentTemplate = _desc->fragmentTemplateFile;
117  newEntry.fragmentFileLastMod = QFileInfo(newEntry.strFragmentTemplate).lastModified();
118  }
119 
120  if (!_desc->tessControlTemplateFile.isEmpty())
121  {
122  newEntry.strTessControlTemplate = _desc->tessControlTemplateFile;
123  newEntry.tessControlFileLastMod = QFileInfo(newEntry.strTessControlTemplate).lastModified();
124  }
125 
126  if (!_desc->tessEvaluationTemplateFile.isEmpty())
127  {
128  newEntry.strTessEvaluationTemplate = _desc->tessEvaluationTemplateFile;
129  newEntry.tessEvaluationFileLastMod = QFileInfo(newEntry.strTessEvaluationTemplate).lastModified();
130  }
131 
132  if (!_desc->geometryTemplateFile.isEmpty())
133  {
134  newEntry.strGeometryTemplate = _desc->geometryTemplateFile;
135  newEntry.geometryFileLastMod = QFileInfo(newEntry.strGeometryTemplate).lastModified();
136  }
137 
138  if (!_desc->vertexTemplateFile.isEmpty())
139  {
140  newEntry.strVertexTemplate = _desc->vertexTemplateFile;
141  newEntry.vertexFileLastMod = QFileInfo(newEntry.strVertexTemplate).lastModified();
142  }
143 
144  CacheList::iterator oldCache = cache_.end();
145 
146  for (CacheList::iterator it = cache_.begin(); it != cache_.end(); ++it)
147  {
148  // If the shaders are equal, we return the cached entry
149  if (!compareShaderGenDescs(&it->first, &newEntry))
150  {
151  if ( timeCheck_ && !compareTimeStamp(&it->first, &newEntry))
152  oldCache = it;
153  else
154  return it->second;
155  }
156  }
157 
158  // glsl program not in cache, generate shaders
159  ShaderProgGenerator progGen(_desc, _mods);
160 
161  if (!dbgOutputDir_.isEmpty())
162  {
163  static int counter = 0;
164 
165  QString fileName;
166  fileName.sprintf("shader_%02d.glsl", counter++);
167  fileName = dbgOutputDir_ + QDir::separator() + fileName;
168 
169  QFile fileOut(fileName);
170  if (fileOut.open(QFile::WriteOnly | QFile::Truncate))
171  {
172  QTextStream outStrm(&fileOut);
173 
174  outStrm << _desc->toString();
175  outStrm << "\nmods: ";
176 
177  for (size_t i = 0; i < _mods.size(); ++i)
178  outStrm << _mods[i] << (i+1 < _mods.size() ? ", " : "");
179  outStrm << "\n";
180 
181 
182  outStrm << "\n---------------------vertex-shader--------------------\n\n";
183 
184  for (int i = 0; i < progGen.getVertexShaderCode().size(); ++i)
185  outStrm << progGen.getVertexShaderCode()[i] << "\n";
186 
187  if (progGen.hasTessControlShader())
188  {
189  outStrm << "\n---------------------tesscontrol-shader--------------------\n\n";
190 
191  for (int i = 0; i < progGen.getTessControlShaderCode().size(); ++i)
192  outStrm << progGen.getTessControlShaderCode()[i] << "\n";
193  }
194 
195  if (progGen.hasTessEvaluationShader())
196  {
197  outStrm << "\n---------------------tesseval-shader--------------------\n\n";
198 
199  for (int i = 0; i < progGen.getTessEvaluationShaderCode().size(); ++i)
200  outStrm << progGen.getTessEvaluationShaderCode()[i] << "\n";
201  }
202 
203  if (progGen.hasGeometryShader())
204  {
205  outStrm << "\n---------------------geometry-shader--------------------\n\n";
206 
207  for (int i = 0; i < progGen.getGeometryShaderCode().size(); ++i)
208  outStrm << progGen.getGeometryShaderCode()[i] << "\n";
209  }
210 
211  outStrm << "\n---------------------fragment-shader--------------------\n\n";
212 
213  for (int i = 0; i < progGen.getFragmentShaderCode().size(); ++i)
214  outStrm << progGen.getFragmentShaderCode()[i] << "\n";
215 
216 
217  fileOut.close();
218  }
219  }
220 
221  GLSL::FragmentShader* fragShader = new GLSL::FragmentShader();
222  GLSL::VertexShader* vertShader = new GLSL::VertexShader();
223 
224  vertShader->setSource(progGen.getVertexShaderCode());
225  fragShader->setSource(progGen.getFragmentShaderCode());
226 
227  vertShader->compile();
228  fragShader->compile();
229 
230  GLSL::Program* prog = new GLSL::Program();
231  prog->attach(vertShader);
232  prog->attach(fragShader);
233 
234  // Check if we have a geometry shader and if we have support for it, enable it here
235  if ( progGen.hasGeometryShader() ) {
236  GLSL::GeometryShader* geomShader = new GLSL::GeometryShader();
237  geomShader->setSource(progGen.getGeometryShaderCode());
238  geomShader->compile();
239  prog->attach(geomShader);
240  }
241 
242  // Check if we have tessellation shaders and if we have support for it, enable it here
243  if ( progGen.hasTessControlShader() || progGen.hasTessEvaluationShader() ) {
244  GLSL::Shader* tessControlShader = 0, *tessEvalShader = 0;
245 
246 #ifdef GL_ARB_tessellation_shader
247  tessControlShader = new GLSL::TessControlShader();
248  tessEvalShader = new GLSL::TessEvaluationShader();
249 #endif // GL_ARB_tessellation_shader
250 
251  if (tessControlShader && progGen.hasTessControlShader())
252  {
253  tessControlShader->setSource(progGen.getTessControlShaderCode());
254  tessControlShader->compile();
255  prog->attach(tessControlShader);
256  }
257 
258  if (tessEvalShader && progGen.hasTessEvaluationShader())
259  {
260  tessEvalShader->setSource(progGen.getTessEvaluationShaderCode());
261  tessEvalShader->compile();
262  prog->attach(tessEvalShader);
263  }
264 
265  }
266 
267  prog->link();
268  glCheckErrors();
269 
270  if (oldCache != cache_.end())
271  {
272  if (!prog->isLinked())
273  {
274  delete prog;
275  return oldCache->second;
276  }
277  else
278  {
279  cache_.erase(oldCache);
280  }
281  }
282 
283  cache_.push_back(std::pair<CacheEntry, GLSL::Program*>(newEntry, prog));
284 
285  return prog;
286 }
287 
288 GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile,
289  const char* _tessControlShaderFile,
290  const char* _tessEvalShaderFile,
291  const char* _geometryShaderFile,
292  const char* _fragmentShaderFile,
293  QStringList* _macros, bool _verbose )
294 {
295  CacheEntry newEntry;
296 
297 
298  // store filenames and timestamps in new entry
299 
300  // fragment shader
301  QFileInfo fileInfo(_fragmentShaderFile);
302  if (fileInfo.isRelative())
303  {
304  QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_fragmentShaderFile);
305  fileInfo = QFileInfo(absFilename);
306 
307  newEntry.strFragmentTemplate = absFilename;
308  newEntry.fragmentFileLastMod = fileInfo.lastModified();
309  }
310  else
311  {
312  newEntry.strFragmentTemplate = _fragmentShaderFile;
313  newEntry.fragmentFileLastMod = fileInfo.lastModified();
314  }
315 
316  // vertex shader
317  fileInfo = QFileInfo(_vertexShaderFile);
318  if (fileInfo.isRelative())
319  {
320  QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_vertexShaderFile);
321  fileInfo = QFileInfo(absFilename);
322 
323  newEntry.strVertexTemplate = absFilename;
324  newEntry.vertexFileLastMod = fileInfo.lastModified();
325  }
326  else
327  {
328  newEntry.strVertexTemplate = _vertexShaderFile;
329  newEntry.vertexFileLastMod = fileInfo.lastModified();
330  }
331 
332 
333  // geometry shader
334  if (_geometryShaderFile)
335  {
336  fileInfo = QFileInfo(_geometryShaderFile);
337  if (fileInfo.isRelative())
338  {
339  QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_geometryShaderFile);
340  fileInfo = QFileInfo(absFilename);
341 
342  newEntry.strGeometryTemplate = absFilename;
343  newEntry.geometryFileLastMod = fileInfo.lastModified();
344  }
345  else
346  {
347  newEntry.strGeometryTemplate = _geometryShaderFile;
348  newEntry.geometryFileLastMod = fileInfo.lastModified();
349  }
350  }
351 
352  // tess-ctrl shader
353  if (_tessControlShaderFile)
354  {
355  fileInfo = QFileInfo(_tessControlShaderFile);
356  if (fileInfo.isRelative())
357  {
358  QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_tessControlShaderFile);
359  fileInfo = QFileInfo(absFilename);
360 
361  newEntry.strTessControlTemplate = absFilename;
362  newEntry.tessControlFileLastMod = fileInfo.lastModified();
363  }
364  else
365  {
366  newEntry.strTessControlTemplate = _tessControlShaderFile;
367  newEntry.tessControlFileLastMod = fileInfo.lastModified();
368  }
369  }
370 
371  // tess-eval shader
372  if (_tessEvalShaderFile)
373  {
374  fileInfo = QFileInfo(_tessEvalShaderFile);
375  if (fileInfo.isRelative())
376  {
377  QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_tessEvalShaderFile);
378  fileInfo = QFileInfo(absFilename);
379 
380  newEntry.strTessEvaluationTemplate = absFilename;
381  newEntry.tessEvaluationFileLastMod = fileInfo.lastModified();
382  }
383  else
384  {
385  newEntry.strTessEvaluationTemplate = _tessEvalShaderFile;
386  newEntry.tessEvaluationFileLastMod = fileInfo.lastModified();
387  }
388  }
389 
390 
391 
392  if (_macros)
393  newEntry.macros = *_macros;
394 
395  CacheList::iterator oldCache = cacheStatic_.end();
396 
397  for (CacheList::iterator it = cacheStatic_.begin(); it != cacheStatic_.end(); ++it)
398  {
399  // If the shaders are equal, we return the cached entry
400  if (!compareShaderGenDescs(&it->first, &newEntry))
401  {
402  if ( timeCheck_ && !compareTimeStamp(&it->first, &newEntry))
403  oldCache = it;
404  else
405  return it->second;
406  }
407  }
408 
409 
410  // convert QStringList to GLSL::StringList
411 
412  GLSL::StringList glslMacros;
413 
414  if (_macros)
415  {
416  for (QStringList::const_iterator it = _macros->constBegin(); it != _macros->constEnd(); ++it)
417  glslMacros.push_back(it->toStdString());
418  }
419 
420 
421  GLSL::Program* prog = GLSL::loadProgram(_vertexShaderFile, _tessControlShaderFile, _tessEvalShaderFile, _geometryShaderFile, _fragmentShaderFile, &glslMacros, _verbose);
422  glCheckErrors();
423 
424  if (oldCache != cacheStatic_.end())
425  {
426  if (!prog || !prog->isLinked())
427  {
428  delete prog;
429  return oldCache->second;
430  }
431  else
432  {
433  cacheStatic_.erase(oldCache);
434  }
435  }
436 
437  cacheStatic_.push_back(std::pair<CacheEntry, GLSL::Program*>(newEntry, prog));
438 
439  return prog;
440 }
441 
442 GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile, const char* _fragmentShaderFile, QStringList* _macros, bool _verbose )
443 {
444  return getProgram(_vertexShaderFile, 0, 0, 0, _fragmentShaderFile, _macros, _verbose);
445 }
446 
447 GLSL::Program* ACG::ShaderCache::getComputeProgram(const char* _computeShaderFile, QStringList* _macros /* = 0 */, bool _verbose /* = true */)
448 {
449  CacheEntry newEntry;
450 
451  // store filenames and timestamps in new entry
452  // use vertex shader filename as compute shader
453  QFileInfo fileInfo(_computeShaderFile);
454  if (fileInfo.isRelative())
455  {
456  QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_computeShaderFile);
457  fileInfo = QFileInfo(absFilename);
458 
459  newEntry.strVertexTemplate = absFilename;
460  newEntry.vertexFileLastMod = fileInfo.lastModified();
461  }
462  else
463  {
464  newEntry.strVertexTemplate = _computeShaderFile;
465  newEntry.vertexFileLastMod = fileInfo.lastModified();
466  }
467 
468  if (_macros)
469  newEntry.macros = *_macros;
470 
471  CacheList::iterator oldCache = cacheComputeShaders_.end();
472 
473  for (CacheList::iterator it = cacheComputeShaders_.begin(); it != cacheComputeShaders_.end(); ++it)
474  {
475  // If the shaders are equal, we return the cached entry
476  if (!compareShaderGenDescs(&it->first, &newEntry))
477  {
478  if ( ( timeCheck_ && !compareTimeStamp(&it->first, &newEntry)) || !it->second || !it->second->isLinked())
479  oldCache = it;
480  else
481  return it->second;
482  }
483  }
484 
485 
486  // convert QStringList to GLSL::StringList
487 
488  GLSL::StringList glslMacros;
489 
490  if (_macros)
491  {
492  for (QStringList::const_iterator it = _macros->constBegin(); it != _macros->constEnd(); ++it)
493  glslMacros.push_back(it->toStdString());
494  }
495 
496 
497  GLSL::Program* prog = GLSL::loadComputeProgram(newEntry.strVertexTemplate.toUtf8(), &glslMacros, _verbose);
498  glCheckErrors();
499 
500  if (oldCache != cacheComputeShaders_.end())
501  {
502  if (!prog || !prog->isLinked())
503  {
504  delete prog;
505 
506 
507  // dump shader source including macros to debug output
508  if (!glslMacros.empty() && !dbgOutputDir_.isEmpty())
509  {
510  GLSL::StringList shaderSrc = GLSL::loadShader(newEntry.strVertexTemplate.toUtf8(), &glslMacros);
511 
512  // compute FNV hash of macros
513 
514  unsigned int fnv_prime = 16777619;
515  unsigned int fnv_hash = 2166136261;
516 
517  for (GLSL::StringList::iterator it = shaderSrc.begin(); it != shaderSrc.end(); ++it)
518  {
519  for (size_t i = 0; i < it->length(); ++i)
520  {
521  fnv_hash *= fnv_prime;
522  fnv_hash ^= static_cast<unsigned char>((*it)[i]);
523  }
524  }
525 
526  QString fnv_string;
527  fnv_string.sprintf("%X", fnv_hash);
528 
529  std::string dumpFilename = QString(dbgOutputDir_ + QDir::separator() + fileInfo.fileName() + fnv_string).toStdString();
530 
531  std::ofstream dumpStream(dumpFilename.c_str());
532  if (dumpStream.is_open())
533  {
534  for (GLSL::StringList::iterator it = shaderSrc.begin(); it != shaderSrc.end(); ++it)
535  dumpStream << it->c_str();
536  dumpStream.close();
537  }
538  }
539 
540  return oldCache->second;
541  }
542  else
543  {
544  cacheComputeShaders_.erase(oldCache);
545  }
546  }
547 
548  cacheComputeShaders_.push_back(std::pair<CacheEntry, GLSL::Program*>(newEntry, prog));
549 
550  return prog;
551 }
552 
554 {
555  if (_a->vertexFileLastMod != _b->vertexFileLastMod)
556  return false;
557 
558  if (_a->geometryFileLastMod != _b->geometryFileLastMod)
559  return false;
560 
561  if (_a->fragmentFileLastMod != _b->fragmentFileLastMod)
562  return false;
563 
564  if (_a->tessControlFileLastMod != _b->tessControlFileLastMod)
565  return false;
566 
567  if (_a->tessEvaluationFileLastMod != _b->tessEvaluationFileLastMod)
568  return false;
569  return true;
570 }
571 
573 {
574  if (_a->mods != _b->mods)
575  return -1;
576 
577  const ShaderGenDesc* a = &_a->desc;
578  const ShaderGenDesc* b = &_b->desc;
579 
580  if (_a->strFragmentTemplate != _b->strFragmentTemplate)
581  return -1;
582 
583  if (_a->strGeometryTemplate != _b->strGeometryTemplate)
584  return -1;
585 
586  if (_a->strVertexTemplate != _b->strVertexTemplate)
587  return -1;
588 
589  if (_a->strTessControlTemplate != _b->strTessControlTemplate)
590  return -1;
591 
592  if (_a->strTessEvaluationTemplate != _b->strTessEvaluationTemplate)
593  return -1;
594 
595  if (_a->macros != _b->macros)
596  return -1;
597 
598 
599  return *a == *b ? 0 : -1;
600 }
601 
602 
604 {
605  cache_.clear();
606  cacheStatic_.clear();
607  cacheComputeShaders_.clear();
608 }
609 
610 void ACG::ShaderCache::setDebugOutputDir(const char* _outputDir)
611 {
612  dbgOutputDir_ = _outputDir;
613 }
614 
615 //=============================================================================
616 } // namespace ACG
617 //=============================================================================
void attach(PtrConstShader _shader)
Attaches a shader object to the program object.
Definition: GLSLShader.cc:298
void setSource(const StringList &source)
Upload the source of the shader.
Definition: GLSLShader.cc:109
QString toString() const
convert ShaderGenDesc to string format for debugging
GLSL::Program * getComputeProgram(const char *_computeShaderFile, QStringList *_macros=0, bool _verbose=true)
Query a static compute shader program from cache.
Definition: ShaderCache.cc:447
int compareShaderGenDescs(const CacheEntry *_a, const CacheEntry *_b)
Returns true, if the shaders are not equal.
Definition: ShaderCache.cc:572
bool compile(bool verbose=true)
Compile the shader object.
Definition: GLSLShader.cc:152
bool isLinked()
Returns if the program object has been succesfully linked.
Definition: GLSLShader.cc:376
static ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:90
A generic shader base class.
Definition: GLSLShader.hh:77
virtual ~ShaderCache()
Destructor.
Definition: ShaderCache.cc:77
void link()
Links the shader objects to the program.
Definition: GLSLShader.cc:332
GLSL::PtrProgram loadComputeProgram(const char *computeShaderFile, const GLSL::StringList *macros, bool verbose)
Definition: GLSLShader.cc:1160
GLSL::PtrProgram loadProgram(const char *vertexShaderFile, const char *tessControlShaderFile, const char *tessEvaluationShaderFile, const char *geometryShaderFile, const char *fragmentShaderFile, const GLSL::StringList *macros, bool verbose)
Definition: GLSLShader.cc:1082
const QStringList & getFragmentShaderCode()
Returns generated fragment shader code.
GLSL vertex shader.
Definition: GLSLShader.hh:101
GLSL::StringList loadShader(const char *filename, const GLSL::StringList *macros, bool appendNewLineChar, GLSL::StringList *outIncludes)
Loads the shader source.
Definition: GLSLShader.cc:927
bool hasGeometryShader() const
check whether there is a geometry shader present
static QString getShaderDir()
GLSL geometry shader.
Definition: GLSLShader.hh:130
Cache for shaders.
Definition: ShaderCache.hh:79
bool compareTimeStamp(const CacheEntry *_a, const CacheEntry *_b)
Returns true, if the shaders have the timestamp.
Definition: ShaderCache.cc:553
const QStringList & getGeometryShaderCode()
Returns generated tessellation evaluation shader code.
const QStringList & getVertexShaderCode()
Returns generated vertex shader code.
bool hasTessControlShader() const
check whether there is a tess-control shader present
Namespace providing different geometric functions concerning angles.
Definition: DBSCANT.cc:51
const QStringList & getTessEvaluationShaderCode()
Returns generated tessellation control shader code.
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:108
void setDebugOutputDir(const char *_outputDir)
Enable debug output of generated shaders to specified directory.
Definition: ShaderCache.cc:610
const QStringList & getTessControlShaderCode()
Returns generated vertex shader code.
bool hasTessEvaluationShader() const
check whether there is a tess-evaluation shader present
void clearCache()
Delete all cached shaders.
Definition: ShaderCache.cc:603
GLSL fragment shader.
Definition: GLSLShader.hh:116
GLSL program class.
Definition: GLSLShader.hh:217
void glCheckErrors()
Definition: GLError.hh:105