12#ifndef GAMP_GLSLSHADERCODE_HPP_
13#define GAMP_GLSLSHADERCODE_HPP_
27 using namespace gamp::render::gl;
112 struct Private{
explicit Private() =
default; };
118 : m_shaderBinaryFormat(0), m_shaderType(0), m_id(0), m_compiled(
false)
120 if(sources.size() != count) {
121 ERR_PRINT(
"shader number (%zu) and sourceFiles array (%zu) of different length.", count, sources.size());
125 ERR_PRINT(
"Invalid shader type: %u", type);
128 m_shaderSource = sources;
131 m_shader.reserve(count);
132 m_shader.resize(count, 0);
143 ShaderCodeRef res = std::make_shared<ShaderCode>(Private(), type, count, sources);
144 if( res->isValid() ) {
151 ShaderCode(Private, GLenum type,
size_t count, GLenum binFormat,
const bytes_t& binary) noexcept
152 : m_shaderBinaryFormat(0), m_shaderType(0), m_id(0), m_compiled(
false)
155 ERR_PRINT(
"Invalid shader type: %u", type);
159 m_shaderBinaryFormat = binFormat;
160 m_shaderBinary = binary;
162 m_shader.reserve(count);
163 m_shader.resize(count, 0);
174 ShaderCodeRef res = std::make_shared<ShaderCode>(Private(), type, count, binFormat, binary);
175 if( res->isValid() ) {
197 ERR_PRINT(
"No shader compiler available for %s",
gl.toString().c_str());
201 ERR_PRINT(
"Invalid shader type: %u", type);
205 one_string.reserve(1);
206 one_string.resize(1,
"");
207 const size_t sourceFileCount = sourceFiles.size();
210 if(sourceFileCount > 0) {
212 shaderSources.reserve(sourceFileCount);
213 shaderSources.resize(sourceFileCount, one_string);
214 for(
size_t i=0; i<sourceFileCount && ok; ++i) {
215 ok = readShaderSource(sourceFiles[i], shaderSources[i][0]);
220 if( res->isValid() ) {
245 const std::vector<Uri>& sourceLocations,
bool mutableStringBuilder)
noexcept {
250 CharSequence[][] shaderSources = null;
251 if(null!=sourceLocations) {
253 shaderSources =
new CharSequence[sourceLocations.length][1];
254 for(
int i=0; i<sourceLocations.length; i++) {
256 shaderSources[i][0] = readShaderSource(sourceLocations[i], mutableStringBuilder);
257 }
catch (
final IOException ioe) {
258 throw RuntimeException(
"readShaderSource("+sourceLocations[i]+
") error: ", ioe);
260 if(null == shaderSources[i][0]) {
261 shaderSources = null;
265 if(null==shaderSources) {
268 return new ShaderCode(type, count, shaderSources);
286 public static ShaderCode create(
final int type,
final int count,
final Class<?> context,
int binFormat,
final String binaryFile)
noexcept {
288 if(null!=binaryFile && 0<=binFormat) {
291 }
catch (
final IOException ioe) {
292 throw RuntimeException(
"readShaderBinary("+binaryFile+
") error: ", ioe);
335 case GL_VERTEX_SHADER:
337 case GL_FRAGMENT_SHADER:
339 case GL_GEOMETRY_SHADER:
341 case GL_TESS_CONTROL_SHADER:
343 case GL_TESS_EVALUATION_SHADER:
345 case GL_COMPUTE_SHADER:
459 srcPaths.resize(srcBasenames.size());
461 if( !srcRoot.empty() ) {
462 for(
size_t i=0; i<srcPaths.size(); ++i) {
463 srcPaths[i] =
string_t(srcRoot).append(
"/").append(srcBasenames[i]).append(
".").append(srcSuffix);
466 for(
size_t i=0; i<srcPaths.size(); ++i) {
467 srcPaths[i] =
string_t(srcBasenames[i]).append(
".").append(srcSuffix);
470 res =
create(
gl, type, count, srcPaths);
471 if(res && res->isValid()) {
475 if( !srcPathsString.empty() ) {
476 srcPathsString.append(
";");
478 srcPathsString.append(s);
481 if( !binBasename.empty() ) {
484 for(GLenum bFmt : binFmts) {
486 if(bFmtPath.empty())
continue;
487 binFileName =
string_t(binRoot).append(
"/").append(bFmtPath).append(
"/").append(binBasename).append(
".").append(binSuffix);
489 if(res && res->isValid()) {
573 return create(
gl, type, count, srcRoot, srcBasenames,
"", binRoot, binBasename,
"");
632 return create(
gl, type, 1, srcRoot, srcBasenames, srcSuffixOpt, binRoot, basename, binSuffixOpt);
683 return create(
gl, type, srcRoot, binRoot, basename,
"",
"");
687 constexpr size_t id() const noexcept {
return m_id; }
690 constexpr bool isValid() const noexcept {
return 0 != m_id; }
693 constexpr bool isCompiled() const noexcept {
return m_compiled; }
709 if(!m_shaderSource.empty()) {
716 }
else if(!m_shaderBinary.empty()) {
718 m_shaderBinaryFormat, m_shaderBinary,
verbose);
731 if(!m_shaderBinary.empty()) {
732 m_shaderBinary.clear();
734 m_shaderSource.clear();
735 m_shaderBinaryFormat=0;
740 if(
this==&rhs) {
return true; }
741 return m_id == rhs.m_id;
743 constexpr std::size_t
hash_code() const noexcept {
return m_id; }
747 r.append(std::to_string(m_id)).append(
", type=").append(
shaderTypeStr())
748 .append(
", valid=").append(std::to_string(
isValid())).append(
", compiled=").append(std::to_string(m_compiled))
749 .append(
", ").append(std::to_string(m_shader.size())).append(
" shader: ");
750 for(GLuint s : m_shader) {
751 r.append(
" ").append(std::to_string(s));
753 if(!m_shaderSource.empty()) {
754 r.append(
", source]");
755 }
else if(!m_shaderBinary.empty()) {
756 r.append(
", binary]");
763 if(m_shaderSource.empty()) {
767 const size_t sourceCount = m_shaderSource.size();
768 const size_t shaderCount = m_shader.size();
770 jau::PLAIN_PRINT(
true,
"ShaderCode[id=%d, type=%s, valid=%d, compiled=%d, %zu/%zu shader:",
772 if( 0 == shaderCount ) {
775 for(
size_t i=0; i<shaderCount; ++i) {
777 jau::PLAIN_PRINT(
true,
"Shader #%zu/%zu name %u", i, shaderCount, m_shader[i]);
778 jau::PLAIN_PRINT(
true,
"--------------------------------------------------------------");
785 for(
size_t j=0; j<src.size(); j++) {
787 std::istringstream reader(src[j]);
789 while (std::getline(reader, line)) {
795 jau::PLAIN_PRINT(
true,
"--------------------------------------------------------------");
810 if(m_shaderSource.empty()) {
812 return string_t::npos;
814 const size_t shaderCount = m_shader.size();
815 if(shaderIdx>=shaderCount) {
816 ERR_PRINT(
"shaderIdx %zu not within shader bounds %zu", shaderIdx, shaderCount);
817 return string_t::npos;
819 const size_t sourceCount = m_shaderSource.size();
820 if(shaderIdx>=sourceCount) {
821 ERR_PRINT(
"shaderIdx %zu not within source bounds %zu", shaderIdx, shaderCount);
822 return string_t::npos;
826 ERR_PRINT(
"no shader source at for shader index %zu", shaderIdx);
827 return string_t::npos;
829 size_t curEndIndex = 0;
830 for(
auto & sb : src) {
831 curEndIndex += sb.length();
832 if(fromIndex < curEndIndex) {
833 size_t insertIdx = sb.find(tag, fromIndex);
834 if(string_t::npos != insertIdx) {
835 insertIdx += tag.length();
836 size_t eol = sb.find(
'\n', insertIdx);
837 if(string_t::npos == eol) {
838 eol = sb.find(
'\r', insertIdx);
840 if(string_t::npos != eol) {
843 sb.insert(insertIdx,
"\n");
846 sb.insert(insertIdx,
data);
847 return insertIdx+
data.length();
851 return string_t::npos;
863 if(m_shaderSource.empty()) {
865 return string_t::npos;
867 const size_t shaderCount = m_shader.size();
868 if(shaderIdx>=shaderCount) {
869 ERR_PRINT(
"shaderIdx %zu not within shader bounds %zu", shaderIdx, shaderCount);
870 return string_t::npos;
872 const size_t sourceCount = m_shaderSource.size();
873 if(shaderIdx>=sourceCount) {
874 ERR_PRINT(
"shaderIdx %zu not within source bounds %zu", shaderIdx, shaderCount);
875 return string_t::npos;
879 ERR_PRINT(
"no shader source at for shader index %zu", shaderIdx);
880 return string_t::npos;
882 size_t curEndIndex = 0;
884 for(; j<src.size()-1 && position > curEndIndex; ++j) {
885 curEndIndex += src[j].length();
888 const size_t lastEndIndex = curEndIndex;
889 curEndIndex += src[j].length();
890 if( position > curEndIndex ) {
891 position = curEndIndex;
893 sb.insert(position - lastEndIndex,
data);
894 return position+
data.length();
909 if( !readShaderSource(path,
data) ) {
910 return string_t::npos;
926 if(m_shaderSource.empty() || oldName == newName) {
929 const size_t oldNameLen = oldName.length();
930 const size_t newNameLen = newName.length();
932 const size_t sourceCount = m_shaderSource.size();
933 for(
size_t shaderIdx = 0; shaderIdx<sourceCount; ++shaderIdx) {
935 for(
auto & sb : src) {
937 while(curPos<sb.length()-oldNameLen+1) {
938 const size_t startIdx = sb.find(oldName, curPos);
939 if(string_t::npos != startIdx) {
940 sb.replace(startIdx, oldNameLen, newName);
941 curPos = startIdx + newNameLen;
944 curPos = sb.length();
954 static bool readShaderSource(
const string_t& conn,
string_t& result,
int& lineno)
noexcept {
957 result.append(
"// '"+conn+
"'\n");
959 result.append(
"// included @ line "+std::to_string(lineno)+
": '"+conn+
"'\n");
962 std::ifstream reader(conn);
964 while ( std::getline(reader, line) ) {
966 if (line.starts_with(
"#include ")) {
972 if( s.starts_with(
"\"") && s.ends_with(
"\"")) {
973 s = s.substr(1, s.length()-2);
983 nextConn = includeFile;
986 if( nextConn.empty() ) {
990 jau::PLAIN_PRINT(
true,
"readShaderSource: including '%s' -> '%s'", includeFile.c_str(), nextConn.c_str());
992 lineno = readShaderSource(nextConn, result, lineno);
994 result.append(line +
"\n");
1012 jau::PLAIN_PRINT(
true,
"readShaderSource: %s -> %s", path.c_str(), conn.c_str());
1014 if (!conn.empty()) {
1016 return readShaderSource(conn, result, lineno);
1032 if( !readShaderSource(path, result) ) {
1054 public static ByteBuffer readShaderBinary(
final Class<?> context,
final String path)
noexcept {
1055 final URLConnection conn = IOUtil.getResource(path, context.getClassLoader(), context);
1059 final BufferedInputStream bis =
new BufferedInputStream( conn.getInputStream() );
1061 return IOUtil.copyStream2ByteBuffer( bis );
1063 IOUtil.close(bis,
false);
1072 public static ByteBuffer readShaderBinary(
final Uri binLocation)
noexcept {
1073 final URLConnection conn = IOUtil.openURL(binLocation.toURL(),
"ShaderCode ");
1077 final BufferedInputStream bis =
new BufferedInputStream( conn.getInputStream() );
1079 return IOUtil.copyStream2ByteBuffer( bis );
1081 IOUtil.close(bis,
false);
1088 constexpr static std::string_view
es2_default_precision_vp =
"\nprecision highp float;\nprecision highp int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n";
1090 constexpr static std::string_view
es2_default_precision_fp =
"\nprecision mediump float;\nprecision mediump int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n";
1104 constexpr static std::string_view
gl3_default_precision_fp =
"\nprecision highp float;\nprecision mediump int;\n/*precision mediump sampler2D;*/\n";
1107 constexpr static std::string_view
REQUIRE =
"require";
1109 constexpr static std::string_view
ENABLE =
"enable";
1111 constexpr static std::string_view
DISABLE =
"disable";
1113 constexpr static std::string_view
WARN =
"warn";
1126 return "#extension " + extensionName +
" : " + behavior +
"\n";
1148 std::string_view defaultPrecision;
1149 if(
gl.glProfile().nativeGLES3() ) {
1150 switch ( m_shaderType ) {
1151 case GL_VERTEX_SHADER:
1153 case GL_FRAGMENT_SHADER:
1155 case GL_COMPUTE_SHADER:
1158 defaultPrecision =
"";
1161 }
else if(
gl.glProfile().nativeGLES2() ) {
1162 switch ( m_shaderType ) {
1163 case GL_VERTEX_SHADER:
1165 case GL_FRAGMENT_SHADER:
1168 defaultPrecision =
"";
1173 switch ( m_shaderType ) {
1174 case GL_VERTEX_SHADER:
1175 case GL_GEOMETRY_SHADER:
1176 case GL_TESS_CONTROL_SHADER:
1177 case GL_TESS_EVALUATION_SHADER:
1179 case GL_FRAGMENT_SHADER:
1181 case GL_COMPUTE_SHADER:
1184 defaultPrecision =
"";
1188 defaultPrecision =
"";
1190 if( defaultPrecision.length() > 0 ) {
1198 if(
gl.glProfile().nativeGLES() ) {
1206 if( !
gl.glProfile().nativeGLES() ) {
1228 if( preludeVersion ) {
1233 if( addDefaultPrecision ) {
1251 if( preludeVersion ) {
1256 if(
gl.glProfile().nativeGLES() && esDefaultPrecision.length()>0 ) {
1271 GLenum m_shaderBinaryFormat;
1273 GLenum m_shaderType;
1277 static size_t nextID() {
return m_nextID++; }
1278 static std::atomic<size_t> m_nextID;
1297 return a.hash_code();
1302 return a->hash_code();
Convenient shader code class to use and instantiate vertex or fragment programs.
size_t insertShaderSource(size_t shaderIdx, size_t position, stringview_t data) noexcept
Adds data at position in shader source for shader shaderIdx.
ShaderCode(Private, GLenum type, size_t count, const source_list_t &sources) noexcept
Private ctor for `ShaderCodeRef create(...).
static ShaderCodeRef create(GL &gl, GLenum type, size_t count, stringview_t srcRoot, const string_list_t &srcBasenames, stringview_t binRoot, stringview_t binBasename) noexcept
Simplified variation of create(GL2ES2, int, int, Class, String, String[], String, String,...
static constexpr bool requiresGL3DefaultPrecision(const GL &gl) noexcept
Returns true, if GL3 GLSL version requires default precision, i.e.
static constexpr std::string_view gl3_default_precision_fp
Default precision of GLSL ≥ 1.30 as required until < 1.50 for fragment-shader: {@value gl3_default_pr...
static constexpr std::string_view SUFFIX_TESS_CONTROL_BINARY
Unique resource suffix for GL4#GL_TESS_CONTROL_SHADER in binary: {@value}
GLenum shaderType() const noexcept
constexpr bool operator==(const ShaderCode &rhs) const noexcept
constexpr std::size_t hash_code() const noexcept
constexpr size_t id() const noexcept
Returns the unique shader id for successfully created instances, zero if instance creation failed.
static string_t createExtensionDirective(const string_t &extensionName, const string_t &behavior)
Creates a GLSL extension directive.
static ShaderCodeRef create(GL &gl, GLenum type, size_t count, const string_list_t &sourceFiles) noexcept
Creates a complete ShaderCode object while reading all shader source of sourceFiles,...
size_t replaceInShaderSource(const string_t &oldName, const string_t &newName) noexcept
Replaces oldName with newName in all shader sources.
static ShaderCodeRef create(GL &gl, GLenum type, size_t count, stringview_t srcRoot, const string_list_t &srcBasenames, stringview_t srcSuffixOpt, stringview_t binRoot, stringview_t binBasename, stringview_t binSuffixOpt) noexcept
Convenient creation method for instantiating a complete ShaderCode object either from source code usi...
static constexpr std::string_view SUFFIX_FRAGMENT_SOURCE
Unique resource suffix for GL2ES2#GL_FRAGMENT_SHADER in source code: {@value}
static constexpr std::string_view WARN
Behavior for GLSL extension directive, see createExtensionDirective(String, String),...
static constexpr std::string_view SUFFIX_COMPUTE_BINARY
Unique resource suffix for GL3ES3#GL_COMPUTE_SHADER in binary: {@value}
static constexpr std::string_view SUFFIX_TESS_EVALUATION_BINARY
Unique resource suffix for GL4#GL_TESS_EVALUATION_SHADER in binary: {@value}
static constexpr std::string_view SUFFIX_TESS_EVALUATION_SOURCE
Unique resource suffix for GL4#GL_TESS_EVALUATION_SHADER in source code: {@value}
void destroy(GL &gl) noexcept
static constexpr std::string_view SUFFIX_VERTEX_SOURCE
Unique resource suffix for GL2ES2#GL_VERTEX_SHADER in source code: {@value}
size_t defaultShaderCustomization(const GL &gl, bool preludeVersion, bool addDefaultPrecision)
Default customization of this shader source code.
bool compile(GL &gl, bool verbose=false)
GLenum shaderBinaryFormat() const noexcept
static constexpr std::string_view SUFFIX_VERTEX_BINARY
Unique resource suffix for GL2ES2#GL_VERTEX_SHADER in binary: {@value}
static string_t readShaderSource(stringview_t path) noexcept
Reads shader source from file located in path, returning the string.
constexpr bool isCompiled() const noexcept
Returns true if this instance is valid and compiled.
static ShaderCodeRef create(GLenum type, size_t count, GLenum binFormat, const bytes_t &binary) noexcept
const bytes_t & shaderBinary() const noexcept
static constexpr std::string_view SUB_PATH_NVIDIA
Unique relative path for binary shader resources for NVIDIA: {@value}
static constexpr std::string_view REQUIRE
Behavior for GLSL extension directive, see createExtensionDirective(String, String),...
static constexpr std::string_view ENABLE
Behavior for GLSL extension directive, see createExtensionDirective(String, String),...
static constexpr std::string_view gl3_default_precision_vp_gp
Default precision of GLSL ≥ 1.30 as required until < 1.50 for vertex-shader or geometry-shader: {@val...
ShaderCode(Private, GLenum type, size_t count, GLenum binFormat, const bytes_t &binary) noexcept
Private ctor for `ShaderCodeRef create(...).
static ShaderCodeRef create(GLenum type, size_t count, const source_list_t &sources) noexcept
static ShaderCodeRef create(GL &gl, GLenum type, stringview_t srcRoot, stringview_t binRoot, stringview_t basename, stringview_t srcSuffixOpt, stringview_t binSuffixOpt) noexcept
Simplified variation of create(GL2ES2, int, int, Class, String, String[], String, String,...
static constexpr std::string_view SUFFIX_TESS_CONTROL_SOURCE
Unique resource suffix for GL4#GL_TESS_CONTROL_SHADER in source code: {@value}
constexpr bool isValid() const noexcept
Returns true if this instance is valid, i.e.
const shader_list_t & shader() const noexcept
string_t shaderTypeStr() const noexcept
static constexpr std::string_view DISABLE
Behavior for GLSL extension directive, see createExtensionDirective(String, String),...
static std::string_view getFileSuffix(bool binary, GLenum type) noexcept
Returns a unique suffix for shader resources as follows:
static bool readShaderSource(stringview_t path0, string_t &result) noexcept
Reads shader source from file located in path and appends it to result.
static constexpr std::string_view SUFFIX_GEOMETRY_BINARY
Unique resource suffix for GL3#GL_GEOMETRY_SHADER in binary: {@value}
static constexpr std::string_view es3_default_precision_fp
Default precision of ES3 for fragment-shader: {@value es3_default_precision_fp}, same as for vertex-s...
string_t toString() const
static constexpr std::string_view es2_default_precision_vp
Default precision of ES2 for vertex-shader: {@value es2_default_precision_vp}.
const source_list_t & shaderSource() const noexcept
static constexpr std::string_view SUFFIX_FRAGMENT_BINARY
Unique resource suffix for GL2ES2#GL_FRAGMENT_SHADER in binary: {@value}
size_t addGLSLVersion(const GL &gl) noexcept
Add GLSL version at the head of this shader source code.
size_t insertShaderSourceFile(size_t shaderIdx, size_t position, const string_t &path) noexcept
Adds shader source located in path, either relative to the location or absolute as-is at position in ...
size_t insertShaderSource(size_t shaderIdx, stringview_t tag, size_t fromIndex, stringview_t data) noexcept
Adds data after the line containing tag.
static constexpr std::string_view SUFFIX_COMPUTE_SOURCE
Unique resource suffix for GL3ES3#GL_COMPUTE_SHADER in source code: {@value}
static ShaderCodeRef create(GL &gl, GLenum type, stringview_t srcRoot, stringview_t binRoot, stringview_t basename) noexcept
Simplified variation of create(GL2ES2, int, Class, String, String, String, String,...
static constexpr std::string_view es3_default_precision_vp
Default precision of ES3 for vertex-shader: {@value es3_default_precision_vp}.
size_t addDefaultShaderPrecision(const GL &gl, size_t pos)
Adds default precision to source code at given position if required, i.e.
static constexpr std::string_view es2_default_precision_fp
Default precision of ES2 for fragment-shader: {@value es2_default_precision_fp}.
static std::string_view getBinarySubPath(GLenum binFormat) noexcept
Returns a unique relative path for binary shader resources as follows:
static constexpr std::string_view SUFFIX_GEOMETRY_SOURCE
Unique resource suffix for GL3#GL_GEOMETRY_SHADER in source code: {@value}
static constexpr bool requiresDefaultPrecision(const GL &gl) noexcept
Returns true, if GLSL version requires default precision, i.e.
size_t defaultShaderCustomization(const GL &gl, bool preludeVersion, const string_t &esDefaultPrecision)
Default customization of this shader source code.
static bool createAndCompileShader(GL &gl, shader_list_t &shader, GLenum shaderType, const source_list_t &sources, bool verbose=false)
Creates shader.size() new shaders, stored in the shader list.
static void deleteShader(GL &, const shader_list_t &shaders) noexcept
static bool createAndLoadShader(GL &gl, shader_list_t &shader, GLenum shaderType, GLenum binFormat, const bytes_t &bin, bool verbose=false)
Creates shader.size() new shaders, stored in the shader list.
static name_list_t getShaderBinaryFormats(GL &gl) noexcept
If supported, queries the natively supported shader binary formats using GL2ES2#GL_NUM_SHADER_BINARY_...
static bool isShaderCompilerAvailable(GL &gl) noexcept
Returns true if a hader compiler is available, otherwise false.
Simple version number class containing a version number either being defined explicit or derived from...
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
std::ostream & operator<<(std::ostream &os, const T v)
bool exists(const std::string &path, bool verbose_on_error=false) noexcept
Returns true if path exists and is accessible.
std::string dirname(const std::string_view &path) noexcept
Return stripped last component from given path separated by /, excluding the trailing separator /.
bool isAbsolute(const std::string_view &path) noexcept
Returns true if first character is / or - in case of Windows - \\.
@ null
Denotes a func::null_target_t.
std::vector< uint8_t > bytes_t
std::vector< GLenum > name_list_t
std::string_view stringview_t
std::shared_ptr< ShaderCode > ShaderCodeRef
std::vector< string_t > string_list_t
std::vector< GLuint > shader_list_t
std::vector< string_list_t > source_list_t
static constexpr jau::util::VersionNumber Version1_30
Version 1.30, i.e.
static constexpr GLenum GL_NVIDIA_PLATFORM_BINARY_NV
static constexpr jau::util::VersionNumber Version1_50
Version 1.50, i.e.
constexpr std::string_view shaderTypeString(GLenum type) noexcept
constexpr bool isValidShaderType(GLenum type) noexcept
@ verbose
Verbose operations (debugging).
std::string resolve_asset(const std::string &asset_file, bool lookup_direct=false) noexcept
void trimInPlace(std::string &s) noexcept
trim in place
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
std::size_t operator()(gamp::render::gl::glsl::ShaderCodeRef const &a) const noexcept
std::size_t operator()(gamp::render::gl::glsl::ShaderCode const &a) const noexcept