Gamp v0.0.7-36-g24b1eb6
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
ShaderProgram.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_GLSLSHADERPROGRAM_HPP_
13#define GAMP_GLSLSHADERPROGRAM_HPP_
14
15#include <unordered_set>
16
17#include <jau/basic_types.hpp>
18#include <jau/debug.hpp>
19#include <jau/file_util.hpp>
20#include <jau/io_util.hpp>
21#include <jau/string_util.hpp>
22
23#include <gamp/Gamp.hpp>
25
26namespace gamp::render::gl::glsl {
27 using namespace gamp::render::gl;
28
29 /** \addtogroup Gamp_GLSL
30 *
31 * @{
32 */
33
34 class ShaderProgram;
35 typedef std::shared_ptr<ShaderProgram> ShaderProgramRef;
36
38 private:
39 struct Private{ explicit Private() = default; };
40
41 public:
42 /** Private ctor for `ShaderProgramRef create(...). */
43 ShaderProgram(Private) noexcept
44 : m_programLinked(false), m_programInUse(false), m_shaderProgram(0), m_id(0) {
45 m_id = nextID();
46 }
47 static ShaderProgramRef create() noexcept {
48 return std::make_shared<ShaderProgram>(Private());
49 }
50
51 constexpr bool linked() const noexcept { return m_programLinked; }
52
53 constexpr bool inUse() const noexcept { return m_programInUse; }
54
55 /** Returns the shader program name, which is non zero if valid. */
56 constexpr GLuint program() const noexcept { return m_shaderProgram; }
57
58 /** Returns the unique program id for successfully created instances, zero if instance creation failed. */
59 constexpr size_t id() const noexcept { return m_id; }
60
61 /**
62 * Detaches all shader codes and deletes the program.
63 * Destroys the shader codes as well.
64 * Calls release(gl, true)
65 *
66 * @see #release(GL2ES2, boolean)
67 */
68 void destroy(GL& gl) noexcept {
69 release(gl, true);
70 }
71
72 /**
73 * Detaches all shader codes and deletes the program,
74 * but leaves the shader code intact.
75 * Calls release(gl, false)
76 *
77 * @see #release(GL2ES2, boolean)
78 */
79 void release(GL& gl) noexcept {
80 release(gl, false);
81 }
82
83 /**
84 * Detaches all shader codes and deletes the program.
85 * If <code>destroyShaderCode</code> is true it destroys the shader codes as well.
86 */
87 void release(GL& gl, bool destroyShaderCode) noexcept {
88 if( m_programLinked ) {
90 }
91 for(const auto & shaderCode : m_allShaderCode) {
92 if( 1 == m_attachedShaderCode.erase(shaderCode) ) {
93 ShaderUtil::detachShader(gl, m_shaderProgram, shaderCode->shader());
94 }
95 if(destroyShaderCode) {
96 shaderCode->destroy(gl);
97 }
98 }
99 m_allShaderCode.clear();
100 m_attachedShaderCode.clear();
101 if( 0 != m_shaderProgram ) {
102 glDeleteProgram(m_shaderProgram);
103 m_shaderProgram=0;
104 }
105 m_programLinked=false;
106 }
107
108 //
109 // ShaderCode handling
110 //
111
112 /**
113 * Adds a new shader to this program.
114 *
115 * <p>This command does not compile and attach the shader,
116 * use {@link #add(GL2ES2, ShaderCode)} for this purpose.</p>
117 *
118 * @return true if the shader was successfully added, otherwise false (duplicate)
119 */
120 bool add(const ShaderCodeRef& shaderCode) noexcept {
121 auto res = m_allShaderCode.insert(shaderCode);
122 return res.second;
123 }
124
125 bool contains(const ShaderCodeRef& shaderCode) const noexcept{
126 return m_allShaderCode.contains(shaderCode);
127 }
128
129 /**
130 * Warning slow O(n) operation ..
131 * @param id
132 * @return
133 */
134 ShaderCodeRef getShader(size_t id) noexcept {
135 for(const auto & shaderCode : m_allShaderCode) {
136 if(shaderCode->id() == id) {
137 return shaderCode;
138 }
139 }
140 return nullptr;
141 }
142
143 //
144 // ShaderCode / Program handling
145 //
146
147 /**
148 * Creates the empty GL program object using {@link GL2ES2#glCreateProgram()},
149 * if not already created.
150 *
151 * @param gl
152 * @return true if shader program is valid, i.e. not zero
153 */
154 bool init(const GL&) noexcept {
155 if( 0 == m_shaderProgram ) {
156 m_shaderProgram = glCreateProgram();
157 }
158 return 0 != m_shaderProgram;
159 }
160
161 /**
162 * Adds a new shader to a this non running program.
163 *
164 * <p>Compiles and attaches the shader, if not done yet.</p>
165 *
166 * @return true if the shader was successfully added, false if duplicate or compilation failed.
167 */
168 bool add(GL& gl, const ShaderCodeRef& shaderCode, bool verbose=false) {
169 if( !init(gl) ) { return false; }
170 if( m_allShaderCode.insert(shaderCode).second ) {
171 if( !shaderCode->compile(gl, verbose) ) {
172 return false;
173 }
174 if( m_attachedShaderCode.insert(shaderCode).second ) {
175 ShaderUtil::attachShader(gl, m_shaderProgram, shaderCode->shader());
176 }
177 }
178 return true;
179 }
180
181 /**
182 * Replace a shader in a program and re-links the program.
183 *
184 * @param gl
185 * @param oldShader the to be replace Shader
186 * @param newShader the new ShaderCode
187 * @param verboseOut the optional verbose output stream
188 *
189 * @return true if all steps are valid, shader compilation, attachment and linking; otherwise false.
190 *
191 * @see ShaderState#glEnableVertexAttribArray
192 * @see ShaderState#glDisableVertexAttribArray
193 * @see ShaderState#glVertexAttribPointer
194 * @see ShaderState#getVertexAttribPointer
195 * @see ShaderState#glReleaseAllVertexAttributes
196 * @see ShaderState#glResetAllVertexAttributes
197 * @see ShaderState#glResetAllVertexAttributes
198 * @see ShaderState#glResetAllVertexAttributes
199 */
200 bool replaceShader(GL& gl, const ShaderCodeRef& oldShader, const ShaderCodeRef& newShader, bool verbose=false) {
201 if(!init(gl) || !newShader->compile(gl, verbose)) {
202 return false;
203 }
204
205 const bool shaderWasInUse = inUse();
206 if(shaderWasInUse) {
208 }
209
210 if( 1 == m_allShaderCode.erase(oldShader) && 1 == m_attachedShaderCode.erase(oldShader) ) {
211 ShaderUtil::detachShader(gl, m_shaderProgram, oldShader->shader());
212 }
213
214 add(newShader);
215 if( m_attachedShaderCode.insert(newShader).second ) {
216 ShaderUtil::attachShader(gl, m_shaderProgram, newShader->shader());
217 }
218
219 glLinkProgram(m_shaderProgram);
220
221 m_programLinked = ShaderUtil::isProgramLinkStatusValid(gl, m_shaderProgram, verbose);
222 if ( m_programLinked && shaderWasInUse ) {
223 useProgram(gl, true);
224 }
225 return m_programLinked;
226 }
227
228 /**
229 * Links the shader code to the program.
230 *
231 * <p>Compiles and attaches the shader code to the program if not done by yet</p>
232 *
233 * <p>Within this process, all GL resources (shader and program objects) are created if necessary.</p>
234 *
235 * @param gl
236 * @param verboseOut
237 * @return true if program was successfully linked and is valid, otherwise false
238 *
239 * @see #init(GL2ES2)
240 */
241 bool link(GL& gl, bool verbose=false) {
242 if( !init(gl) ) {
243 m_programLinked = false; // mark unlinked due to user attempt to [re]link
244 return false;
245 }
246
247 for(const auto & shaderCode : m_allShaderCode) {
248 if(!shaderCode->compile(gl, verbose)) {
249 m_programLinked = false; // mark unlinked due to user attempt to [re]link
250 return false;
251 }
252 if( m_attachedShaderCode.insert(shaderCode).second ) {
253 ShaderUtil::attachShader(gl, m_shaderProgram, shaderCode->shader());
254 }
255 }
256
257 // Link the program
258 glLinkProgram(m_shaderProgram);
259
260 m_programLinked = ShaderUtil::isProgramLinkStatusValid(gl, m_shaderProgram, verbose);
261
262 return m_programLinked;
263 }
264
265 constexpr bool operator==(const ShaderProgram& rhs) const noexcept {
266 if(this==&rhs) { return true; }
267 return m_id == rhs.m_id;
268 }
269 constexpr std::size_t hash_code() const noexcept { return m_id; }
270
272 string_t sb("ShaderCode[id=");
273 sb.append(std::to_string(m_id))
274 .append(", linked=").append(std::to_string(m_programLinked))
275 .append(", inUse=").append(std::to_string(m_programInUse))
276 .append(", program: ").append(std::to_string(m_shaderProgram)).append(", ")
277 .append(std::to_string(m_allShaderCode.size())).append(" code: ");
278 if( 0 < m_allShaderCode.size() ) {
279 for(const auto & iter : m_allShaderCode) {
280 sb.append("\n").append(" ").append(iter->toString());
281 }
282 } else {
283 sb.append("none");
284 }
285 sb.append("]");
286 return sb;
287 }
288
289 /**
290 * Performs {@link GL2ES2#glValidateProgram(int)} via {@link ShaderUtil#isProgramExecStatusValid(GL, int, PrintStream)}.
291 * @return true on success, otherwise false
292 * @see ShaderUtil#isProgramExecStatusValid(GL, int, PrintStream)
293 **/
294 bool validateProgram(GL& gl, bool verbose=false) noexcept {
295 return ShaderUtil::isProgramExecStatusValid(gl, m_shaderProgram, verbose);
296 }
297
298 /**
299 * Enables or disabled the shader program.
300 *
301 * @throws GLException if on==true and program was not linked
302 */
303 void useProgram(const GL&, bool on) {
304 if(on && !m_programLinked) {
305 ERR_PRINT("Not linked (on = %d): %s", on, toString().c_str());
306 throw RenderException("Program is not linked", E_FILE_LINE);
307 }
308 if(m_programInUse==on) { return; }
309 if( 0 == m_shaderProgram ) {
310 on = false;
311 }
312 glUseProgram( on ? m_shaderProgram : 0 );
313 m_programInUse = on;
314 }
315 /**
316 * Disabled the shader program.
317 */
318 void unUseProgram(const GL&) noexcept {
319 if(!m_programInUse) { return; }
320 glUseProgram( 0 );
321 m_programInUse = false;
322 }
323 void notifyNotInUse() noexcept {
324 m_programInUse = false;
325 }
326
327 void dumpSource() {
328 jau::PLAIN_PRINT(true, "");
329 jau::PLAIN_PRINT(true, "%s", toString().c_str());
330 for(const auto & iter : m_allShaderCode) {
331 iter->dumpSource();
332 }
333 jau::PLAIN_PRINT(true, "");
334 }
335
336 private:
337 bool m_programLinked;
338 bool m_programInUse;
339 GLuint m_shaderProgram; // non zero is valid!
340 size_t m_id;
341 std::unordered_set<ShaderCodeRef> m_allShaderCode, m_attachedShaderCode;
342
343 static size_t nextID() { return m_nextID++; }
344 static std::atomic<size_t> m_nextID;
345 };
346
347 inline std::ostream& operator<<(std::ostream& out, const ShaderProgram& v) {
348 return out << v.toString();
349 }
350
351 /**@}*/
352}
353
354// injecting specialization of std::hash to namespace std of our types above
355namespace std
356{
357 /** \addtogroup Gamp_GLSL
358 *
359 */
360
361 template<> struct hash<gamp::render::gl::glsl::ShaderProgram> {
362 std::size_t operator()(gamp::render::gl::glsl::ShaderProgram const& a) const noexcept {
363 return a.hash_code();
364 }
365 };
366 template<> struct hash<gamp::render::gl::glsl::ShaderProgramRef> {
367 std::size_t operator()(gamp::render::gl::glsl::ShaderProgramRef const& a) const noexcept {
368 return a->hash_code();
369 }
370 };
371
372 /**@}*/
373}
374
375#endif // GAMP_GLSLSHADERPROGRAM_HPP_
376
#define E_FILE_LINE
constexpr std::size_t hash_code() const noexcept
void unUseProgram(const GL &) noexcept
Disabled the shader program.
bool init(const GL &) noexcept
Creates the empty GL program object using GL2ES2#glCreateProgram(), if not already created.
constexpr size_t id() const noexcept
Returns the unique program id for successfully created instances, zero if instance creation failed.
void release(GL &gl) noexcept
Detaches all shader codes and deletes the program, but leaves the shader code intact.
void release(GL &gl, bool destroyShaderCode) noexcept
Detaches all shader codes and deletes the program.
constexpr GLuint program() const noexcept
Returns the shader program name, which is non zero if valid.
constexpr bool linked() const noexcept
ShaderProgram(Private) noexcept
Private ctor for `ShaderProgramRef create(...).
bool link(GL &gl, bool verbose=false)
Links the shader code to the program.
void destroy(GL &gl) noexcept
Detaches all shader codes and deletes the program.
bool add(GL &gl, const ShaderCodeRef &shaderCode, bool verbose=false)
Adds a new shader to a this non running program.
static ShaderProgramRef create() noexcept
void useProgram(const GL &, bool on)
Enables or disabled the shader program.
constexpr bool operator==(const ShaderProgram &rhs) const noexcept
bool add(const ShaderCodeRef &shaderCode) noexcept
Adds a new shader to this program.
bool replaceShader(GL &gl, const ShaderCodeRef &oldShader, const ShaderCodeRef &newShader, bool verbose=false)
Replace a shader in a program and re-links the program.
bool contains(const ShaderCodeRef &shaderCode) const noexcept
constexpr bool inUse() const noexcept
bool validateProgram(GL &gl, bool verbose=false) noexcept
Performs GL2ES2#glValidateProgram(int) via ShaderUtil#isProgramExecStatusValid(GL,...
ShaderCodeRef getShader(size_t id) noexcept
Warning slow O(n) operation .
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 isProgramExecStatusValid(GL &gl, GLuint programObj, bool verbose=false) noexcept
Performs GL2ES2#glValidateProgram(int).
static bool isProgramLinkStatusValid(GL &gl, GLuint programObj, bool verbose=false) noexcept
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:112
std::ostream & operator<<(std::ostream &os, const T v)
std::shared_ptr< ShaderProgram > ShaderProgramRef
std::shared_ptr< ShaderCode > ShaderCodeRef
@ verbose
Verbose operations (debugging).
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
Definition Gamp.hpp:29
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
STL namespace.
std::size_t operator()(gamp::render::gl::glsl::ShaderProgramRef const &a) const noexcept
std::size_t operator()(gamp::render::gl::glsl::ShaderProgram const &a) const noexcept