From c5a331010d813e56f332fabd2bef108564f6df84 Mon Sep 17 00:00:00 2001
From: Robert Menzel <menzel@informatik.rwth-aachen.de>
Date: Sun, 26 Aug 2012 18:59:01 +0200
Subject: [PATCH] refactored shader type detection, added compute shader
 detection

---
 include/ACGL/Base/FileHelpers.hh              |  6 ++
 .../Controller/ShaderProgramControlFiles.hh   |  8 ---
 include/ACGL/OpenGL/Tools.hh                  | 32 +++++++++++
 src/ACGL/Base/FileHelpers.cc                  | 14 +++++
 .../OpenGL/Controller/ShaderControlFile.cc    | 32 ++---------
 .../Controller/ShaderProgramControlFiles.cc   | 38 +------------
 src/ACGL/OpenGL/Tools.cc                      | 57 ++++++++++++++++++-
 7 files changed, 116 insertions(+), 71 deletions(-)

diff --git a/include/ACGL/Base/FileHelpers.hh b/include/ACGL/Base/FileHelpers.hh
index 91d04c03..0ad994c8 100644
--- a/include/ACGL/Base/FileHelpers.hh
+++ b/include/ACGL/Base/FileHelpers.hh
@@ -41,6 +41,12 @@ namespace FileHelpers
      */
     bool stringEndsWith( const std::string &theString, const std::string &theEnding );
 
+    /*
+     * Returns the lowercase fileending if the filename has a '.' in it, otherwise
+     * an empty string.
+     */
+    std::string getFileEnding( const std::string &_fileName );
+
     /*
      * Checks if a string begins with a certain prefix.
      */
diff --git a/include/ACGL/OpenGL/Controller/ShaderProgramControlFiles.hh b/include/ACGL/OpenGL/Controller/ShaderProgramControlFiles.hh
index eb687e75..38cfec3a 100644
--- a/include/ACGL/OpenGL/Controller/ShaderProgramControlFiles.hh
+++ b/include/ACGL/OpenGL/Controller/ShaderProgramControlFiles.hh
@@ -31,14 +31,6 @@ namespace OpenGL{
 
 class ShaderProgramControlFiles : public Resource::MultiFileController<ShaderProgram>
 {
-    struct ShaderEndings
-    {
-        const char *ending;
-        GLenum      type;
-    };
-    static const unsigned int  sShaderEndingsSize; // size of the array sShaderEndings
-    static const ShaderEndings sShaderEndings[];   // all supported endings (see .cc file)
-
     // ========================================================================================================= \/
     // ============================================================================================ CONSTRUCTORS \/
     // ========================================================================================================= \/
diff --git a/include/ACGL/OpenGL/Tools.hh b/include/ACGL/OpenGL/Tools.hh
index 9de2a540..aef3e0b5 100644
--- a/include/ACGL/OpenGL/Tools.hh
+++ b/include/ACGL/OpenGL/Tools.hh
@@ -46,8 +46,40 @@ uint32_t getOpenGLMajorVersionNumber();
 // returns the combined version number as 10*major + minor for easy comparing
 uint32_t getOpenGLVersionNumber();
 
+// query support for specific shader stages:
 bool doesSupportGeometryShader();
 bool doesSupportTessellationShader();
+bool doesSupportComputeShader();
+
+
+struct ShaderEndings
+{
+    const char *ending;
+    GLenum      type;
+};
+const ShaderEndings sShaderEndings[  ] = {
+    {"vsh",  GL_VERTEX_SHADER},
+    {"vert", GL_VERTEX_SHADER},
+    {"tcsh", GL_TESS_CONTROL_SHADER},
+    {"tcs",  GL_TESS_CONTROL_SHADER},
+    {"tesh", GL_TESS_EVALUATION_SHADER},
+    {"tes",  GL_TESS_EVALUATION_SHADER},
+    {"gsh",  GL_GEOMETRY_SHADER},
+    {"geo",  GL_GEOMETRY_SHADER},
+    {"fsh",  GL_FRAGMENT_SHADER},
+    {"frag", GL_FRAGMENT_SHADER},
+    {"csh",  GL_COMPUTE_SHADER},
+    {"cs",   GL_COMPUTE_SHADER}
+};
+
+// returns the GL_TESS_CONTROL_SHADER GL_TESS_EVALUATION_SHADER GL_GEOMETRY_SHADER GL_COMPUTE_SHADER
+// GL_VERTEX_SHADER GL_FRAGMENT_SHADER or GL_INVALID_VALUE in case the shadertype was not detected
+// if _ignoreUnsupportedShaderTypes is true, types unsupported by the current runtime will return
+// an GL_INVALID_ENUM also.
+GLenum getShaderTypeByFileEnding( const std::string _fileName, bool _ignoreUnsupportedShaderTypes = true );
+
+// looks up the enum and gives a human readable version
+const GLubyte* acglShaderTypeString( GLenum _shaderType );
 
 // for every OpenGL error enum this will return a human readable version of it
 // similar to gluErrorString, but that function is not available on all plattforms
diff --git a/src/ACGL/Base/FileHelpers.cc b/src/ACGL/Base/FileHelpers.cc
index 7abe3903..4d153a5f 100644
--- a/src/ACGL/Base/FileHelpers.cc
+++ b/src/ACGL/Base/FileHelpers.cc
@@ -8,6 +8,8 @@
 
 #include <cstdio> // fopen etc.
 #include <string>
+#include <algorithm> // transform
+#include <cctype>    // tolower
 #include <vector>
 #include <sstream>
 #include <iostream>
@@ -59,6 +61,18 @@ namespace FileHelpers
         return false;
     }
 
+    std::string getFileEnding( const std::string &_fileName )
+    {
+        size_t lastDot = _fileName.rfind('.');
+        if (lastDot == std::string::npos) {
+            return "";
+        }
+        std::string ending = _fileName.substr( lastDot+1, _fileName.size() );
+        std::transform(ending.begin(), ending.end(), ending.begin(), ::tolower);
+
+        return ending;
+    }
+
     /*
     bool stringBeginsWith( const std::string &theString, const std::string &thePrefix )
     {
diff --git a/src/ACGL/OpenGL/Controller/ShaderControlFile.cc b/src/ACGL/OpenGL/Controller/ShaderControlFile.cc
index 625fcf3f..863d187f 100644
--- a/src/ACGL/OpenGL/Controller/ShaderControlFile.cc
+++ b/src/ACGL/OpenGL/Controller/ShaderControlFile.cc
@@ -6,6 +6,7 @@
 
 #include <ACGL/OpenGL/Controller/ShaderControlFile.hh>
 #include <ACGL/Base/StringOperations.hh>
+#include <ACGL/OpenGL/Tools.hh>
 
 using namespace ACGL::Base;
 using namespace ACGL::OpenGL;
@@ -14,37 +15,14 @@ SharedShader ShaderControlFile::create(void)
 {
     updateFileModificationTime();
 
-    if(mType == GL_INVALID_ENUM)
+    if (mType == GL_INVALID_ENUM)
     {
-        std::string filename;
-        std::string extension;
-        if(!StringOperations::splitFileExtension(mFilename, filename, extension))
-            return SharedShader();
-
-        if(extension == "vsh")
-            mType = GL_VERTEX_SHADER;
-        else if(extension == "fsh")
-            mType = GL_FRAGMENT_SHADER;
-#if (ACGL_OPENGL_VERSION >= 43)
-        else if(extension == "csh")
-            mType = GL_COMPUTE_SHADER;
-#endif
-#if (ACGL_OPENGL_VERSION >= 40)
-        else if(extension == "tcsh")
-            mType = GL_TESS_CONTROL_SHADER;
-        else if(extension == "tesh")
-            mType = GL_TESS_EVALUATION_SHADER;
-#endif
-#if (ACGL_OPENGL_VERSION >= 32)
-        else if(extension == "gsh")
-            mType = GL_GEOMETRY_SHADER;
-#endif
-        else
-            return SharedShader();
+        mType = ACGL::OpenGL::getShaderTypeByFileEnding( mFilename );
+        if (mType == GL_INVALID_ENUM) return SharedShader();
     }
 
     SharedShader shader(new Shader(mType));
-    if(shader->setFromFile(mFullFilePath))
+    if (shader->setFromFile(mFullFilePath))
         return shader;
     return SharedShader();
 }
diff --git a/src/ACGL/OpenGL/Controller/ShaderProgramControlFiles.cc b/src/ACGL/OpenGL/Controller/ShaderProgramControlFiles.cc
index 624c3e41..87ae6e07 100644
--- a/src/ACGL/OpenGL/Controller/ShaderProgramControlFiles.cc
+++ b/src/ACGL/OpenGL/Controller/ShaderProgramControlFiles.cc
@@ -6,6 +6,7 @@
 
 #include <ACGL/OpenGL/Controller/ShaderProgramControlFiles.hh>
 #include <ACGL/OpenGL/Controller/ShaderControlFile.hh>
+#include <ACGL/OpenGL/Tools.hh>
 #include <ACGL/Resource/FileManager.hh>
 #include <ACGL/OpenGL/Managers.hh>
 #include <ACGL/Base/Settings.hh>
@@ -17,37 +18,11 @@
 using namespace ACGL::Base;
 using namespace ACGL::OpenGL;
 
-#ifndef ACGL_OPENGL_ES
-#if (ACGL_OPENGL_VERSION >= 43)
-const unsigned int ShaderProgramControlFiles::sShaderEndingsSize = 9;
-#else
-const unsigned int ShaderProgramControlFiles::sShaderEndingsSize = 8;
-#endif
-#else
-const unsigned int ShaderProgramControlFiles::sShaderEndingsSize = 4;
-#endif
-
-const ShaderProgramControlFiles::ShaderEndings ShaderProgramControlFiles::sShaderEndings[ ShaderProgramControlFiles::sShaderEndingsSize ] = {
-#ifndef ACGL_OPENGL_ES
-    {"tcsh", GL_TESS_CONTROL_SHADER},
-    {"tesh", GL_TESS_EVALUATION_SHADER},
-    {"gsh",  GL_GEOMETRY_SHADER},
-    {"geo",  GL_GEOMETRY_SHADER},
-#if (ACGL_OPENGL_VERSION >= 43)
-    {"csh",  GL_COMPUTE_SHADER},
-#endif
-#endif
-    {"vsh",  GL_VERTEX_SHADER},
-    {"vert", GL_VERTEX_SHADER},
-    {"fsh",  GL_FRAGMENT_SHADER},
-    {"frag", GL_FRAGMENT_SHADER}
-};
-
 ShaderProgramControlFiles& ShaderProgramControlFiles::autoFiles(const std::string &_fileName)
 {
     //Utils::debug() << "autoFiles: " << _fileName << std::endl;
     std::string baseFileName = _fileName + ".";
-    for (unsigned int ending = 0; ending < sShaderEndingsSize; ++ending)
+    for (unsigned int ending = 0; ending < sizeof(sShaderEndings) / sizeof(ShaderEndings); ++ending)
     {
         std::string fileName = baseFileName + sShaderEndings[ending].ending;
         if ( FileHelpers::fileExists( mBasePath + fileName ) )
@@ -71,14 +46,7 @@ SharedShaderProgram ShaderProgramControlFiles::create(void)
 
         if ( mShaderType[file] == GL_INVALID_VALUE ) {
             // guess the shader type:
-            for (unsigned int ending = 0; ending < sShaderEndingsSize; ++ending)
-            {
-                if ( FileHelpers::stringEndsWith( mFileNames[file], sShaderEndings[ending].ending ) )
-                {
-                    mShaderType[file] = sShaderEndings[ending].type;
-                    continue;
-                }
-            }
+            mShaderType[file] = ACGL::OpenGL::getShaderTypeByFileEnding( mFileNames[file] );
         }
     }
 
diff --git a/src/ACGL/OpenGL/Tools.cc b/src/ACGL/OpenGL/Tools.cc
index d7b66b83..bdc82b4f 100644
--- a/src/ACGL/OpenGL/Tools.cc
+++ b/src/ACGL/OpenGL/Tools.cc
@@ -6,6 +6,7 @@
 
 #include <ACGL/ACGL.hh>
 #include <ACGL/OpenGL/Tools.hh>
+#include <ACGL/Base/FileHelpers.hh>
 
 namespace ACGL{
 namespace OpenGL{
@@ -14,7 +15,7 @@ namespace OpenGL{
 // This is a "private" function that should not be called from outside of this file.
 //
 // glGetIntegerv(GL_MAJOR_VERSION... and glGetIntegerv(GL_MINOR_VERSION... are great, but
-// require OpenGL 3.0 and are not supported on ES :-( so the VERSION string has to get parsed...
+// require OpenGL 3.0 and are not supported on ES 2 :-( so the VERSION string has to get parsed...
 //
 // OpenGL spec:
 // The VERSION ... strings are laid out as follows:
@@ -104,6 +105,60 @@ bool doesSupportTessellationShader()
 #endif
 }
 
+bool doesSupportComputeShader()
+{
+#ifdef ACGL_OPENGL_ES
+    return false;
+#else
+#   if defined(ACGL_USE_GLEW)
+        return ( GLEW_ARB_compute_shader || (getOpenGLVersionNumber() >= 43));
+#   else
+        return (getOpenGLVersionNumber() >= 43);
+#   endif
+#endif
+}
+
+
+GLenum getShaderTypeByFileEnding( const std::string _fileName, bool _ignoreUnsupportedShaderTypes )
+{
+    std::string fileEnding = ACGL::Base::FileHelpers::getFileEnding( _fileName );
+    if( fileEnding.size() == 0 ) return GL_INVALID_ENUM;
+
+    GLenum foundType = GL_INVALID_ENUM;
+
+    // guess the shader type:
+    for (unsigned int ending = 0; ending < sizeof(sShaderEndings) / sizeof(ShaderEndings); ++ending)
+    {
+        if ( fileEnding == sShaderEndings[ending].ending )
+        {
+            foundType = sShaderEndings[ending].type;
+            break;
+        }
+    }
+
+    if (_ignoreUnsupportedShaderTypes) {
+        if (foundType == GL_GEOMETRY_SHADER        && !doesSupportGeometryShader()    ) return GL_INVALID_ENUM;
+        if (foundType == GL_TESS_CONTROL_SHADER    && !doesSupportTessellationShader()) return GL_INVALID_ENUM;
+        if (foundType == GL_TESS_EVALUATION_SHADER && !doesSupportTessellationShader()) return GL_INVALID_ENUM;
+        if (foundType == GL_COMPUTE_SHADER         && !doesSupportComputeShader()     ) return GL_INVALID_ENUM;
+    }
+
+    return foundType;
+}
+
+const GLubyte* acglShaderTypeString( GLenum _shaderType )
+{
+    if      (_shaderType == GL_VERTEX_SHADER)          { return (GLubyte*) "vertex shader"; }
+    else if (_shaderType == GL_TESS_CONTROL_SHADER)    { return (GLubyte*) "tessellation control shader"; }
+    else if (_shaderType == GL_TESS_EVALUATION_SHADER) { return (GLubyte*) "tessellation evaluation shader"; }
+    else if (_shaderType == GL_GEOMETRY_SHADER)        { return (GLubyte*) "geometry shader"; }
+    else if (_shaderType == GL_FRAGMENT_SHADER)        { return (GLubyte*) "fragment shader"; }
+    else if (_shaderType == GL_COMPUTE_SHADER)         { return (GLubyte*) "compute shader"; }
+    else {
+        return (GLubyte*) "unknown shader type";
+    }
+}
+
 const GLubyte* acglErrorString( GLenum _errorCode )
 {
     // no gluErrorString on iOS, problems on visual studio...
-- 
GitLab