Gamp v0.0.7-36-g24b1eb6
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
ShaderUtil.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11
12#ifndef GAMP_GLSLSHADERUTIL_HPP_
13#define GAMP_GLSLSHADERUTIL_HPP_
14
17
18namespace gamp::render::gl::glsl {
19 using namespace gamp::render::gl;
20
21 /** \addtogroup Gamp_GLSL
22 *
23 * @{
24 */
25
26 typedef std::vector<GLuint> shader_list_t;
27 typedef std::vector<GLenum> name_list_t;
28 typedef std::vector<GLint> int_list_t;
29 typedef std::vector<GLfloat> float_list_t;
30 typedef std::string string_t;
31 typedef std::string_view stringview_t;
32 typedef std::vector<stringview_t> stringview_list_t;
33 typedef std::vector<string_t> string_list_t;
34 typedef std::vector<string_list_t> source_list_t;
35 typedef std::vector<uint8_t> bytes_t;
36
37 /** Returns type signature of std::vector<T>. */
38 template<typename T>
39 const jau::type_info& vectorSignature() noexcept {
41 }
42
43 class ShaderUtil {
44 public:
45 static std::string getShaderInfoLog(GL&, GLuint shaderObj) noexcept {
46 GLint infoLogLength = 0;
47 glGetShaderiv(shaderObj, GL_INFO_LOG_LENGTH, &infoLogLength);
48
49 if(infoLogLength==0) {
50 return "(no info log)";
51 }
52 GLsizei charsWritten = 0;
53 std::string infoLog;
54 infoLog.reserve(infoLogLength + 1); // incl. EOS
55 infoLog.resize(infoLogLength); // excl. EOS
56 glGetShaderInfoLog(shaderObj, infoLogLength, &charsWritten, &infoLog[0]);
57 return infoLog;
58 }
59
60 static std::string getProgramInfoLog(GL&, GLuint programObj) noexcept {
61 GLint infoLogLength = 0;
62 glGetShaderiv(programObj, GL_INFO_LOG_LENGTH, &infoLogLength);
63
64 if(infoLogLength==0) {
65 return "(no info log)";
66 }
67 GLsizei charsWritten = 0;
68 std::string infoLog;
69 infoLog.reserve(infoLogLength + 1); // incl. EOS
70 infoLog.resize(infoLogLength); // excl. EOS
71 glGetProgramInfoLog(programObj, infoLogLength, &charsWritten, &infoLog[0]);
72 return infoLog;
73 }
74
75 static bool isShaderStatusValid(GL& gl, GLuint shaderObj, GLenum name, bool verbose=false) noexcept {
76 GLint ires = 0;
77 glGetShaderiv(shaderObj, name, &ires);
78
79 const bool res = ires==1;
80 if(!res && verbose) {
81 jau::PLAIN_PRINT(true, "Shader status invalid: %s", getShaderInfoLog(gl, shaderObj).c_str());
82 }
83 return res;
84 }
85
86 static bool isShaderStatusValid(GL& gl, const shader_list_t& shaders, GLenum name, bool verbose=false) noexcept {
87 bool res = true;
88 for (GLuint s : shaders) {
89 res = isShaderStatusValid(gl, s, name, verbose) && res;
90 }
91 return res;
92 }
93
94 static bool isProgramStatusValid(GL& gl, GLuint programObj, GLenum name, bool verbose=false) noexcept {
95 GLint ires = 0;
96 glGetProgramiv(programObj, name, &ires);
97 const bool res = ires==1;
98 if(!res && verbose) {
99 jau::PLAIN_PRINT(true, "Program status invalid: %s", getProgramInfoLog(gl, programObj).c_str());
100 }
101 return ires==1;
102 }
103
104 static bool isProgramLinkStatusValid(GL& gl, GLuint programObj, bool verbose=false) noexcept {
105 if(!glIsProgram(programObj)) {
106 if(verbose) {
107 jau::PLAIN_PRINT(true, "Program name invalid: %u", programObj);
108 }
109 return false;
110 }
111 if(!isProgramStatusValid(gl, programObj, GL_LINK_STATUS)) {
112 if(verbose) {
113 jau::PLAIN_PRINT(true, "Program link failed: %u\n\t%s", programObj, getProgramInfoLog(gl, programObj).c_str());
114 }
115 return false;
116 }
117 return true;
118 }
119
120 /**
121 * Performs {@link GL2ES2#glValidateProgram(int)}
122 * <p>
123 * One shall only call this method while debugging and only if all required
124 * resources by the shader are set.
125 * </p>
126 * <p>
127 * Note: It is possible that a working shader program will fail validation.
128 * This has been experienced on NVidia APX2500 and Tegra2.
129 * </p>
130 * @see GL2ES2#glValidateProgram(int)
131 **/
132 static bool isProgramExecStatusValid(GL& gl, GLuint programObj, bool verbose=false) noexcept {
133 glValidateProgram(programObj);
134 if(!isProgramStatusValid(gl, programObj, GL_VALIDATE_STATUS)) {
135 if(verbose) {
136 jau::PLAIN_PRINT(true, "Program validation failed: %u\n\t%s", programObj, getProgramInfoLog(gl, programObj).c_str());
137 }
138 return false;
139 }
140 return true;
141 }
142
143 /** Creates shaders.size() new shaders and stores them in the shaders list. */
144 static void createShader(GL&, GLenum type, shader_list_t& shaders) noexcept {
145 for (GLuint & shader : shaders) {
146 shader = glCreateShader(type);
147 }
148 }
149
150 private:
151 static constexpr const char* compilerInfoKey = "gamp.renderer.gl.glsl.CompilerInfo";
152 class CompilerInfo : public Attachable {
153 public:
154 /** flag whether shader compiler is supported */
155 bool shaderCompilerAvail;
156 /** List of binary shader formats */
157 name_list_t binFormats;
158 };
159 typedef std::shared_ptr<CompilerInfo> CompilerInfoRef;
160
161 static CompilerInfoRef getOrCreateCompilerInfo(GL& gl) noexcept {
162 AttachableRef o = gl.getAttachedObject(compilerInfoKey);
163 if( o ) {
164 CompilerInfoRef ci = std::static_pointer_cast<CompilerInfo>(o);
165 return ci;
166 }
167 CompilerInfoRef ci = std::make_shared<CompilerInfo>();
168 if (gl.glProfile().hasGLSL()) {
169 GLint param = 0;
170 glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &param);
171 const GLenum err = glGetError();
172 const size_t numFormats = GL_NO_ERROR == err ? param : 0;
173 if(numFormats>0) {
174 ci->binFormats.reserve(numFormats);
175 ci->binFormats.resize(numFormats);
176 int_list_t il(numFormats, 0);
177 glGetIntegerv(GL_SHADER_BINARY_FORMATS, il.data());
178 for(size_t i=0; i<numFormats; ++i) {
179 ci->binFormats[i] = (GLenum)il[i];
180 }
181 }
182 }
183 if(gl.glProfile().isGLES2()) {
184 GLboolean param = 0;
185 glGetBooleanv(GL_SHADER_COMPILER, &param); // GL2ES2
186 const GLenum err = glGetError();
187 const bool v = GL_NO_ERROR == err && param!=0;
188 ci->shaderCompilerAvail = v || ci->binFormats.empty(); // alt assume compiler w/o binary fmts
189 } else if( gl.glProfile().isGL2ES2() ) {
190 ci->shaderCompilerAvail = true;
191 } else {
192 ci->shaderCompilerAvail = false;
193 }
194 gl.attachObject(compilerInfoKey, ci);
195 return ci;
196 }
197
198 public:
199 /**
200 * If supported, queries the natively supported shader binary formats using
201 * {@link GL2ES2#GL_NUM_SHADER_BINARY_FORMATS} and {@link GL2ES2#GL_SHADER_BINARY_FORMATS}
202 * via {@link GL2ES2#glGetIntegerv(int, int[], int)}.
203 */
205 const CompilerInfoRef& ci = getOrCreateCompilerInfo(gl);
206 return ci->binFormats;
207 }
208
209 /** Returns true if a hader compiler is available, otherwise false. */
210 static bool isShaderCompilerAvailable(GL& gl) noexcept {
211 const CompilerInfoRef& ci = getOrCreateCompilerInfo(gl);
212 return ci->shaderCompilerAvail;
213 }
214
215 /** Returns true if GeometryShader is supported, i.e. whether GLContext is &ge; 3.2 or ARB_geometry_shader4 extension is available. */
216 static bool isGeometryShaderSupported(GL& gl) noexcept {
217 return gl.version() >= Version3_2 ||
218 gl.isExtensionAvailable("ARB_geometry_shader4");
219 }
220
221 static void shaderSource(GL& gl, GLuint shader, const string_list_t& source) {
223 throw RenderException("No compiler is available", E_FILE_LINE);
224 }
225 const size_t count = source.size();
227 if(count==0) {
228 throw RenderException("No sources specified", E_FILE_LINE);
229 }
230 std::vector<GLint> lengths(count, 0);
231 for(size_t i=0; i<count; ++i) {
232 size_t l = source[i].length();
234 lengths[i] = (GLint)l;
235 }
236 std::vector<GLchar*> sourcePtr(count, nullptr);
237 for(size_t i = 0; i<count; ++i) {
238 sourcePtr[i] = (GLchar*)source[i].data();
239 }
240 glShaderSource(shader, (GLsizei)count, sourcePtr.data(), lengths.data());
241 }
242
243 static void shaderSource(GL& gl, const shader_list_t& shaders, const source_list_t& sources) {
244 const size_t sourceNum = sources.size();
245 const size_t shaderNum = shaders.size();
246 if(shaderNum==0 || sourceNum==0 || shaderNum!=sourceNum) {
247 throw RenderException("Invalid number of shaders and/or sources: shaders="+
248 std::to_string(shaderNum)+", sources="+std::to_string(sourceNum), E_FILE_LINE);
249 }
250 for(size_t i=0; i<sourceNum; ++i) {
251 shaderSource(gl, shaders[i], sources[i]);
252 }
253 }
254
255 static void shaderBinary(GL& gl, const shader_list_t& shaders, GLenum binFormat, const bytes_t& bin) {
256 if(getShaderBinaryFormats(gl).size()==0) {
257 throw RenderException("No binary formats supported", E_FILE_LINE);
258 }
259
260 const size_t shaderNum = shaders.size();
262 if(shaderNum==0) {
263 throw RenderException("No shaders specified", E_FILE_LINE);
264 }
265 if(bin.empty()) {
266 throw RenderException("Null shader binary", E_FILE_LINE);
267 }
268 const size_t binLength = bin.size();
270 if(0==binLength) {
271 throw RenderException("Empty shader binary (remaining == 0)", E_FILE_LINE);
272 }
273 glShaderBinary((GLsizei)shaderNum, shaders.data(), binFormat, bin.data(), (GLsizei)binLength);
274 }
275
276 static void compileShader(GL&, const shader_list_t& shaders) noexcept {
277 for (GLuint shader : shaders) {
278 glCompileShader(shader);
279 }
280 }
281
282 static void attachShader(GL&, GLuint program, const shader_list_t& shaders) noexcept {
283 for (GLuint shader : shaders) {
284 glAttachShader(program, shader);
285 }
286 }
287
288 static void detachShader(GL&, GLuint program, const shader_list_t& shaders) noexcept {
289 for (GLuint shader : shaders) {
290 glDetachShader(program, shader);
291 }
292 }
293
294 static void deleteShader(GL&, const shader_list_t& shaders) noexcept {
295 for (GLuint shader : shaders) {
296 glDeleteShader(shader);
297 }
298 }
299
300 /** Creates shader.size() new shaders, stored in the shader list. Then the bin shader are loaded into them. */
301 static bool createAndLoadShader(GL& gl, shader_list_t& shader, GLenum shaderType,
302 GLenum binFormat, const bytes_t& bin, bool verbose=false)
303 {
304 GLenum err = glGetError(); // flush previous errors ..
305 if(err!=GL_NO_ERROR && verbose) {
306 jau::PLAIN_PRINT(true, "createAndLoadShader: Pre GL Error: 0x%x", err);
307 }
308
309 createShader(gl, shaderType, shader);
310 err = glGetError();
311 if(err!=GL_NO_ERROR) {
312 throw RenderException("createAndLoadShader: CreateShader failed, GL Error: "+jau::to_hexstring(err), E_FILE_LINE);
313 }
314
315 shaderBinary(gl, shader, binFormat, bin);
316
317 err = glGetError();
318 if(err!=GL_NO_ERROR && verbose) {
319 jau::PLAIN_PRINT(true, "createAndLoadShader: ShaderBinary failed, GL Error: 0x%x", err);
320 }
321 return err == GL_NO_ERROR;
322 }
323
324 /** Creates shader.size() new shaders, stored in the shader list. Then the source shader are loaded into them and compiled. */
325 static bool createAndCompileShader(GL& gl, shader_list_t& shader, GLenum shaderType,
326 const source_list_t& sources, bool verbose=false)
327 {
328 GLenum err = glGetError(); // flush previous errors ..
329 if(err!=GL_NO_ERROR && verbose) {
330 jau::PLAIN_PRINT(true, "createAndCompileShader: Pre GL Error: 0x%x", err);
331 }
332
333 createShader(gl, shaderType, shader);
334 err = glGetError();
335 if(err!=GL_NO_ERROR) {
336 throw RenderException("createAndCompileShader: CreateShader failed, GL Error: "+jau::to_hexstring(err), E_FILE_LINE);
337 }
338 shaderSource(gl, shader, sources);
339 err = glGetError();
340 if(err!=GL_NO_ERROR) {
341 throw RenderException("createAndCompileShader: ShaderSource failed, GL Error: "+jau::to_hexstring(err), E_FILE_LINE);
342 }
343
344 compileShader(gl, shader);
345 err = glGetError();
346 if(err!=GL_NO_ERROR && verbose) {
347 jau::PLAIN_PRINT(true, "createAndCompileShader: CompileShader failed, GL Error: 0x%x", err);
348 }
349
350 return isShaderStatusValid(gl, shader, GL_COMPILE_STATUS, verbose) && err == GL_NO_ERROR;
351 }
352 };
353
354 /**@}*/
355}
356
357#endif // GAMP_GLSLSHADERUTIL_HPP_
#define E_FILE_LINE
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 bool isShaderStatusValid(GL &gl, const shader_list_t &shaders, GLenum name, bool verbose=false) noexcept
static std::string getShaderInfoLog(GL &, GLuint shaderObj) noexcept
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 void compileShader(GL &, const shader_list_t &shaders) noexcept
static void shaderBinary(GL &gl, const shader_list_t &shaders, GLenum binFormat, const bytes_t &bin)
static bool isProgramStatusValid(GL &gl, GLuint programObj, GLenum name, bool verbose=false) noexcept
static name_list_t getShaderBinaryFormats(GL &gl) noexcept
If supported, queries the natively supported shader binary formats using GL2ES2#GL_NUM_SHADER_BINARY_...
static std::string getProgramInfoLog(GL &, GLuint programObj) noexcept
static void shaderSource(GL &gl, GLuint shader, const string_list_t &source)
static bool isGeometryShaderSupported(GL &gl) noexcept
Returns true if GeometryShader is supported, i.e.
static void attachShader(GL &, GLuint program, const shader_list_t &shaders) noexcept
static void detachShader(GL &, GLuint program, const shader_list_t &shaders) noexcept
static bool isShaderStatusValid(GL &gl, GLuint shaderObj, GLenum name, bool verbose=false) noexcept
static void createShader(GL &, GLenum type, shader_list_t &shaders) noexcept
Creates shaders.size() new shaders and stores them in the shaders list.
static bool isProgramExecStatusValid(GL &gl, GLuint programObj, bool verbose=false) noexcept
Performs GL2ES2#glValidateProgram(int).
static bool isProgramLinkStatusValid(GL &gl, GLuint programObj, bool verbose=false) noexcept
static void shaderSource(GL &gl, const shader_list_t &shaders, const source_list_t &sources)
static bool isShaderCompilerAvailable(GL &gl) noexcept
Returns true if a hader compiler is available, otherwise false.
Generic type information using either Runtime type information (RTTI) or Compile time type informatio...
consteval_cxx20 std::string_view name() noexcept
const jau::type_info & static_ctti() noexcept
Returns a static global reference of make_ctti<T>(true) w/ identity instance.
std::vector< uint8_t > bytes_t
std::vector< GLenum > name_list_t
std::vector< GLfloat > float_list_t
std::string_view stringview_t
std::vector< GLint > int_list_t
std::vector< stringview_t > stringview_list_t
const jau::type_info & vectorSignature() noexcept
Returns type signature of std::vector<T>.
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 Version3_2
Version 3.2.
@ verbose
Verbose operations (debugging).
std::shared_ptr< Attachable > AttachableRef
Definition GampTypes.hpp:79
void throwOnOverflow(T has)
Definition GampTypes.hpp:64
std::string to_hexstring(value_type const &v, const bool skipLeading0x=false) noexcept
Produce a lower-case hexadecimal string representation with leading 0x in MSB of the given pointer.
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:258
An attachable object.
Definition GampTypes.hpp:78