Commit f81c0c87 authored by Christopher Tenter's avatar Christopher Tenter
Browse files

support for compute shaders

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@19641 383ad7c9-94d9-4d36-a494-682f7c89f535
parent eb9c2d6c
......@@ -62,8 +62,6 @@
namespace ACG
{
//#define SG_DEBUG_OUTPUT
ShaderCache::ShaderCache():
cache_(),
cacheStatic_(),
......@@ -79,6 +77,9 @@ ShaderCache::~ShaderCache()
for (CacheList::iterator it = cacheStatic_.begin(); it != cacheStatic_.end(); ++it)
delete it->second;
for (CacheList::iterator it = cacheComputeShaders_.begin(); it != cacheComputeShaders_.end(); ++it)
delete it->second;
}
ShaderCache* ACG::ShaderCache::getInstance()
......@@ -146,12 +147,13 @@ GLSL::Program* ACG::ShaderCache::getProgram( const ShaderGenDesc* _desc, unsigne
// glsl program not in cache, generate shaders
ShaderProgGenerator progGen(_desc, _usage);
#ifdef SG_DEBUG_OUTPUT
if (!dbgOutputDir_.isEmpty())
{
static int counter = 0;
char fileName[0x100];
sprintf(fileName, "../../../shader_%02d.glsl", counter++);
QString fileName;
fileName.sprintf("shader_%02d.glsl", counter++);
fileName = dbgOutputDir_ + QDir::separator() + fileName;
QFile fileOut(fileName);
if (fileOut.open(QFile::WriteOnly | QFile::Truncate))
......@@ -200,9 +202,6 @@ GLSL::Program* ACG::ShaderCache::getProgram( const ShaderGenDesc* _desc, unsigne
fileOut.close();
}
}
#endif
// TODO: Build geometry shader only if supported!
GLSL::FragmentShader* fragShader = new GLSL::FragmentShader();
GLSL::VertexShader* vertShader = new GLSL::VertexShader();
......@@ -271,7 +270,12 @@ GLSL::Program* ACG::ShaderCache::getProgram( const ShaderGenDesc* _desc, unsigne
return prog;
}
GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile, const char* _fragmentShaderFile, QStringList* _macros, bool _verbose )
GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile,
const char* _tessControlShaderFile,
const char* _tessEvalShaderFile,
const char* _geometryShaderFile,
const char* _fragmentShaderFile,
QStringList* _macros, bool _verbose )
{
CacheEntry newEntry;
newEntry.usage = 0;
......@@ -294,7 +298,7 @@ GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile, cons
newEntry.strFragmentTemplate = _fragmentShaderFile;
newEntry.fragmentFileLastMod = fileInfo.lastModified();
}
// vertex shader
fileInfo = QFileInfo(_vertexShaderFile);
if (fileInfo.isRelative())
......@@ -311,6 +315,66 @@ GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile, cons
newEntry.vertexFileLastMod = fileInfo.lastModified();
}
// geometry shader
if (_geometryShaderFile)
{
fileInfo = QFileInfo(_geometryShaderFile);
if (fileInfo.isRelative())
{
QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_geometryShaderFile);
fileInfo = QFileInfo(absFilename);
newEntry.strGeometryTemplate = absFilename;
newEntry.geometryFileLastMod = fileInfo.lastModified();
}
else
{
newEntry.strGeometryTemplate = _geometryShaderFile;
newEntry.geometryFileLastMod = fileInfo.lastModified();
}
}
// tess-ctrl shader
if (_tessControlShaderFile)
{
fileInfo = QFileInfo(_tessControlShaderFile);
if (fileInfo.isRelative())
{
QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_tessControlShaderFile);
fileInfo = QFileInfo(absFilename);
newEntry.strTessControlTemplate = absFilename;
newEntry.tessControlFileLastMod = fileInfo.lastModified();
}
else
{
newEntry.strTessControlTemplate = _tessControlShaderFile;
newEntry.tessControlFileLastMod = fileInfo.lastModified();
}
}
// tess-eval shader
if (_tessEvalShaderFile)
{
fileInfo = QFileInfo(_tessEvalShaderFile);
if (fileInfo.isRelative())
{
QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_tessEvalShaderFile);
fileInfo = QFileInfo(absFilename);
newEntry.strTessEvaluationTemplate = absFilename;
newEntry.tessEvaluationFileLastMod = fileInfo.lastModified();
}
else
{
newEntry.strTessEvaluationTemplate = _tessEvalShaderFile;
newEntry.tessEvaluationFileLastMod = fileInfo.lastModified();
}
}
if (_macros)
newEntry.macros = *_macros;
......@@ -340,7 +404,7 @@ GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile, cons
}
GLSL::Program* prog = GLSL::loadProgram(_vertexShaderFile, _fragmentShaderFile, &glslMacros, _verbose);
GLSL::Program* prog = GLSL::loadProgram(_vertexShaderFile, _tessControlShaderFile, _tessEvalShaderFile, _geometryShaderFile, _fragmentShaderFile, &glslMacros, _verbose);
glCheckErrors();
if (oldCache != cacheStatic_.end())
......@@ -361,6 +425,83 @@ GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile, cons
return prog;
}
GLSL::Program* ACG::ShaderCache::getProgram( const char* _vertexShaderFile, const char* _fragmentShaderFile, QStringList* _macros, bool _verbose )
{
return getProgram(_vertexShaderFile, 0, 0, 0, _fragmentShaderFile, _macros, _verbose);
}
GLSL::Program* ACG::ShaderCache::getComputeProgram(const char* _computeShaderFile, QStringList* _macros /* = 0 */, bool _verbose /* = true */)
{
CacheEntry newEntry;
newEntry.usage = 0;
// store filenames and timestamps in new entry
// use vertex shader filename as compute shader
QFileInfo fileInfo(_computeShaderFile);
if (fileInfo.isRelative())
{
QString absFilename = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(_computeShaderFile);
fileInfo = QFileInfo(absFilename);
newEntry.strVertexTemplate = absFilename;
newEntry.vertexFileLastMod = fileInfo.lastModified();
}
else
{
newEntry.strVertexTemplate = _computeShaderFile;
newEntry.vertexFileLastMod = fileInfo.lastModified();
}
if (_macros)
newEntry.macros = *_macros;
CacheList::iterator oldCache = cacheComputeShaders_.end();
for (CacheList::iterator it = cacheComputeShaders_.begin(); it != cacheComputeShaders_.end(); ++it)
{
// If the shaders are equal, we return the cached entry
if (!compareShaderGenDescs(&it->first, &newEntry))
{
if ( timeCheck_ && !compareTimeStamp(&it->first, &newEntry))
oldCache = it;
else
return it->second;
}
}
// convert QStringList to GLSL::StringList
GLSL::StringList glslMacros;
if (_macros)
{
for (QStringList::const_iterator it = _macros->constBegin(); it != _macros->constEnd(); ++it)
glslMacros.push_back(it->toStdString());
}
GLSL::Program* prog = GLSL::loadComputeProgram(_computeShaderFile, &glslMacros, _verbose);
glCheckErrors();
if (oldCache != cacheComputeShaders_.end())
{
if (!prog || !prog->isLinked())
{
delete prog;
return oldCache->second;
}
else
{
cacheComputeShaders_.erase(oldCache);
}
}
cacheComputeShaders_.push_back(std::pair<CacheEntry, GLSL::Program*>(newEntry, prog));
return prog;
}
bool ACG::ShaderCache::compareTimeStamp(const CacheEntry* _a, const CacheEntry* _b)
{
if (_a->vertexFileLastMod != _b->vertexFileLastMod)
......@@ -430,8 +571,13 @@ void ACG::ShaderCache::clearCache()
{
cache_.clear();
cacheStatic_.clear();
cacheComputeShaders_.clear();
}
void ACG::ShaderCache::setDebugOutputDir(const char* _outputDir)
{
dbgOutputDir_ = _outputDir;
}
//=============================================================================
} // namespace ACG
......
......@@ -97,13 +97,49 @@ public:
* However, the program should be queried every time before using the program as it may have been deleted and recompiled in the meantime.
*
* @param _vertexShaderFile relative (from shader directory) or absolute filename of vertex shader
* @param _fragmentShaderFile relative (from shader directory) or absolute filename of vertex shader
* @param _tessControlShaderFile relative (from shader directory) or absolute filename of tessellation control shader, null accepted
* @param _tessEvalShaderFile relative (from shader directory) or absolute filename of tessellation eval shader, null accepted
* @param _geometryShaderFile relative (from shader directory) or absolute filename of geometry shader, null accepted
* @param _fragmentShaderFile relative (from shader directory) or absolute filename of fragment shader
* @param _macros optional list of glsl preprocessor macros, which are added directly after the #version directive (example: "#define USE_METHOD 1", "#define METHOD_PARAM 0"...)
* @param _verbose log or suppress error output
* @return The program (Either from cache or newly compiled and linked)
*/
GLSL::Program* getProgram(const char* _vertexShaderFile,
const char* _tessControlShaderFile,
const char* _tessEvalShaderFile,
const char* _geometryShaderFile,
const char* _fragmentShaderFile,
QStringList* _macros = 0, bool _verbose = true);
/** \brief Query a static shader program from cache
*
* Can be used to load a shader and have external changes automatically applied by the timestamp watchdog.
* However, the program should be queried every time before using the program as it may have been deleted and recompiled in the meantime.
*
* @param _vertexShaderFile relative (from shader directory) or absolute filename of vertex shader
* @param _fragmentShaderFile relative (from shader directory) or absolute filename of fragment shader
* @param _macros optional list of glsl preprocessor macros, which are added directly after the #version directive (example: "#define USE_METHOD 1", "#define METHOD_PARAM 0"...)
* @param _verbose log or suppress error output
* @return The program (Either from cache or newly compiled and linked)
*/
GLSL::Program* getProgram(const char* _vertexShaderFile, const char* _fragmentShaderFile, QStringList* _macros = 0, bool _verbose = true);
/** \brief Query a static compute shader program from cache
*
* Can be used to load a shader and have external changes automatically applied by the timestamp watchdog.
* However, the program should be queried every time before using the program as it may have been deleted and recompiled in the meantime.
*
* @param _computeShaderFile relative (from shader directory) or absolute filename of vertex shader
* @param _macros optional list of glsl preprocessor macros, which are added directly after the #version directive (example: "#define USE_METHOD 1", "#define METHOD_PARAM 0"...)
* @param _verbose log or suppress error output
* @return The program (Either from cache or newly compiled and linked)
*/
GLSL::Program* getComputeProgram(const char* _computeShaderFile, QStringList* _macros = 0, bool _verbose = true);
/** \brief Delete all cached shaders
*/
void clearCache();
......@@ -114,6 +150,11 @@ public:
void setTimeCheck(bool _on){timeCheck_ = _on;}
bool getTimeCheck(){return timeCheck_;}
/** \brief Enable debug output of generated shaders to specified directory
*/
void setDebugOutputDir(const char* _outputDir);
protected:
ShaderCache();
......@@ -153,7 +194,14 @@ protected:
/// cache containing static shaders loaded from files (separate from dynamic cache to reduce access time)
CacheList cacheStatic_;
/// cache for static compute shaders
CacheList cacheComputeShaders_;
bool timeCheck_;
/// output directory for shaders in dynamic cache
QString dbgOutputDir_;
};
......
......@@ -214,6 +214,19 @@ namespace GLSL {
#endif // GL_ARB_tessellation_shader
//--------------------------------------------------------------------------
// Compute shader
//--------------------------------------------------------------------------
ComputeShader::ComputeShader() : Shader(
#ifdef GL_ARB_compute_shader
GL_COMPUTE_SHADER
#else
0
#endif
) {}
ComputeShader::~ComputeShader() {}
//--------------------------------------------------------------------------
// Shader program object
//--------------------------------------------------------------------------
......@@ -860,6 +873,26 @@ namespace GLSL {
}
/** \brief Loads, compiles and installs a new compute shader.
*/
GLSL::PtrComputeShader loadComputeShader(const char *name, const GLSL::StringList *macros, bool verbose) {
GLSL::PtrComputeShader shader = 0;
#ifdef GL_ARB_compute_shader
StringList src = loadShader(name, macros);
if (!src.empty()) {
shader = new GLSL::ComputeShader();
shader->setSource(src);
if (!shader->compile(verbose)) {
delete shader;
shader = 0;
}
}
#endif // GL_ARB_compute_shader
return shader;
}
GLSL::PtrProgram loadProgram(const char *vertexShaderFile,
const char *tessControlShaderFile,
......@@ -876,28 +909,37 @@ namespace GLSL {
GLSL::Shader* tempShaders[numShaders] = {0};
for (int i = 0; i < numShaders; ++i) {
QString shaderFile = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(ShaderFiles[i]);
if (i == 0) // vertex shader
tempShaders[i] = GLSL::loadVertexShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 1 && tessControlShaderFile) // tesscontrol shader
tempShaders[i] = GLSL::loadTessControlShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 2 && tessEvaluationShaderFile) // tesseval shader
tempShaders[i] = GLSL::loadTessEvaluationShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 3 && geometryShaderFile) // geometry shader
tempShaders[i] = GLSL::loadGeometryShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 4) // fragment shader
tempShaders[i] = GLSL::loadFragmentShader(shaderFile.toUtf8(), macros, verbose);
if (!tempShaders[i] && ShaderFiles[i]) {
if (verbose)
std::cerr << ShaderFiles[i] << " could not be loaded and compiled" << std::endl;
// avoid memleak
for (int k = 0; k < numShaders; ++k)
delete tempShaders[k];
return 0;
QString inputFile = ShaderFiles[i];
if (ShaderFiles[i] && !inputFile.isEmpty()) {
QDir inputFileDir = inputFile;
// eventually add shader dir to relative filepath
QString shaderFile = inputFile;
if (inputFileDir.isRelative())
shaderFile = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + inputFile;
if (i == 0) // vertex shader
tempShaders[i] = GLSL::loadVertexShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 1 && tessControlShaderFile) // tesscontrol shader
tempShaders[i] = GLSL::loadTessControlShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 2 && tessEvaluationShaderFile) // tesseval shader
tempShaders[i] = GLSL::loadTessEvaluationShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 3 && geometryShaderFile) // geometry shader
tempShaders[i] = GLSL::loadGeometryShader(shaderFile.toUtf8(), macros, verbose);
else if (i == 4) // fragment shader
tempShaders[i] = GLSL::loadFragmentShader(shaderFile.toUtf8(), macros, verbose);
if (!tempShaders[i] && ShaderFiles[i]) {
if (verbose)
std::cerr << ShaderFiles[i] << " could not be loaded and compiled" << std::endl;
// avoid memleak
for (int k = 0; k < numShaders; ++k)
delete tempShaders[k];
return 0;
}
}
}
......@@ -928,6 +970,48 @@ namespace GLSL {
GLSL::PtrProgram loadProgram(const char *vertexShaderFile, const char *fragmentShaderFile, const GLSL::StringList *macros, bool verbose){
return loadProgram(vertexShaderFile, 0, fragmentShaderFile, macros, verbose);
}
GLSL::PtrProgram loadComputeProgram(const char *computeShaderFile, const GLSL::StringList *macros /* = 0 */, bool verbose /* = true */) {
GLSL::PtrProgram result = 0;
QString inputFile = computeShaderFile;
if (computeShaderFile && !inputFile.isEmpty()) {
QDir inputFileDir = inputFile;
// eventually add shader dir to relative filepath
QString shaderFile = inputFile;
if (inputFileDir.isRelative())
shaderFile = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + inputFile;
GLSL::PtrShader sh = GLSL::loadComputeShader(computeShaderFile, macros, verbose);
if (!sh) {
if (verbose)
std::cerr << computeShaderFile << " could not be loaded and compiled" << std::endl;
// avoid memleak
delete sh;
return 0;
}
// create program
result = new GLSL::Program();
result->attach(sh);
result->link();
delete sh;
if (verbose)
ACG::glCheckErrors();
}
return result;
}
}
......
......@@ -164,6 +164,21 @@ namespace GLSL {
//--------------------------------------------------------------------------
/** \brief GLSL compute shader.
*/
class ACGDLLEXPORT ComputeShader : public Shader {
public:
ComputeShader();
virtual ~ComputeShader();
};
typedef ComputeShader* PtrComputeShader;
typedef const ComputeShader* PtrConstComputeShader;
//--------------------------------------------------------------------------
/** \brief GLSL program class.
*
* A GLSL program links together the vertex and fragment shaders.
......@@ -286,6 +301,7 @@ namespace GLSL {
GLSL::PtrGeometryShader ACGDLLEXPORT loadGeometryShader(const char *name, const GLSL::StringList *macros = 0, bool verbose = true);
GLSL::PtrShader ACGDLLEXPORT loadTessControlShader(const char *name, const GLSL::StringList *macros = 0, bool verbose = true);
GLSL::PtrShader ACGDLLEXPORT loadTessEvaluationShader(const char *name, const GLSL::StringList *macros = 0, bool verbose = true);
GLSL::PtrComputeShader ACGDLLEXPORT loadComputeShader(const char *name, const GLSL::StringList *macros = 0, bool verbose = true);
/** load shaders and create GLSL program if successful
*
......@@ -321,6 +337,14 @@ namespace GLSL {
const GLSL::StringList *macros = 0,
bool verbose = true);
/** load glsl compute shader and create GLSL program if successful
*
* Shader file paths for this function are assumed to be relative
* to the "Shader" directory as specified in ShaderProgGenerator::getShaderDir()
*/
GLSL::PtrProgram ACGDLLEXPORT loadComputeProgram(const char *computeShaderFile,
const GLSL::StringList *macros = 0,
bool verbose = true);
}
#endif // GLSLSHADER_H
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment