Gamp v0.0.8
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/io/file_util.hpp>
20#include <jau/io/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> ShaderProgramSRef;
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 ShaderProgramSRef 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 ShaderCodeSRef& shaderCode) noexcept {
121 auto res = m_allShaderCode.insert(shaderCode);
122 return res.second;
123 }
124
125 bool contains(const ShaderCodeSRef& 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 ShaderCodeSRef 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 ShaderCodeSRef& 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 ShaderCodeSRef& oldShader, const ShaderCodeSRef& 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 jau_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());
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<ShaderCodeSRef> 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::ShaderProgramSRef> {
367 std::size_t operator()(gamp::render::gl::glsl::ShaderProgramSRef const& a) const noexcept {
368 return a->hash_code();
369 }
370 };
371
372 /**@}*/
373}
374
375#endif // GAMP_GLSLSHADERPROGRAM_HPP_
376
constexpr std::size_t hash_code() const noexcept
void unUseProgram(const GL &) noexcept
Disabled the shader program.
ShaderCodeSRef getShader(size_t id) noexcept
Warning slow O(n) operation .
bool init(const GL &) noexcept
Creates the empty GL program object using GL2ES2#glCreateProgram(), if not already created.
static ShaderProgramSRef create() noexcept
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.
bool replaceShader(GL &gl, const ShaderCodeSRef &oldShader, const ShaderCodeSRef &newShader, bool verbose=false)
Replace a shader in a program and re-links the program.
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(const ShaderCodeSRef &shaderCode) noexcept
Adds a new shader to this program.
bool contains(const ShaderCodeSRef &shaderCode) const noexcept
void useProgram(const GL &, bool on)
Enables or disabled the shader program.
constexpr bool operator==(const ShaderProgram &rhs) const noexcept
constexpr bool inUse() const noexcept
bool validateProgram(GL &gl, bool verbose=false) noexcept
Performs GL2ES2#glValidateProgram(int) via ShaderUtil#isProgramExecStatusValid(GL,...
bool add(GL &gl, const ShaderCodeSRef &shaderCode, bool verbose=false)
Adds a new shader to a this non running program.
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 jau_PLAIN_PRINT(printPrefix, fmt,...)
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.hpp:171
#define jau_ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:152
std::ostream & operator<<(std::ostream &os, const T v)
#define E_FILE_LINE
std::shared_ptr< ShaderCode > ShaderCodeSRef
std::shared_ptr< ShaderProgram > ShaderProgramSRef
@ verbose
Verbose operations (debugging).
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
Definition PTS.hpp:24
STL namespace.
std::size_t operator()(gamp::render::gl::glsl::ShaderProgramSRef const &a) const noexcept
std::size_t operator()(gamp::render::gl::glsl::ShaderProgram const &a) const noexcept