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