Gamp v0.0.7-54-gccdc599
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 const jau::type_info& signature() const noexcept override { return jau::static_ctti<CompilerInfo>(); }
159 };
160 typedef std::shared_ptr<CompilerInfo> CompilerInfoRef;
161
162 static CompilerInfoRef getOrCreateCompilerInfo(GL& gl) noexcept {
163 AttachableRef o = gl.getAttachedObject(compilerInfoKey);
164 if( o ) {
165 CompilerInfoRef ci = std::static_pointer_cast<CompilerInfo>(o);
166 return ci;
167 }
168 CompilerInfoRef ci = std::make_shared<CompilerInfo>();
169 if (gl.glProfile().hasGLSL()) {
170 GLint param = 0;
171 glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &param);
172 const GLenum err = glGetError();
173 const size_t numFormats = GL_NO_ERROR == err ? param : 0;
174 if(numFormats>0) {
175 ci->binFormats.reserve(numFormats);
176 ci->binFormats.resize(numFormats);
177 int_list_t il(numFormats, 0);
178 glGetIntegerv(GL_SHADER_BINARY_FORMATS, il.data());
179 for(size_t i=0; i<numFormats; ++i) {
180 ci->binFormats[i] = (GLenum)il[i];
181 }
182 }
183 }
184 if(gl.glProfile().isGLES2()) {
185 GLboolean param = 0;
186 glGetBooleanv(GL_SHADER_COMPILER, &param); // GL2ES2
187 const GLenum err = glGetError();
188 const bool v = GL_NO_ERROR == err && param!=0;
189 ci->shaderCompilerAvail = v || ci->binFormats.empty(); // alt assume compiler w/o binary fmts
190 } else if( gl.glProfile().isGL2ES2() ) {
191 ci->shaderCompilerAvail = true;
192 } else {
193 ci->shaderCompilerAvail = false;
194 }
195 gl.attachObject(compilerInfoKey, ci);
196 return ci;
197 }
198
199 public:
200 /**
201 * If supported, queries the natively supported shader binary formats using
202 * {@link GL2ES2#GL_NUM_SHADER_BINARY_FORMATS} and {@link GL2ES2#GL_SHADER_BINARY_FORMATS}
203 * via {@link GL2ES2#glGetIntegerv(int, int[], int)}.
204 */
206 const CompilerInfoRef& ci = getOrCreateCompilerInfo(gl);
207 return ci->binFormats;
208 }
209
210 /** Returns true if a hader compiler is available, otherwise false. */
211 static bool isShaderCompilerAvailable(GL& gl) noexcept {
212 const CompilerInfoRef& ci = getOrCreateCompilerInfo(gl);
213 return ci->shaderCompilerAvail;
214 }
215
216 /** Returns true if GeometryShader is supported, i.e. whether GLContext is &ge; 3.2 or ARB_geometry_shader4 extension is available. */
217 static bool isGeometryShaderSupported(GL& gl) noexcept {
218 return gl.version() >= Version3_2 ||
219 gl.isExtensionAvailable("ARB_geometry_shader4");
220 }
221
222 static void shaderSource(GL& gl, GLuint shader, const string_list_t& source) {
224 throw RenderException("No compiler is available", E_FILE_LINE);
225 }
226 const size_t count = source.size();
228 if(count==0) {
229 throw RenderException("No sources specified", E_FILE_LINE);
230 }
231 std::vector<GLint> lengths(count, 0);
232 for(size_t i=0; i<count; ++i) {
233 size_t l = source[i].length();
235 lengths[i] = (GLint)l;
236 }
237 std::vector<GLchar*> sourcePtr(count, nullptr);
238 for(size_t i = 0; i<count; ++i) {
239 sourcePtr[i] = (GLchar*)source[i].data();
240 }
241 glShaderSource(shader, (GLsizei)count, sourcePtr.data(), lengths.data());
242 }
243
244 static void shaderSource(GL& gl, const shader_list_t& shaders, const source_list_t& sources) {
245 const size_t sourceNum = sources.size();
246 const size_t shaderNum = shaders.size();
247 if(shaderNum==0 || sourceNum==0 || shaderNum!=sourceNum) {
248 throw RenderException("Invalid number of shaders and/or sources: shaders="+
249 std::to_string(shaderNum)+", sources="+std::to_string(sourceNum), E_FILE_LINE);
250 }
251 for(size_t i=0; i<sourceNum; ++i) {
252 shaderSource(gl, shaders[i], sources[i]);
253 }
254 }
255
256 static void shaderBinary(GL& gl, const shader_list_t& shaders, GLenum binFormat, const bytes_t& bin) {
257 if(getShaderBinaryFormats(gl).size()==0) {
258 throw RenderException("No binary formats supported", E_FILE_LINE);
259 }
260
261 const size_t shaderNum = shaders.size();
263 if(shaderNum==0) {
264 throw RenderException("No shaders specified", E_FILE_LINE);
265 }
266 if(bin.empty()) {
267 throw RenderException("Null shader binary", E_FILE_LINE);
268 }
269 const size_t binLength = bin.size();
271 if(0==binLength) {
272 throw RenderException("Empty shader binary (remaining == 0)", E_FILE_LINE);
273 }
274 glShaderBinary((GLsizei)shaderNum, shaders.data(), binFormat, bin.data(), (GLsizei)binLength);
275 }
276
277 static void compileShader(GL&, const shader_list_t& shaders) noexcept {
278 for (GLuint shader : shaders) {
279 glCompileShader(shader);
280 }
281 }
282
283 static void attachShader(GL&, GLuint program, const shader_list_t& shaders) noexcept {
284 for (GLuint shader : shaders) {
285 glAttachShader(program, shader);
286 }
287 }
288
289 static void detachShader(GL&, GLuint program, const shader_list_t& shaders) noexcept {
290 for (GLuint shader : shaders) {
291 glDetachShader(program, shader);
292 }
293 }
294
295 static void deleteShader(GL&, const shader_list_t& shaders) noexcept {
296 for (GLuint shader : shaders) {
297 glDeleteShader(shader);
298 }
299 }
300
301 /** Creates shader.size() new shaders, stored in the shader list. Then the bin shader are loaded into them. */
302 static bool createAndLoadShader(GL& gl, shader_list_t& shader, GLenum shaderType,
303 GLenum binFormat, const bytes_t& bin, bool verbose=false)
304 {
305 GLenum err = glGetError(); // flush previous errors ..
306 if(err!=GL_NO_ERROR && verbose) {
307 jau::PLAIN_PRINT(true, "createAndLoadShader: Pre GL Error: 0x%x", err);
308 }
309
310 createShader(gl, shaderType, shader);
311 err = glGetError();
312 if(err!=GL_NO_ERROR) {
313 throw RenderException("createAndLoadShader: CreateShader failed, GL Error: "+jau::toHexString(err), E_FILE_LINE);
314 }
315
316 shaderBinary(gl, shader, binFormat, bin);
317
318 err = glGetError();
319 if(err!=GL_NO_ERROR && verbose) {
320 jau::PLAIN_PRINT(true, "createAndLoadShader: ShaderBinary failed, GL Error: 0x%x", err);
321 }
322 return err == GL_NO_ERROR;
323 }
324
325 /** Creates shader.size() new shaders, stored in the shader list. Then the source shader are loaded into them and compiled. */
326 static bool createAndCompileShader(GL& gl, shader_list_t& shader, GLenum shaderType,
327 const source_list_t& sources, bool verbose=false)
328 {
329 GLenum err = glGetError(); // flush previous errors ..
330 if(err!=GL_NO_ERROR && verbose) {
331 jau::PLAIN_PRINT(true, "createAndCompileShader: Pre GL Error: 0x%x", err);
332 }
333
334 createShader(gl, shaderType, shader);
335 err = glGetError();
336 if(err!=GL_NO_ERROR) {
337 throw RenderException("createAndCompileShader: CreateShader failed, GL Error: "+jau::toHexString(err), E_FILE_LINE);
338 }
339 shaderSource(gl, shader, sources);
340 err = glGetError();
341 if(err!=GL_NO_ERROR) {
342 throw RenderException("createAndCompileShader: ShaderSource failed, GL Error: "+jau::toHexString(err), E_FILE_LINE);
343 }
344
345 compileShader(gl, shader);
346 err = glGetError();
347 if(err!=GL_NO_ERROR && verbose) {
348 jau::PLAIN_PRINT(true, "createAndCompileShader: CompileShader failed, GL Error: 0x%x", err);
349 }
350
351 return isShaderStatusValid(gl, shader, GL_COMPILE_STATUS, verbose) && err == GL_NO_ERROR;
352 }
353 };
354
355 /**@}*/
356}
357
358#endif // GAMP_GLSLSHADERUTIL_HPP_
#define E_FILE_LINE
An attachable object.
Definition GampTypes.hpp:78
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:83
void throwOnOverflow(T has)
Definition GampTypes.hpp:64
std::string toHexString(const void *data, const nsize_t length, const lb_endian_t byteOrder=lb_endian_t::big, const LoUpCase capitalization=LoUpCase::lower, const PrefixOpt prefix=PrefixOpt::prefix) noexcept
Produce a hexadecimal string representation of the given lsb-first byte values.
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:264