Gamp v0.0.7-36-g24b1eb6
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
ShaderState.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_GLSLSHADERSTATE_HPP_
13#define GAMP_GLSLSHADERSTATE_HPP_
14
15#include <jau/basic_types.hpp>
16#include <jau/debug.hpp>
17#include <jau/file_util.hpp>
18#include <jau/io_util.hpp>
19#include <jau/string_util.hpp>
20
21#include <algorithm>
22#include <cstddef>
23#include <gamp/Gamp.hpp>
28
29namespace gamp::render::gl::glsl {
30 using namespace gamp::render::gl;
31 using namespace gamp::render::gl::data;
32
33 /** \addtogroup Gamp_GLSL
34 *
35 * @{
36 */
37
38 /**
39 * ShaderState allows to sharing data between shader programs,
40 * while updating the attribute and uniform locations when switching.
41 * <p>
42 * This allows seamless switching of programs using <i>almost</i> same data
43 * but performing different artifacts.
44 * </p>
45 * <p>
46 * A {@link #useProgram(GL2ES2, boolean) used} ShaderState is attached to the current GL context
47 * and can be retrieved via {@link #getShaderState(GL)}.
48 * </p>
49 */
51 public:
52 static bool DEBUG_STATE;
53
54 ShaderState() noexcept = default;
55
56 bool verbose() const noexcept { return DEBUG_STATE || m_verbose; }
57
58 void setVerbose(bool v) noexcept { m_verbose = v; }
59
60 /** Returns the attached user object for the given name. */
61 AttachableRef getAttachedObject(std::string_view key) const { return m_attachables.get(key); }
62
63 /** Clears the attachment map. */
64 void clearAttachedObjects() { m_attachables.clear(); }
65
66 /**
67 * Attaches user object for the given name, overwrites old mapping if exists.
68 * @return previously set object or nullptr.
69 */
70 AttachableRef attachObject(std::string_view key, const AttachableRef& obj) { return m_attachables.put(key, obj); }
71
72 /** Removes attached object if exists and returns it, otherwise returns nullptr. */
73 AttachableRef detachObject(std::string_view key) { return m_attachables.remove(key); }
74
75 /**
76 * Turns the shader program on or off
77 *
78 * @throws GLException if no program is attached, linkage or useProgram fails
79 *
80 * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean)
81 */
82 void useProgram(GL& gl, bool on) {
83 if(!m_shaderProgram) { throw RenderException("No program is attached", E_FILE_LINE); }
84 if(on) {
85 if(m_shaderProgram->linked()) {
86 m_shaderProgram->useProgram(gl, true);
87 if(m_resetAllShaderData) {
88 resetAllAttributes(gl);
89 resetAllUniforms(gl);
90 }
91 } else {
92 if(m_resetAllShaderData) {
93 setAllAttributes(gl);
94 }
95 if(!m_shaderProgram->link(gl, verbose())) {
96 throw RenderException("could not link program: "+m_shaderProgram->toString(), E_FILE_LINE);
97 }
98 m_shaderProgram->useProgram(gl, true);
99 if(m_resetAllShaderData) {
100 resetAllUniforms(gl);
101 }
102 }
103 m_resetAllShaderData = false;
104 } else {
105 m_shaderProgram->useProgram(gl, false);
106 }
107 }
108
109 /** Returns true if the shaderProgram() is linked, see ShaderProgram::linked(). */
110 bool linked() const noexcept {
111 return m_shaderProgram ? m_shaderProgram->linked() : false;
112 }
113
114 /** Returns true if the shaderProgram() is in use, see ShaderProgram::inUse(). */
115 bool inUse() const noexcept {
116 return m_shaderProgram ? m_shaderProgram->inUse() : false;
117 }
118
119 /**
120 * Attach or switch a shader program
121 *
122 * <p>Attaching a shader program the first time,
123 * as well as switching to another program on the fly,
124 * while managing all attribute and uniform data.</p>
125 *
126 * <p>[Re]sets all data and use program in case of a program switch.</p>
127 *
128 * <p>Use program, {@link #useProgram(GL2ES2, boolean)},
129 * if <code>enable</code> is <code>true</code>.</p>
130 *
131 * @return true if shader program was attached, otherwise false (already attached)
132 *
133 * @throws GLException if program was not linked and linking fails
134 */
135 bool attachShaderProgram(GL& gl, const ShaderProgramRef& prog, bool enable) {
136 if(verbose()) {
137 const size_t curId = m_shaderProgram ? m_shaderProgram->id() : 0;
138 const size_t newId = prog ? prog->id() : 0;
139 jau::INFO_PRINT("ShaderState: attachShaderProgram: %zu -> %zu (enable: %d)\n\t%s\n\t%s",
140 curId, newId, enable,
141 (m_shaderProgram ? m_shaderProgram->toString().c_str() : "null"),
142 (prog ? prog->toString().c_str() : "null"));
143 }
144 if(m_shaderProgram) {
145 if(m_shaderProgram == prog) {
146 if(enable) {
147 useProgram(gl, true);
148 }
149 // nothing else to do ..
150 if(verbose()) {
151 jau::INFO_PRINT("ShaderState: attachShaderProgram: No switch, equal id: %zu , enabling %d",
152 m_shaderProgram->id(), enable);
153 }
154 return false;
155 }
156 if(m_shaderProgram->inUse()) {
157 if(prog && enable) {
158 m_shaderProgram->notifyNotInUse();
159 } else {
160 // no new 'enabled' program - disable
161 useProgram(gl, false);
162 }
163 }
164 m_resetAllShaderData = true;
165 }
166
167 // register new one
168 m_shaderProgram = prog;
169
170 if(m_shaderProgram) {
171 // [re]set all data and use program if switching program,
172 // or use program if program is linked
173 if(m_resetAllShaderData || enable) {
174 useProgram(gl, true); // may reset all data
175 if(!enable) {
176 useProgram(gl, false);
177 }
178 }
179 }
180 if(verbose()) {
181 jau::INFO_PRINT("Info: attachShaderProgram: END");
182 }
183 return true;
184 }
185
186 /** Returns the attachedShaderProgram() or nullptr. */
187 const ShaderProgramRef& shaderProgram() const noexcept { return m_shaderProgram; }
188
189 /**
190 * Calls {@link #release(GL2ES2, boolean, boolean, boolean) release(gl, true, true, true)}
191 *
192 * @see #glReleaseAllVertexAttributes
193 * @see #glReleaseAllUniforms
194 * @see #release(GL2ES2, boolean, boolean, boolean)
195 */
196 void destroy(GL& gl) {
197 release(gl, true, true, true);
199 }
200
201 /**
202 * Calls {@link #release(GL2ES2, boolean, boolean, boolean) release(gl, false, false, false)}
203 *
204 * @see #glReleaseAllVertexAttributes
205 * @see #glReleaseAllUniforms
206 * @see #release(GL2ES2, boolean, boolean, boolean)
207 */
209 release(gl, false, false, false);
210 }
211
212 /**
213 * @see #glReleaseAllVertexAttributes
214 * @see #glReleaseAllUniforms
215 * @see ShaderProgram#release(GL2ES2, boolean)
216 */
217 void release(GL& gl, bool destroyBoundAttributes, bool destroyShaderProgram, bool destroyShaderCode) {
218 if(m_shaderProgram && m_shaderProgram->linked() ) {
219 m_shaderProgram->useProgram(gl, false);
220 }
221 if(destroyBoundAttributes) {
222 for(GLArrayDataRef& iter : m_managedAttributes) {
223 iter->destroy(gl);
224 }
225 }
228 if(m_shaderProgram && destroyShaderProgram) {
229 m_shaderProgram->release(gl, destroyShaderCode);
230 }
231 }
232
233 //
234 // Shader attribute handling
235 //
236
237 /**
238 * Gets the cached location of a shader attribute.
239 *
240 * @return -1 if there is no such attribute available,
241 * otherwise >= 0
242 *
243 * @see #bindAttribLocation(GL2ES2, int, String)
244 * @see #bindAttribLocation(GL2ES2, int, GLArrayData)
245 * @see #getAttribLocation(GL2ES2, String)
246 * @see GL2ES2#glGetAttribLocation(int, String)
247 */
249 return m_activeAttribLocationMap.get(name);
250 }
251
252 /**
253 * Get the previous cached vertex attribute data.
254 *
255 * @return the GLArrayData object, null if not previously set.
256 *
257 * @see #ownAttribute(GLArrayData, boolean)
258 *
259 * @see #glEnableVertexAttribArray
260 * @see #glDisableVertexAttribArray
261 * @see #glVertexAttribPointer
262 * @see #getVertexAttribPointer
263 * @see #glReleaseAllVertexAttributes
264 * @see #glResetAllVertexAttributes
265 * @see ShaderProgram#glReplaceShader
266 */
268 return m_activeAttribDataMap.get(name);
269 }
270
271 bool isActiveAttribute(const GLArrayDataRef& attribute) const {
272 return attribute == m_activeAttribDataMap.get(attribute->name());
273 }
274
275 /**
276 * Binds or unbinds the {@link GLArrayData} lifecycle to this ShaderState.
277 *
278 * If an attribute location is cached (ie {@link #bindAttribLocation(GL2ES2, int, String)})
279 * it is promoted to the {@link GLArrayData} instance.</p>
280 *
281 * The attribute will be destroyed with {@link #destroy(GL2ES2)}
282 * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.
283 *
284 * The data will not be transfered to the GPU, use {@link #vertexAttribPointer(GL2ES2, GLArrayData)} additionally.
285 *
286 * The data will also be {@link GLArrayData#associate(Object, boolean) associated} with this ShaderState.
287 *
288 * Always issue ownAttribute() before GLArrayDataClient::seal() or GLArrayDataClient::enableBuffer(),
289 * allowing it to fetch the attribute location via this ShaderState instance to render it functional.
290 *
291 * @param attribute the {@link GLArrayData} which lifecycle shall be managed
292 * @param own true if <i>owning</i> shall be performs, false if <i>disowning</i>.
293 *
294 * @see #bindAttribLocation(GL2ES2, int, String)
295 * @see #getAttribute(String)
296 * @see GLArrayData#associate(Object, boolean)
297 */
298 void ownAttribute(const GLArrayDataRef& attr, bool own) {
299 if(own) {
300 const GLint location = getCachedAttribLocation(attr->name());
301 if(0<=location) {
302 attr->setLocation(location);
303 }
304 m_managedAttributes.push_back(attr);
305 } else {
306 std::erase(m_managedAttributes, attr);
307 }
308 attr->associate(*this, own);
309 }
310
311 bool ownsAttribute(const GLArrayDataRef& attribute) const {
312 return m_managedAttributes.end() != std::find(m_managedAttributes.begin(), m_managedAttributes.end(), attribute);
313 }
314
315 /**
316 * Binds a shader attribute to a location.
317 * Multiple names can be bound to one location.
318 * The value will be cached and can be retrieved via {@link #getCachedAttribLocation(String)}
319 * before or after linking.
320 *
321 * @throws GLException if no program is attached or program is already linked
322 *
323 * @see com.jogamp.opengl.GL2ES2#glBindAttribLocation(int, int, String)
324 * @see #getAttribLocation(GL2ES2, String)
325 * @see #getCachedAttribLocation(String)
326 */
327 void bindAttribLocation(const GL&, GLint location, const string_t& name) {
328 if(!m_shaderProgram) throw RenderException("No program is attached", E_FILE_LINE);
329 if(m_shaderProgram->linked()) throw RenderException("Program is already linked", E_FILE_LINE);
330 m_activeAttribLocationMap.put(name, location);
331 ::glBindAttribLocation(m_shaderProgram->program(), location, name.c_str());
332 }
333
334 /**
335 * Binds a shader {@link GLArrayData} attribute to a location.
336 * Multiple names can be bound to one location.
337 * The value will be cached and can be retrieved via {@link #getCachedAttribLocation(String)}
338 * and {@link #getAttribute(String)}before or after linking.
339 * The {@link GLArrayData}'s location will be set as well.
340 *
341 * @throws GLException if no program is attached or program is already linked
342 *
343 * @see com.jogamp.opengl.GL2ES2#glBindAttribLocation(int, int, String)
344 * @see #getAttribLocation(GL2ES2, String)
345 * @see #getCachedAttribLocation(String)
346 * @see #getAttribute(String)
347 */
348 void bindAttribLocation(const GL& gl, GLint location, const GLArrayDataRef& attr) {
349 if(!m_shaderProgram) throw RenderException("No program is attached", E_FILE_LINE);
350 if(m_shaderProgram->linked()) throw RenderException("Program is already linked", E_FILE_LINE);
351 const string_t& name = attr->name();
352 m_activeAttribLocationMap.put(name, location);
353 attr->setLocation(gl, m_shaderProgram->program(), location);
354 m_activeAttribDataMap.put(name, attr);
355 ::glBindAttribLocation(m_shaderProgram->program(), location, name.c_str());
356 }
357
358 /**
359 * Gets the location of a shader attribute with given <code>name</code>.<br>
360 * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid,
361 * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br>
362 * The location will be cached.
363 *
364 * @return -1 if there is no such attribute available,
365 * otherwise >= 0
366 * @throws GLException if no program is attached
367 * @throws GLException if the program is not linked and no location was cached.
368 *
369 * @see #getCachedAttribLocation(String)
370 * @see #bindAttribLocation(GL2ES2, int, GLArrayData)
371 * @see #bindAttribLocation(GL2ES2, int, String)
372 * @see GL2ES2#glGetAttribLocation(int, String)
373 */
374 GLint getAttribLocation(const GL&, const string_t& name) {
375 if(!m_shaderProgram) throw RenderException("No program is attached", E_FILE_LINE);
376 GLint location = getCachedAttribLocation(name);
377 if(0>location) {
378 if(!m_shaderProgram->linked()) throw RenderException("Program is not linked", E_FILE_LINE);
379 location = ::glGetAttribLocation(m_shaderProgram->program(), name.c_str());
380 if(0<=location) {
381 m_activeAttribLocationMap.put(name, location);
382 if(verbose()) {
383 jau::INFO_PRINT("ShaderState: glGetAttribLocation: %s, loc: %d", name.c_str(), location);
384 }
385 } else if(verbose()) {
386 jau::INFO_PRINT("ShaderState: glGetAttribLocation failed, no location for: %s, loc: %d", name.c_str(), location);
387 }
388 }
389 return location;
390 }
391
392 /**
393 * Validates and returns the location of a shader attribute.<br>
394 * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid,
395 * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br>
396 * The location will be cached and set in the
397 * {@link GLArrayData} object.
398 *
399 * @return -1 if there is no such attribute available,
400 * otherwise >= 0
401 *
402 * @throws GLException if no program is attached
403 * @throws GLException if the program is not linked and no location was cached.
404 *
405 * @see #getCachedAttribLocation(String)
406 * @see #bindAttribLocation(GL2ES2, int, GLArrayData)
407 * @see #bindAttribLocation(GL2ES2, int, String)
408 * @see GL2ES2#glGetAttribLocation(int, String)
409 * @see #getAttribute(String)
410 */
411 GLint getAttribLocation(const GL& gl, const GLArrayDataRef& data) {
412 if(!m_shaderProgram) throw RenderException("No program is attached", E_FILE_LINE);
413 const string_t& name = data->name();
414 GLint location = getCachedAttribLocation(name);
415 if(0<=location) {
416 data->setLocation(location);
417 } else {
418 if(!m_shaderProgram->linked()) throw RenderException("Program is not linked", E_FILE_LINE);
419 location = data->setLocation(gl, m_shaderProgram->program());
420 if(0<=location) {
421 m_activeAttribLocationMap.put(name, location);
422 if(verbose()) {
423 jau::INFO_PRINT("ShaderState: glGetAttribLocation: %s, loc: %d", data->name().c_str(), location);
424 }
425 } else if(verbose()) {
426 jau::INFO_PRINT("ShaderState: glGetAttribLocation failed, no location for: %s", data->name().c_str());
427 }
428 }
429 m_activeAttribDataMap.put(data->name(), data);
430 return location;
431 }
432
433 //
434 // Enabled Vertex Arrays and its data
435 //
436
437 /**
438 * @return true if the named attribute is enable
439 */
441 return m_enabledAttribDataMap.get(name);
442 }
443
444 /**
445 * @return true if the {@link GLArrayData} attribute is enable
446 */
448 return isVertexAttribArrayEnabled(data->name());
449 }
450
451 private:
452 bool enableVertexAttribArray(const GL& gl, const string_t& name, GLint location) {
453 m_enabledAttribDataMap.put(name, true);
454 if(0>location) {
455 location = getAttribLocation(gl, name);
456 if(0>location) {
457 if(verbose()) {
458 jau::INFO_PRINT("ShaderState: glEnableVertexAttribArray failed, no location for: %s", name.c_str());
459 }
460 return false;
461 }
462 }
463 if(verbose()) {
464 jau::INFO_PRINT("ShaderState: glEnableVertexAttribArray: %s, loc: %d", name.c_str(), location);
465 }
466 ::glEnableVertexAttribArray(location);
467 return true;
468 }
469
470 public:
471 /**
472 * Enables a vertex attribute array.
473 *
474 * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)}
475 * hence {@link #enableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred.
476 *
477 * Even if the attribute is not found in the current shader,
478 * it is marked enabled in this state.
479 *
480 * @return false, if the name is not found, otherwise true
481 *
482 * @throws GLException if the program is not linked and no location was cached.
483 *
484 * @see #glEnableVertexAttribArray
485 * @see #glDisableVertexAttribArray
486 * @see #glVertexAttribPointer
487 * @see #getVertexAttribPointer
488 */
490 return enableVertexAttribArray(gl, name, -1);
491 }
492
493
494 /**
495 * Enables a vertex attribute array, usually invoked by {@link GLArrayDataEditable#enableBuffer(GL, boolean)}.
496 *
497 * This method uses the {@link GLArrayData}'s location if set
498 * and is the preferred alternative to {@link #enableVertexAttribArray(GL2ES2, String)}.
499 * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set
500 * and cached in this state.
501 *
502 * Even if the attribute is not found in the current shader,
503 * it is marked enabled in this state.
504 *
505 * @return false, if the name is not found, otherwise true
506 *
507 * @throws GLException if the program is not linked and no location was cached.
508 *
509 * @see #glEnableVertexAttribArray
510 * @see #glDisableVertexAttribArray
511 * @see #glVertexAttribPointer
512 * @see #getVertexAttribPointer
513 * @see GLArrayDataEditable#enableBuffer(GL, boolean)
514 */
516 if(0 > data->location()) {
518 } else {
519 // ensure data is the current bound one
520 m_activeAttribDataMap.put(data->name(), data);
521 }
522 return enableVertexAttribArray(gl, data->name(), data->location());
523 }
524
525 private:
526 bool disableVertexAttribArray(const GL& gl, const string_t& name, GLint location) {
527 m_enabledAttribDataMap.put(name, false);
528 if(0>location) {
529 location = getAttribLocation(gl, name);
530 if(0>location) {
531 if(verbose()) {
532 jau::INFO_PRINT("ShaderState: glDisableVertexAttribArray failed, no location for: %s", name.c_str());
533 }
534 return false;
535 }
536 }
537 if(verbose()) {
538 jau::INFO_PRINT("ShaderState: glDisableVertexAttribArray: %s, %d", name.c_str(), location);
539 }
540 ::glDisableVertexAttribArray(location);
541 return true;
542 }
543
544 public:
545 /**
546 * Disables a vertex attribute array
547 *
548 * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)}
549 * hence {@link #disableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred.
550 *
551 * Even if the attribute is not found in the current shader,
552 * it is removed from this state enabled list.
553 *
554 * @return false, if the name is not found, otherwise true
555 *
556 * @throws GLException if no program is attached
557 * @throws GLException if the program is not linked and no location was cached.
558 *
559 * @see #glEnableVertexAttribArray
560 * @see #glDisableVertexAttribArray
561 * @see #glVertexAttribPointer
562 * @see #getVertexAttribPointer
563 */
565 return disableVertexAttribArray(gl, name, -1);
566 }
567
568 /**
569 * Disables a vertex attribute array
570 *
571 * This method uses the {@link GLArrayData}'s location if set
572 * and is the preferred alternative to {@link #disableVertexAttribArray(GL2ES2, String)}.
573 * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set
574 * and cached in this state.
575 *
576 * Even if the attribute is not found in the current shader,
577 * it is removed from this state enabled list.
578 *
579 * @return false, if the name is not found, otherwise true
580 *
581 * @throws GLException if no program is attached
582 * @throws GLException if the program is not linked and no location was cached.
583 *
584 * @see #glEnableVertexAttribArray
585 * @see #glDisableVertexAttribArray
586 * @see #glVertexAttribPointer
587 * @see #getVertexAttribPointer
588 */
590 if(0 > data->location()) {
592 }
593 return disableVertexAttribArray(gl, data->name(), data->location());
594 }
595
596 /**
597 * Set the {@link GLArrayData} vertex attribute data, if it's location is valid, i.e. &ge; 0.
598 * <p>
599 * This method uses the {@link GLArrayData}'s location if valid, i.e. &ge; 0.<br/>
600 * If data's location is invalid, it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)},
601 * set and cached in this state.
602 * </p>
603 *
604 * @return false, if the location could not be determined, otherwise true
605 *
606 * @throws GLException if no program is attached
607 * @throws GLException if the program is not linked and no location was cached.
608 *
609 * @see #glEnableVertexAttribArray
610 * @see #glDisableVertexAttribArray
611 * @see #glVertexAttribPointer
612 * @see #getVertexAttribPointer
613 */
615 GLint location = data->location();
616 if(0 > location) {
617 location = getAttribLocation(gl, data);
618 }
619 if(0>location) {
620 if(verbose()) {
621 jau::INFO_PRINT("ShaderState: glVertexAttribPointer failed, no location for: %s", data->name().c_str());
622 }
623 return false;
624 }
625 // only pass the data, if the attribute exists in the current shader
626 if(verbose()) {
627 jau::INFO_PRINT("ShaderState: glVertexAttribPointer: %s, location %d", data->name().c_str(), location);
628 }
629 data->glVertexAttribPointer(gl);
630 return true;
631 }
632
633 /**
634 * Releases all mapped vertex attribute data,
635 * disables all enabled attributes and loses all indices
636 *
637 * @see #glEnableVertexAttribArray
638 * @see #glDisableVertexAttribArray
639 * @see #glVertexAttribPointer
640 * @see #getVertexAttribPointer
641 * @see #glReleaseAllVertexAttributes
642 * @see #glResetAllVertexAttributes
643 * @see #glResetAllVertexAttributes
644 * @see ShaderProgram#glReplaceShader
645 */
647 if(m_shaderProgram) {
648 for (const std::pair<const std::string, GLArrayDataRef>& n : m_activeAttribDataMap.map()) {
649 disableVertexAttribArray(gl, n.second);
650 }
651 }
652 m_activeAttribDataMap.clear();
653 m_enabledAttribDataMap.clear();
654 m_activeAttribLocationMap.clear();
655 m_managedAttributes.clear();
656 }
657
658 /**
659 * Disables all vertex attribute arrays.
660 *
661 * Their enabled stated will be removed from this state only
662 * if 'removeFromState' is true.
663 *
664 * This method purpose is more for debugging.
665 *
666 * @see #glEnableVertexAttribArray
667 * @see #glDisableVertexAttribArray
668 * @see #glVertexAttribPointer
669 * @see #getVertexAttribPointer
670 * @see #glReleaseAllVertexAttributes
671 * @see #glResetAllVertexAttributes
672 * @see #glResetAllVertexAttributes
673 * @see ShaderProgram#glReplaceShader
674 */
675 void disableAllVertexAttributeArrays(const GL& gl, bool removeFromState) {
676 for (const std::pair<const std::string, bool>& n : m_enabledAttribDataMap.map()) {
677 const string_t& name = n.first;
678 if(removeFromState) {
679 m_enabledAttribDataMap.remove(name);
680 }
681 const GLint index = getAttribLocation(gl, name);
682 if(0<=index) {
683 ::glDisableVertexAttribArray(index);
684 }
685 }
686 }
687
688 private:
689 void relocateAttribute(const GL& gl, GLArrayData& attribute) {
690 // get new location .. note: 'activeAttribLocationMap' is cleared before
691 const string_t& name = attribute.name();
692 const GLint loc = attribute.setLocation(gl, m_shaderProgram->program());
693 if(0<=loc) {
694 m_activeAttribLocationMap.put(name, loc);
695 if(verbose()) {
696 jau::INFO_PRINT("ShaderState: relocateAttribute: %s, loc: %d", attribute.toString().c_str(), loc);
697 }
699 // enable attrib, VBO and pass location/data
700 ::glEnableVertexAttribArray(loc);
701 }
702
703 if( attribute.isVBO() ) {
704 ::glBindBuffer(GL_ARRAY_BUFFER, attribute.vboName());
705 attribute.glVertexAttribPointer(gl);
706 ::glBindBuffer(GL_ARRAY_BUFFER, 0);
707 } else {
708 attribute.glVertexAttribPointer(gl);
709 }
710 }
711 }
712
713 /**
714 * Reset all previously enabled mapped vertex attribute data.
715 *
716 * <p>
717 * Attribute data is bound to the GL state, i.e. VBO data itself will not be updated.
718 * </p>
719 *
720 * <p>
721 * Attribute location and it's data assignment is bound to the program,
722 * hence both are updated.
723 * </p>
724 *
725 * <p>
726 * Note: Such update could only be prevented,
727 * if tracking am attribute/program dirty flag.
728 * </p>
729 *
730 * @throws GLException is the program is not linked
731 *
732 * @see #attachShaderProgram(GL2ES2, ShaderProgram)
733 */
734 void resetAllAttributes(const GL& gl) {
735 if(!m_shaderProgram->linked()) throw RenderException("Program is not linked", E_FILE_LINE);
736 m_activeAttribLocationMap.clear();
737
738 for(GLArrayDataRef& ad : m_managedAttributes) {
739 ad->setLocation(-1);
740 }
741 for (const std::pair<const std::string, GLArrayDataRef>& n : m_activeAttribDataMap.map()) {
742 relocateAttribute(gl, *n.second);
743 }
744 }
745
746 void setAttribute(const GL& gl, const GLArrayData& attribute) {
747 // get new location ..
748 const string_t& name = attribute.name();
749 const GLint loc = attribute.location();
750
751 if(0<=loc) {
752 bindAttribLocation(gl, loc, name);
753
755 // enable attrib, VBO and pass location/data
756 ::glEnableVertexAttribArray(loc);
757 }
758
759 if( attribute.isVBO() ) {
760 ::glBindBuffer(GL_ARRAY_BUFFER, attribute.vboName());
761 attribute.glVertexAttribPointer(gl);
762 ::glBindBuffer(GL_ARRAY_BUFFER, 0);
763 } else {
764 attribute.glVertexAttribPointer(gl);
765 }
766 }
767 }
768
769 /**
770 * preserves the attribute location .. (program not linked)
771 */
772 void setAllAttributes(const GL& gl) {
773 for (const std::pair<const std::string, GLArrayDataRef>& n : m_activeAttribDataMap.map()) {
774 setAttribute(gl, *n.second);
775 }
776 }
777
778 public:
779 //
780 // Shader Uniform handling
781 //
782
783 /**
784 * Gets the cached location of the shader uniform.
785 *
786 * @return -1 if there is no such uniform available,
787 * otherwise >= 0
788 */
790 return m_activeUniformLocationMap.get(name);
791 }
792
793 /**
794 * Bind the {@link GLUniform} lifecycle to this ShaderState.
795 *
796 * If a uniform location is cached it is promoted to the {@link GLUniformData} instance.
797 *
798 * The uniform will be destroyed with {@link #destroy(GL2ES2)}
799 * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.
800 *
801 * The data will not be transfered to the GPU, use pushUniform() additionally.
802 *
803 * @param uniform the {@link GLUniformData} which lifecycle shall be managed
804 *
805 * @see #getUniform(String)
806 */
807 void ownUniform(const GLUniformDataRef& data, bool own) {
808 if(own) {
809 const GLint location = getCachedUniformLocation(data->name());
810 if(0<=location) {
811 data->setLocation(location);
812 }
813 m_activeUniformDataMap.put(data->name(), data);
814 m_managedUniforms.push_back(data);
815 } else {
816 m_activeUniformDataMap.remove(data->name());
817 std::erase(m_managedUniforms, data);
818 }
819 }
820
821 bool ownsUniform(const GLUniformDataRef& uniform) {
822 return m_managedUniforms.end() != std::find(m_managedUniforms.begin(), m_managedUniforms.end(), uniform);
823 }
824
825 /**
826 * Gets the location of a shader uniform with given <code>name</code>.<br>
827 * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid,
828 * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br>
829 * The location will be cached.
830 * <p>
831 * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)})
832 * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p>
833 *
834 * @return -1 if there is no such attribute available,
835 * otherwise >= 0
836
837 * @throws GLException is the program is not linked
838 *
839 * @see #glGetUniformLocation
840 * @see com.jogamp.opengl.GL2ES2#glGetUniformLocation
841 * @see #getUniformLocation
842 * @see ShaderProgram#glReplaceShader
843 */
844 GLint getUniformLocation(const GL&, const string_t& name) {
845 if(!m_shaderProgram->inUse()) throw RenderException("Program is not in use", E_FILE_LINE);
846 GLint location = getCachedUniformLocation(name);
847 if(0>location) {
848 if(!m_shaderProgram->linked()) throw RenderException("Program is not linked", E_FILE_LINE);
849 location = ::glGetUniformLocation(m_shaderProgram->program(), name.c_str());
850 if(0<=location) {
851 m_activeUniformLocationMap.put(name, location);
852 } else if(verbose()) {
853 jau::INFO_PRINT("ShaderState: glUniform failed, no location for: %s", name.c_str());
854 }
855 }
856 return location;
857 }
858
859 /**
860 * Validates and returns the location of a shader uniform.<br>
861 * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid,
862 * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br>
863 * The location will be cached and set in the
864 * {@link GLUniformData} object.
865 * <p>
866 * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)})
867 * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p>
868 *
869 * @return -1 if there is no such attribute available,
870 * otherwise >= 0
871
872 * @throws GLException is the program is not linked
873 *
874 * @see #glGetUniformLocation
875 * @see com.jogamp.opengl.GL2ES2#glGetUniformLocation
876 * @see #getUniformLocation
877 * @see ShaderProgram#glReplaceShader
878 */
880 if(!m_shaderProgram->inUse()) throw RenderException("Program is not in use", E_FILE_LINE);
881 const string_t& name = data->name();
882 GLint location = getCachedUniformLocation(name);
883 if(0<=location) {
884 data->setLocation(location);
885 } else {
886 if(!m_shaderProgram->linked()) throw RenderException("Program is not linked", E_FILE_LINE);
887 location = data->setLocation(gl, m_shaderProgram->program());
888 if(0<=location) {
889 m_activeUniformLocationMap.put(name, location);
890 } else if(verbose()) {
891 jau::INFO_PRINT("ShaderState: glUniform failed, no location for: %s", data->name().c_str());
892 }
893 }
894 m_activeUniformDataMap.put(name, data);
895 return location;
896 }
897
898 /**
899 * Set the uniform data, if it's location is valid, i.e. &ge; 0.
900 * <p>
901 * This method uses the {@link GLUniformData}'s location if valid, i.e. &ge; 0.<br/>
902 * If data's location is invalid, it will be retrieved via {@link #getUniformLocation(GL2ES2, GLUniformData)},
903 * set and cached in this state.
904 * </p>
905 *
906 * @return false, if the location could not be determined, otherwise true
907 *
908 * @see #glGetUniformLocation
909 * @see com.jogamp.opengl.GL2ES2#glGetUniformLocation
910 * @see com.jogamp.opengl.GL2ES2#glUniform
911 * @see #getUniformLocation
912 * @see ShaderProgram#glReplaceShader
913 */
914 bool pushUniform(const GL& gl, const GLUniformDataRef& data) {
915 if(!m_shaderProgram->inUse()) throw RenderException("Program is not in use", E_FILE_LINE);
916 GLint location = data->location();
917 if(0>location) {
918 location = getUniformLocation(gl, data);
919 }
920 if(0>location) {
921 if(verbose()) {
922 jau::INFO_PRINT("ShaderState: glUniform failed, no location for: %s", data->name().c_str());
923 }
924 return false;
925 }
926 // only pass the data, if the uniform exists in the current shader
927 if(verbose()) {
928 jau::INFO_PRINT("ShaderState: glUniform: %s, location %d", data->name().c_str(), location);
929 }
930 data->send(gl);
931 return true;
932 }
933 /** Same as pushUniform(), but retrieves GLUniformDataRef by name first. */
934 bool pushUniform(const GL& gl, const stringview_t& name) {
935 const GLUniformDataRef& data = m_activeUniformDataMap.get(name);
936 return pushUniform(gl, data);
937 }
938 /** Same as pushUniform(), but for all active uniforms. */
939 void pushAllUniforms(const GL& gl) {
940 if(!m_shaderProgram->inUse()) throw RenderException("Program is not in use", E_FILE_LINE);
941 for (const std::pair<const std::string, GLUniformDataRef>& n : m_activeUniformDataMap.map()) {
942 const GLUniformDataRef& data = n.second;
944 }
945 }
946
947 /**
948 * Get the uniform data, previously set.
949 *
950 * @return the GLUniformData object, null if not previously set.
951 */
953 return m_activeUniformDataMap.get(name);
954 }
955
956 /**
957 * Releases all mapped uniform data
958 * and loses all indices
959 */
961 m_activeUniformDataMap.clear();
962 m_activeUniformLocationMap.clear();
963 m_managedUniforms.clear();
964 }
965
966 private:
967 /**
968 * Reset all previously mapped uniform data
969 * <p>
970 * Uniform data and location is bound to the program,
971 * hence both are updated.
972 * </p>
973 * <p>
974 * Note: Such update could only be prevented,
975 * if tracking a uniform/program dirty flag.
976 * </p>
977 *
978 * @throws GLException is the program is not in use
979 *
980 * @see #attachShaderProgram(GL2ES2, ShaderProgram)
981 */
982 void resetAllUniforms(const GL& gl) {
983 if(!m_shaderProgram->inUse()) throw RenderException("Program is not in use", E_FILE_LINE);
984 m_activeUniformLocationMap.clear();
985 for(const GLUniformDataRef& u : m_managedUniforms) {
986 u->setLocation(-1);
987 }
988 for (const std::pair<const std::string, GLUniformDataRef>& n : m_activeUniformDataMap.map()) {
989 const GLUniformDataRef& data = n.second;
990 GLint loc = data->setLocation(gl, m_shaderProgram->program());
991 if( 0 <= loc ) {
992 // only pass the data, if the uniform exists in the current shader
993 m_activeUniformLocationMap.put(data->name(), loc);
994 if(verbose()) {
995 jau::INFO_PRINT("ShaderState: resetAllUniforms: %s", data->name().c_str());
996 }
997 data->send(gl);
998 }
999 }
1000 }
1001
1002 public:
1003 string_t toString(bool alsoUnlocated=DEBUG_STATE) const {
1004 string_t sb;
1005 sb.append("ShaderState[\n ");
1006 if(m_shaderProgram) {
1007 sb.append(m_shaderProgram->toString());
1008 } else {
1009 sb.append("ShaderProgram: null");
1010 }
1011 sb.append("\n").append(" enabledAttributes [");
1012 for (const std::pair<const std::string, bool>& n : m_enabledAttribDataMap.map()) {
1013 sb.append("\n ").append(n.first).append(": ").append(n.second?"enabled":"disabled");
1014 }
1015 sb.append("\n ],").append(" activeAttributes [");
1016 for (const std::pair<const std::string, GLArrayDataRef>& n : m_activeAttribDataMap.map()) {
1017 if( alsoUnlocated || 0 <= n.second->location() ) {
1018 sb.append("\n ").append(n.second->toString());
1019 }
1020 }
1021 sb.append("\n ],").append(" managedAttributes [");
1022 for(const GLArrayDataRef& ad : m_managedAttributes) {
1023 if( alsoUnlocated || 0 <= ad->location() ) {
1024 sb.append("\n ").append(ad->toString());
1025 }
1026 }
1027 sb.append("\n ],").append(" activeUniforms [");
1028 for (const std::pair<const std::string, GLUniformDataRef>& n : m_activeUniformDataMap.map()) {
1029 const GLUniformDataRef& ud = n.second;
1030 if( alsoUnlocated || 0 <= ud->location() ) {
1031 sb.append("\n ").append(ud->toString());
1032 }
1033 }
1034 sb.append("\n ],").append(" managedUniforms [");
1035 for(const GLUniformDataRef& ud : m_managedUniforms) {
1036 if( alsoUnlocated || 0 <= ud->location() ) {
1037 sb.append("\n ").append(ud->toString());
1038 }
1039 }
1040 sb.append("\n ]").append("\n]");
1041 return sb;
1042 }
1043
1044 private:
1045 bool m_verbose = false;
1046 ShaderProgramRef m_shaderProgram = nullptr;
1047 bool m_resetAllShaderData = false;
1048
1049 StringHashMapWrap<bool, bool, false> m_enabledAttribDataMap;
1050 StringHashMapWrap<GLint, GLint, -1> m_activeAttribLocationMap;
1052 std::vector<GLArrayDataRef> m_managedAttributes;
1053
1054 StringHashMapWrap<GLint, GLint, -1> m_activeUniformLocationMap;
1056 std::vector<GLUniformDataRef> m_managedUniforms;
1057
1058 StringAttachables m_attachables;
1059 };
1060
1061 inline std::ostream& operator<<(std::ostream& out, const ShaderState& v) {
1062 return out << v.toString();
1063 }
1064
1065 /**@}*/
1066}
1067
1068#endif // GAMP_GLSLSHADERSTATE_HPP_
#define E_FILE_LINE
void clear()
Clears the hash map.
jau::StringHashMap< Value_type > & map() noexcept
Definition GampTypes.hpp:90
Value_type put(std::string_view key, const Value_type &obj)
Maps the value for the given name, overwrites old mapping if exists.
Interface for a generic data buffer to be used for OpenGL arrays.
virtual std::string toString() const noexcept
constexpr const std::string & name() const noexcept
The name of the reflecting shader array attribute.
void glVertexAttribPointer(const GL &) const noexcept
Associates the vboName() buffer as an vertex attribute on the GPU, or sends the data if !...
constexpr GLuint vboName() const noexcept
The VBO name or 0 if not a VBO.
constexpr bool isVBO() const noexcept
Determines whether the data is server side (VBO) and enabled, or a client side array (false).
constexpr GLint location() const noexcept
Returns the shader attribute location for this name, -1 if not yet determined.
void setLocation(GLint loc) noexcept
Sets the given location of the shader attribute.
ShaderState allows to sharing data between shader programs, while updating the attribute and uniform ...
AttachableRef attachObject(std::string_view key, const AttachableRef &obj)
Attaches user object for the given name, overwrites old mapping if exists.
void bindAttribLocation(const GL &, GLint location, const string_t &name)
Binds a shader attribute to a location.
bool vertexAttribPointer(const GL &gl, const GLArrayDataRef &data)
Set the GLArrayData vertex attribute data, if it's location is valid, i.e.
bool enableVertexAttribArray(const GL &gl, const string_t &name)
Enables a vertex attribute array.
bool pushUniform(const GL &gl, const GLUniformDataRef &data)
Set the uniform data, if it's location is valid, i.e.
bool isVertexAttribArrayEnabled(const GLArrayDataRef &data) const
bool ownsAttribute(const GLArrayDataRef &attribute) const
bool inUse() const noexcept
Returns true if the shaderProgram() is in use, see ShaderProgram::inUse().
GLint getAttribLocation(const GL &, const string_t &name)
Gets the location of a shader attribute with given name.
bool disableVertexAttribArray(const GL &gl, const string_t &name)
Disables a vertex attribute array.
bool pushUniform(const GL &gl, const stringview_t &name)
Same as pushUniform(), but retrieves GLUniformDataRef by name first.
bool isVertexAttribArrayEnabled(stringview_t name) const
bool isActiveAttribute(const GLArrayDataRef &attribute) const
GLint getCachedUniformLocation(const stringview_t &name)
Gets the cached location of the shader uniform.
void destroy(GL &gl)
Calls release(gl, true, true, true).
const GLUniformDataRef getUniform(const stringview_t &name)
Get the uniform data, previously set.
bool linked() const noexcept
Returns true if the shaderProgram() is linked, see ShaderProgram::linked().
void releaseAllData(GL &gl)
Calls release(gl, false, false, false).
void ownUniform(const GLUniformDataRef &data, bool own)
Bind the GLUniform lifecycle to this ShaderState.
void release(GL &gl, bool destroyBoundAttributes, bool destroyShaderProgram, bool destroyShaderCode)
void disableAllVertexAttributeArrays(const GL &gl, bool removeFromState)
Disables all vertex attribute arrays.
void bindAttribLocation(const GL &gl, GLint location, const GLArrayDataRef &attr)
Binds a shader GLArrayData attribute to a location.
void releaseAllUniforms()
Releases all mapped uniform data and loses all indices.
void clearAttachedObjects()
Clears the attachment map.
void useProgram(GL &gl, bool on)
Turns the shader program on or off.
void pushAllUniforms(const GL &gl)
Same as pushUniform(), but for all active uniforms.
void ownAttribute(const GLArrayDataRef &attr, bool own)
Binds or unbinds the GLArrayData lifecycle to this ShaderState.
GLint getAttribLocation(const GL &gl, const GLArrayDataRef &data)
Validates and returns the location of a shader attribute.
bool disableVertexAttribArray(const GL &gl, const GLArrayDataRef &data)
Disables a vertex attribute array.
bool ownsUniform(const GLUniformDataRef &uniform)
AttachableRef detachObject(std::string_view key)
Removes attached object if exists and returns it, otherwise returns nullptr.
GLArrayDataRef getAttribute(stringview_t name) const
Get the previous cached vertex attribute data.
bool enableVertexAttribArray(const GL &gl, const GLArrayDataRef &data)
Enables a vertex attribute array, usually invoked by GLArrayDataEditable#enableBuffer(GL,...
void releaseAllAttributes(const GL &gl)
Releases all mapped vertex attribute data, disables all enabled attributes and loses all indices.
AttachableRef getAttachedObject(std::string_view key) const
Returns the attached user object for the given name.
GLint getUniformLocation(const GL &gl, const GLUniformDataRef &data)
Validates and returns the location of a shader uniform.
string_t toString(bool alsoUnlocated=DEBUG_STATE) const
bool attachShaderProgram(GL &gl, const ShaderProgramRef &prog, bool enable)
Attach or switch a shader program.
GLint getUniformLocation(const GL &, const string_t &name)
Gets the location of a shader uniform with given name.
const ShaderProgramRef & shaderProgram() const noexcept
Returns the attachedShaderProgram() or nullptr.
GLint getCachedAttribLocation(const stringview_t &name) const
Gets the cached location of a shader attribute.
consteval_cxx20 std::string_view name() noexcept
std::ostream & operator<<(std::ostream &os, const T v)
std::shared_ptr< GLArrayData > GLArrayDataRef
std::shared_ptr< GLUniformData > GLUniformDataRef
std::shared_ptr< ShaderProgram > ShaderProgramRef
std::string_view stringview_t
std::shared_ptr< Attachable > AttachableRef
Definition GampTypes.hpp:79
StringHashMapWrap< AttachableRef, std::nullptr_t, nullptr > StringAttachables
void INFO_PRINT(const char *format,...) noexcept
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
Definition debug.cpp:248