GCC Code Coverage Report


Directory: ./
File: lib/geogram_gfx/basic/GLSL.cpp
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 0 611 0.0%
Functions: 0 28 0.0%
Branches: 0 1092 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram_gfx/basic/GLSL.h>
41 #include <geogram/basic/logger.h>
42 #include <geogram/basic/command_line.h>
43 #include <cstdarg>
44 #include <cstdio>
45
46 #ifdef __clang__
47 # pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
48 #endif
49
50 #ifdef GEO_OS_EMSCRIPTEN
51 # define GEO_THROW_GLSL_ERROR
52 #else
53 # define GEO_THROW_GLSL_ERROR throw GLSL::GLSLCompileError();
54 #endif
55
56 namespace {
57
58 using namespace GEO;
59
60 /**
61 * \brief Loads the content of an ASCII file in a buffer.
62 * \details Memory ownership is transfered
63 * to the caller. Memory should be deallocated with
64 * delete[].
65 * \param[in] filename the name of the file
66 * \return a pointer to a buffer that contains the
67 * contents of the file.
68 */
69 char* load_ASCII_file(const char* filename) {
70 FILE* f = fopen(filename, "rt") ;
71 if(!f) {
72 Logger::err("GLSL")
73 << "Could not open file: \'"
74 << filename << "\'" << std::endl;
75 return nullptr ;
76 }
77 /*
78 * An easy way of determining the length of a file:
79 * Go to the end of the file, and ask where we are.
80 */
81 fseek(f, 0, SEEK_END) ;
82 size_t size = size_t(ftell(f)) ;
83
84 /* Let's go back to the beginning of the file */
85 fseek(f, 0, SEEK_SET) ;
86
87 char* result = new char[size+1] ;
88 size_t read_size = fread(result, 1, size, f);
89 if(read_size != size) {
90 Logger::warn("GLSL")
91 << "Could not read completely file \'"
92 << filename << "\'" << std::endl;
93 }
94 result[size] = '\0' ;
95 fclose(f) ;
96 return result ;
97 }
98
99 /**
100 * \brief Links a GLSL program and displays errors if any.
101 * \details If errors where encountered, program is deleted
102 * and reset to zero.
103 * \param[in,out] program the handle to the GLSL program
104 */
105 void link_program_and_check_status(GLuint& program) {
106 glLinkProgram(program);
107 GLint link_status;
108 glGetProgramiv(program, GL_LINK_STATUS, &link_status);
109 if(!link_status) {
110 GLchar linker_message[4096];
111 glGetProgramInfoLog(
112 program, sizeof(linker_message), nullptr, linker_message
113 );
114 Logger::err("GLSL") << "linker status :"
115 << link_status << std::endl;
116 Logger::err("GLSL") << "linker message:"
117 << linker_message << std::endl;
118 if(!CmdLine::get_arg_bool("dbg:gfx")) {
119 glDeleteProgram(program);
120 program = 0;
121 }
122 }
123 if(CmdLine::get_arg_bool("dbg:gfx")) {
124 GLSL::introspect_program(program);
125 }
126 }
127
128 /**
129 * \brief Dumps a shader source assembled from multiple strings
130 * and displays line numbers.
131 * \details This function can be used to debug shaders that do
132 * not compile, it makes error tracking easier.
133 * \param[in] sources a pointer to an array of strings
134 * \param[in] nb_sources the number of strings
135 */
136 void dump_program_source_with_line_numbers(
137 const char** sources, index_t nb_sources
138 ) {
139 std::string all_sources;
140 for(index_t i=0; i<nb_sources; ++i) {
141 all_sources += sources[i];
142 }
143 std::vector<std::string> lines;
144 String::split_string(all_sources, '\n', lines);
145 bool prev_is_include = false;
146 for(index_t i=0; i<lines.size(); ++i) {
147 std::string line = lines[i];
148 if(line.find("//import") != std::string::npos) {
149 if(prev_is_include) {
150 line = "";
151 } else {
152 line = "// [...skipped //import directives...]";
153 }
154 prev_is_include = true;
155 } else {
156 prev_is_include = false;
157 }
158 if(line.length() > 0 && line[line.length()-1] == '\n') {
159 line[line.length()-1] = ' ';
160 }
161 std::string line_number = String::to_string(i+1);
162 while(line_number.length() < 4) {
163 line_number += ' ';
164 }
165 Logger::out("GLSL") << line_number << " " << line << std::endl;
166 }
167 }
168 }
169
170 namespace GEO {
171
172 /***********************************************************************/
173
174 namespace GLSL {
175
176 void initialize() {
177 }
178
179 void terminate() {
180 }
181
182 /*************************************************************/
183
184 const char* GLSLCompileError::what() const GEO_NOEXCEPT {
185 return "GLSL Compile Error";
186 }
187
188 /*************************************************************/
189
190
191
192 /**
193 * \brief Parses in a string what looks like a version number.
194 * \param[in] version_string a const reference to the string
195 * \return the parsed version number, as a double precision floating
196 * point number.
197 */
198 static double find_version_number(const std::string& version_string) {
199 // The way the driver exposes the version of GLSL may differ,
200 // in some drivers the number comes in first position, in some
201 // others it comes in last position, therefore we take the first
202 // word that contains a valid number.
203 std::vector<std::string> version_words;
204 String::split_string(
205 version_string, ' ', version_words
206 );
207 double version = 0.0;
208 for(index_t i=0; i<version_words.size(); ++i) {
209 version = atof(version_words[i].c_str());
210 if(version != 0.0) {
211 break;
212 }
213 }
214 // Some drivers expose version 4.4 as 4.4 and some others
215 // as 440 !!
216 if(version > 100.0) {
217 version /= 100.0;
218 }
219 return version;
220 }
221
222 // If some drivers do not implement glGetString(GLSL_VERSION),
223 // then we can determine the GLSL version from the OpenGL version,
224 // may be more reliable...
225 //
226 //GLSL Version OpenGL Version
227 //1.10 2.0
228 //1.20 2.1
229 //1.30 3.0
230 //1.40 3.1
231 //1.50 3.2
232 //3.30 3.3
233 //4.00 4.0
234 //4.10 4.1
235 //4.20 4.2
236 //4.30 4.3
237 //4.40 4.4
238 //4.50 4.5
239
240 static double GLSL_version_from_OpenGL_version() {
241 const char* opengl_ver_str = (const char*)glGetString(GL_VERSION);
242 if(opengl_ver_str == nullptr) {
243 Logger::warn("GLSL")
244 << "glGetString(GL_VERSION)"
245 << " did not answer, falling back to VanillaGL"
246 << std::endl;
247 return 0.0;
248 }
249 double OpenGL_version = find_version_number(opengl_ver_str);
250
251 Logger::out("GLSL")
252 << "Determining GLSL version from OpenGL version"
253 << std::endl;
254
255 Logger::out("GLSL")
256 << "OpenGL version = " << OpenGL_version
257 << std::endl;
258
259 double GLSL_version = 0.0;
260 if(OpenGL_version >= 3.3) {
261 GLSL_version = OpenGL_version;
262 } else if(OpenGL_version == 2.0) {
263 GLSL_version = 1.1;
264 } else if(OpenGL_version == 2.1) {
265 GLSL_version = 1.2;
266 } else if(OpenGL_version == 3.0) {
267 GLSL_version = 1.3;
268 } else if(OpenGL_version == 3.1) {
269 GLSL_version = 1.4;
270 } else if(OpenGL_version == 3.2) {
271 GLSL_version = 1.5;
272 }
273
274 if(GLSL_version == 0.0) {
275 Logger::warn("GLSL") << "Could not determine GLSL version"
276 << std::endl;
277 } else {
278 Logger::out("GLSL") << "GLSL version = "
279 << GLSL_version
280 << std::endl;
281 }
282 return GLSL_version;
283 }
284
285
286 double supported_language_version() {
287
288 double GLSL_version = CmdLine::get_arg_double("gfx:GLSL_version");
289
290 if(GLSL_version != 0.0) {
291 Logger::out("GLSL") << "forced to version "
292 << GLSL_version
293 << " (gfx:GLSL_version)" << std::endl;
294 return GLSL_version;
295 }
296
297 const char* shading_language_ver_str = nullptr;
298
299 #ifdef GEO_GL_150
300 #ifndef GEO_OS_APPLE
301 // glGetStringi() is the new way of querying OpenGL implementation
302 if(glGetStringi) {
303 shading_language_ver_str = (const char*)glGetStringi(
304 GL_SHADING_LANGUAGE_VERSION, 0
305 );
306 // Intel driver has glGetStringi() but it does not seem
307 // to be implemented (triggers OpenGL errors). We make
308 // them silent. We use glGetString() below.
309 clear_gl_error_flags(__FILE__, __LINE__);
310 }
311 #endif
312 #endif
313 if(shading_language_ver_str == nullptr) {
314 // Some buggy drivers do not implement glGetStringi(),
315 // so I try also glGetString() (without the "i")
316 shading_language_ver_str =
317 (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
318 }
319
320 // If the driver does not implement glGetString neither
321 // glGetStringi with GL_SHADING_LANGUAGE_VERSION, then try
322 // to deduce it from OpenGL version.
323 if(shading_language_ver_str == nullptr) {
324 return GLSL_version_from_OpenGL_version();
325 }
326
327 const char* vendor = (const char*)glGetString(GL_VENDOR);
328
329 Logger::out("GLSL") << "vendor = " << vendor << std::endl;
330 Logger::out("GLSL") << "version string = "
331 << shading_language_ver_str << std::endl;
332
333
334 GLSL_version = find_version_number(shading_language_ver_str);
335 Logger::out("GLSL") << "version = " << GLSL_version
336 << std::endl;
337 return GLSL_version;
338 }
339
340
341 PseudoFileProvider::~PseudoFileProvider() {
342 }
343
344
345 GLuint compile_shader(
346 GLenum target, const char** sources, index_t nb_sources
347 ) {
348
349 if(CmdLine::get_arg_bool("gfx:GL_debug")) {
350 dump_program_source_with_line_numbers(sources, nb_sources);
351 }
352
353
354 GLuint s_handle = glCreateShader(target);
355 if(s_handle == 0) {
356 Logger::err("GLSL") << "Could not create shader for target"
357 << std::endl;
358 switch(target) {
359 case GL_VERTEX_SHADER:
360 Logger::err("GLSL") << " (target = GL_VERTEX_SHADER)"
361 << std::endl;
362 break;
363 case GL_FRAGMENT_SHADER:
364 Logger::err("GLSL")
365 << " (target = GL_FRAGMENT_SHADER)"
366 << std::endl;
367 break;
368 #ifdef GEO_GL_150
369 case GL_COMPUTE_SHADER:
370 Logger::err("GLSL") << " (target = GL_COMPUTE_SHADER)"
371 << std::endl;
372 break;
373 case GL_TESS_CONTROL_SHADER:
374 Logger::err("GLSL") << " (target = GL_TESS_CONTROL_SHADER)"
375 << std::endl;
376 break;
377 case GL_TESS_EVALUATION_SHADER:
378 Logger::err("GLSL")
379 << " (target = GL_TESS_EVALUATION_SHADER)"
380 << std::endl;
381 break;
382 case GL_GEOMETRY_SHADER:
383 Logger::err("GLSL")
384 << " (target = GL_GEOMETRY_SHADER)"
385 << std::endl;
386 break;
387 #endif
388 default:
389 Logger::err("GLSL")
390 << " (unknown target)"
391 << std::endl;
392 break;
393 }
394 GEO_THROW_GLSL_ERROR;
395 }
396 glShaderSource(s_handle, (GLsizei)nb_sources, sources, nullptr);
397 glCompileShader(s_handle);
398 GLint compile_status;
399 glGetShaderiv(s_handle, GL_COMPILE_STATUS, &compile_status);
400 if(!compile_status) {
401 GLchar compiler_message[4096];
402 glGetShaderInfoLog(
403 s_handle, sizeof(compiler_message), nullptr,
404 compiler_message
405 );
406
407 Logger::out("GLSL") << "Error in program:"
408 << std::endl;
409
410 if(CmdLine::get_arg_bool("gfx:GL_debug")) {
411 dump_program_source_with_line_numbers(sources, nb_sources);
412 }
413
414 Logger::err("GLSL")
415 << "compiler status :"
416 << compile_status << std::endl;
417 Logger::err("GLSL")
418 << "compiler message:" << '\n'
419 << compiler_message << std::endl;
420
421 glDeleteShader(s_handle);
422 s_handle = 0;
423 GEO_THROW_GLSL_ERROR;
424 }
425 return s_handle;
426 }
427
428 GLuint compile_shader(
429 GLenum target,
430 const char* source1,
431 const char* source2,
432 const char* source3,
433 const char* source4,
434 const char* source5,
435 const char* source6,
436 const char* source7,
437 const char* source8,
438 const char* source9,
439 const char* source10,
440 const char* source11,
441 const char* source12,
442 const char* source13,
443 const char* source14,
444 const char* source15,
445 const char* source16,
446 const char* source17,
447 const char* source18,
448 const char* source19,
449 const char* source20
450 ) {
451 vector<const char*> sources;
452 geo_assert(source1 != nullptr);
453 if(source1 != nullptr) {
454 sources.push_back(source1);
455 }
456 if(source2 != nullptr) {
457 sources.push_back(source2);
458 }
459 if(source3 != nullptr) {
460 sources.push_back(source3);
461 }
462 if(source4 != nullptr) {
463 sources.push_back(source4);
464 }
465 if(source5 != nullptr) {
466 sources.push_back(source5);
467 }
468 if(source6 != nullptr) {
469 sources.push_back(source6);
470 }
471 if(source7 != nullptr) {
472 sources.push_back(source7);
473 }
474 if(source8 != nullptr) {
475 sources.push_back(source8);
476 }
477 if(source9 != nullptr) {
478 sources.push_back(source9);
479 }
480 if(source10 != nullptr) {
481 sources.push_back(source10);
482 }
483 if(source11 != nullptr) {
484 sources.push_back(source11);
485 }
486 if(source12 != nullptr) {
487 sources.push_back(source12);
488 }
489 if(source13 != nullptr) {
490 sources.push_back(source13);
491 }
492 if(source14 != nullptr) {
493 sources.push_back(source14);
494 }
495 if(source15 != nullptr) {
496 sources.push_back(source15);
497 }
498 if(source16 != nullptr) {
499 sources.push_back(source16);
500 }
501 if(source17 != nullptr) {
502 sources.push_back(source17);
503 }
504 if(source18 != nullptr) {
505 sources.push_back(source18);
506 }
507 if(source19 != nullptr) {
508 sources.push_back(source19);
509 }
510 if(source20 != nullptr) {
511 sources.push_back(source20);
512 }
513
514 if(CmdLine::get_arg_bool("dbg:gfx")) {
515 std::ofstream out("last_shader.glsl");
516
517 for(index_t i=0; i<sources.size(); ++i) {
518 out << sources[i];
519 }
520
521 Logger::out("GLSL") << "===== Shader source ===="
522 << std::endl;
523
524 dump_program_source_with_line_numbers(
525 &sources[0], sources.size()
526 );
527 }
528
529 return compile_shader(target, &sources[0], sources.size());
530 }
531
532
533 void link_program(GLuint program) {
534 link_program_and_check_status(program);
535 if(program == 0) {
536 GEO_THROW_GLSL_ERROR;
537 }
538 }
539
540 GLuint create_program_from_shaders_no_link(GLuint shader1, ...) {
541 va_list args;
542 GLuint program = glCreateProgram();
543 va_start(args,shader1);
544 GLuint shader = shader1;
545 while(shader != 0) {
546 glAttachShader(program, shader);
547 shader = va_arg(args, GLuint);
548 }
549 va_end(args);
550 return program;
551 }
552
553 GLuint create_program_from_shaders(GLuint shader1, ...) {
554 va_list args;
555 GLuint program = glCreateProgram();
556 va_start(args,shader1);
557 GLuint shader = shader1;
558 while(shader != 0) {
559 glAttachShader(program, shader);
560 shader = va_arg(args, GLuint);
561 }
562 va_end(args);
563 link_program_and_check_status(program);
564 return program;
565 }
566
567 /*****************************************************************/
568
569 GLuint create_program_from_string_no_link(
570 const char* string_in, bool copy_string
571 ) {
572 GLuint program = glCreateProgram();
573
574 // string will be temporarily modified (to insert '\0' markers)
575 // but will be restored to its original state right after.
576 char* string = const_cast<char*>(string_in);
577 if(copy_string) {
578 string = strdup(string_in);
579 }
580
581 char* src = string;
582
583 bool err_flag = false;
584
585 for(;;) {
586 char* begin = strstr(src, "#BEGIN(");
587 char* end = strstr(src, "#END(");
588
589 if(begin == nullptr && end == nullptr) {
590 break;
591 }
592
593 if(begin == nullptr) {
594 Logger::err("GLSL") << "missing #BEGIN() statement"
595 << std::endl;
596 err_flag = true;
597 break;
598 }
599
600 if(end == nullptr) {
601 Logger::err("GLSL") << "missing #END() statement"
602 << std::endl;
603 err_flag = true;
604 break;
605 }
606
607
608 if(begin > end) {
609 Logger::err("GLSL") << "#END() before #BEGIN()"
610 << std::endl;
611 err_flag = true;
612 break;
613 }
614
615 char* begin_opening_brace = begin + strlen("#BEGIN");
616 char* end_opening_brace = end + strlen("#END");
617
618 char* begin_closing_brace = strchr(begin_opening_brace,')');
619 char* end_closing_brace = strchr(end_opening_brace, ')');
620
621 if(begin_closing_brace == nullptr) {
622 Logger::err("GLSL") << "#BEGIN: missing closing brace"
623 << std::endl;
624 err_flag = true;
625 break;
626 }
627
628 if(end_closing_brace == nullptr) {
629 Logger::err("GLSL") << "#END: missing closing brace"
630 << std::endl;
631 err_flag = true;
632 break;
633 }
634
635 std::string begin_kw(
636 begin_opening_brace+1,
637 size_t((begin_closing_brace - begin_opening_brace) - 1)
638 );
639 std::string end_kw(
640 end_opening_brace+1,
641 size_t((end_closing_brace - end_opening_brace) - 1)
642 );
643 if(end_kw != begin_kw) {
644 Logger::err("GLSL")
645 << "Mismatch: #BEGIN(" << begin_kw
646 << ") / #END(" << end_kw << ")"
647 << std::endl;
648 err_flag = true;
649 break;
650 }
651
652 // Replace '#END(...)' with string end marker
653 *end = '\0';
654
655 GLenum shader_type = GLenum(0);
656 if(begin_kw == "GL_VERTEX_SHADER") {
657 shader_type = GL_VERTEX_SHADER;
658 } else if(begin_kw == "GL_FRAGMENT_SHADER") {
659 shader_type = GL_FRAGMENT_SHADER;
660 }
661 #ifdef GEO_GL_150
662 else if(begin_kw == "GL_GEOMETRY_SHADER") {
663 shader_type = GL_GEOMETRY_SHADER;
664 } else if(begin_kw == "GL_TESS_CONTROL_SHADER") {
665 shader_type = GL_TESS_CONTROL_SHADER;
666 } else if(begin_kw == "GL_TESS_EVALUATION_SHADER") {
667 shader_type = GL_TESS_EVALUATION_SHADER;
668 }
669 #endif
670 else {
671 Logger::err("GLSL") << begin_kw
672 << ": No such shader type"
673 << std::endl;
674 err_flag = true;
675 break;
676 }
677
678 src = begin_closing_brace+1;
679 GLuint shader = 0;
680 try {
681 shader = compile_shader(shader_type, src, nullptr);
682 } catch(...) {
683 err_flag = true;
684 break;
685 }
686 glAttachShader(program, shader);
687
688 // Restore '#END(...)' statement
689 // ('#' was replaced with string end marker).
690 *end = '#';
691
692 src = end_closing_brace + 1;
693 }
694
695 if(copy_string) {
696 free(string);
697 }
698
699 if(err_flag) {
700 glDeleteProgram(program);
701 return 0;
702 }
703 return program;
704 }
705
706 /*****************************************************************/
707
708 GLuint create_program_from_file_no_link(const std::string& filename) {
709 char* buffer = load_ASCII_file(filename.c_str());
710 if(buffer == nullptr) {
711 return 0;
712 }
713 GLuint result = 0;
714 #ifdef GEO_OS_EMSCRIPTEN
715 result = create_program_from_string_no_link(buffer,false);
716 #else
717 try {
718 // last argument to false:
719 // no need to copy the buffer, we know it
720 // is not a string litteral.
721 result = create_program_from_string_no_link(buffer,false);
722 } catch(...) {
723 delete[] buffer;
724 throw;
725 }
726 #endif
727 return result;
728 }
729
730 /*****************************************************************/
731
732 GLint GEOGRAM_GFX_API get_uniform_variable_offset(
733 GLuint program, const char* varname
734 ) {
735 #ifndef GEO_GL_150
736 geo_argused(program);
737 geo_argused(varname);
738 return -1;
739 #else
740 GLuint index = GL_INVALID_INDEX;
741 glGetUniformIndices(program, 1, &varname, &index);
742 if(index == GL_INVALID_INDEX) {
743 Logger::err("GLUP")
744 << varname
745 << ":did not find uniform state variable"
746 << std::endl;
747 GEO_THROW_GLSL_ERROR;
748 }
749 geo_assert(index != GL_INVALID_INDEX);
750 GLint offset = -1;
751 glGetActiveUniformsiv(
752 program, 1, &index, GL_UNIFORM_OFFSET, &offset
753 );
754 geo_assert(offset != -1);
755 return offset;
756 #endif
757 }
758
759 size_t get_uniform_variable_array_stride(
760 GLuint program, const char* varname
761 ) {
762 #ifndef GEO_GL_150
763 geo_argused(program);
764 geo_argused(varname);
765 return size_t(-1);
766 #else
767 GLuint index = GL_INVALID_INDEX;
768 glGetUniformIndices(program, 1, &varname, &index);
769 if(index == GL_INVALID_INDEX) {
770 Logger::err("GLUP")
771 << varname
772 << ":did not find uniform state variable"
773 << std::endl;
774 GEO_THROW_GLSL_ERROR;
775 }
776 geo_assert(index != GL_INVALID_INDEX);
777 GLint stride = -1;
778 glGetActiveUniformsiv(
779 program, 1, &index, GL_UNIFORM_ARRAY_STRIDE, &stride
780 );
781 geo_assert(stride != -1);
782 return size_t(stride);
783 #endif
784 }
785
786 void introspect_program(GLuint program) {
787 Logger::out("GLSL") << "Program " << program << " introspection:"
788 << std::endl;
789 if(!glIsProgram(program)) {
790 Logger::out("GLSL") << " not a program !"
791 << std::endl;
792 return;
793 }
794
795 {
796 GLint link_status;
797 glGetProgramiv(program, GL_LINK_STATUS, &link_status);
798 Logger::out("GLSL") << " link status=" << link_status
799 << std::endl;
800 }
801
802 {
803 GLint active_attributes;
804 glGetProgramiv(
805 program, GL_ACTIVE_ATTRIBUTES, &active_attributes
806 );
807 Logger::out("GLSL")
808 << " active attributes=" << active_attributes
809 << std::endl;
810 for(GLuint i=0; i<GLuint(active_attributes); ++i) {
811 GLsizei length;
812 GLint size;
813 GLenum type;
814 GLchar name[1024];
815 glGetActiveAttrib(
816 program, i, GLsizei(1024), &length, &size, &type, name
817 );
818 Logger::out("GLSL") << " Attribute " << i << " : "
819 << name
820 << std::endl;
821 }
822 }
823
824 {
825 GLint active_uniforms;
826 glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniforms);
827 Logger::out("GLSL") << " active uniforms=" << active_uniforms
828 << std::endl;
829 for(GLuint i=0; i<GLuint(active_uniforms); ++i) {
830 GLsizei length;
831 GLint size;
832 GLenum type;
833 GLchar name[1024];
834 glGetActiveUniform(
835 program, i, GLsizei(1024), &length, &size, &type, name
836 );
837 Logger::out("GLSL") << " Uniform " << i << " : "
838 << name
839 << std::endl;
840 }
841 }
842
843 #ifdef GEO_GL_150
844 {
845 GLint active_uniform_blocks;
846 glGetProgramiv(
847 program, GL_ACTIVE_UNIFORM_BLOCKS, &active_uniform_blocks
848 );
849 Logger::out("GLSL") << " active uniform blocks="
850 << active_uniform_blocks
851 << std::endl;
852 }
853 #endif
854
855 }
856 }
857 }
858
859 /***************** GLSL pseudo file system ****************************/
860
861 namespace GEO {
862 namespace GLSL {
863 namespace {
864
865 /**
866 * \brief Gets all pseudo file names included by a GLSL source.
867 * \param[in] source the GLSL source
868 * \param[out] includes a vector of string with all included pseudo
869 * file names.
870 */
871 void get_includes(
872 const char* source, std::vector<std::string>& includes
873 ) {
874 includes.clear();
875 const char* cur = source;
876 while(cur != nullptr) {
877 cur = strstr(cur, "//import");
878 if(cur == nullptr) {
879 return;
880 }
881 cur += 8;
882 while(*cur == ' ') {
883 ++cur;
884 if(*cur == '\0') {
885 return;
886 }
887 }
888 if(*cur != '<') {
889 continue;
890 }
891 const char* next = strchr(cur, '>');
892 if(next != nullptr) {
893 includes.push_back(
894 std::string(cur+1, size_t(next-cur-1))
895 );
896 }
897 cur = next;
898 }
899 }
900
901 /**
902 * \brief A representation of a GLSL file in the pseudo file
903 * system.
904 */
905 struct File {
906
907 /**
908 * \brief File default constructor.
909 */
910 File() {
911 text = nullptr;
912 pseudo_file = nullptr;
913 }
914
915 /**
916 * \brief The name of the file in the pseudo file system.
917 */
918 std::string name;
919
920 /**
921 * \brief The content of the file, or nullptr if it is a pseudo
922 * file.
923 */
924 const char* text;
925
926 /**
927 * \brief A pointer to the function that generates the file
928 * contents if it is a pseudo file, or nullptr if it is a
929 * regular file.
930 */
931 PseudoFile pseudo_file;
932
933 /**
934 * \brief All the included files (directly or indirectly)
935 * in the order they should be grouped to form the source.
936 */
937 std::vector<File*> depends;
938 };
939
940 typedef std::map<std::string, File> FileSystem;
941
942 FileSystem file_system_;
943
944
945
946 /**
947 * \brief Gets the dependencies of a given file.
948 */
949 void get_depends(File& F) {
950 // Does nothing for pseudo files.
951 if(F.text == nullptr) {
952 return;
953 }
954
955 std::vector<std::string> include_names;
956 get_includes(F.text, include_names);
957 std::vector<File*> includes(include_names.size());
958
959 for(size_t i=0; i<include_names.size(); ++i) {
960 FileSystem::iterator it = file_system_.find(
961 include_names[i]
962 );
963 if(it == file_system_.end()) {
964 Logger::err("GLSL")
965 << F.name << " : include file "
966 << include_names[i]
967 << " not found in GLSL pseudo file system"
968 << std::endl;
969 geo_assert_not_reached;
970 }
971 includes[i] = &(it->second);
972 }
973 std::set<File*> included;
974 for(size_t inc=0; inc<includes.size(); ++inc) {
975 File* include = includes[inc];
976 for(size_t dep=0; dep<include->depends.size(); ++dep) {
977 File* depend = include->depends[dep];
978 if(included.find(depend) == included.end()) {
979 included.insert(depend);
980 F.depends.push_back(depend);
981 }
982 }
983 if(included.find(include) != included.end()) {
984 Logger::err("GLSL")
985 << F.name << " : include file "
986 << include_names[inc]
987 << " circularly included"
988 << std::endl;
989 geo_assert_not_reached;
990 }
991 included.insert(include);
992 F.depends.push_back(include);
993 }
994 }
995 }
996 }
997 }
998
999 namespace GEO {
1000 namespace GLSL {
1001
1002 void register_GLSL_include_file(
1003 const std::string& name, const char* source
1004 ) {
1005 geo_assert(file_system_.find(name) == file_system_.end());
1006 File& F = file_system_[name];
1007 F.name = name;
1008 F.text = source;
1009 F.pseudo_file = nullptr;
1010 get_depends(F);
1011 }
1012
1013 void register_GLSL_include_file(
1014 const std::string& name, PseudoFile file
1015 ) {
1016 geo_assert(file_system_.find(name) == file_system_.end());
1017 File& F = file_system_[name];
1018 F.name = name;
1019 F.text = nullptr;
1020 F.pseudo_file = file;
1021 }
1022
1023
1024 const char* get_GLSL_include_file(
1025 const std::string& name
1026 ) {
1027 FileSystem::iterator it = file_system_.find(name);
1028 if(it == file_system_.end()) {
1029
1030 for(FileSystem::iterator jt = file_system_.begin();
1031 jt != file_system_.end(); ++jt) {
1032 Logger::err("GLSL") << "FileSystem has: " << jt->first
1033 << std::endl;
1034 }
1035
1036
1037 Logger::err("GLSL")
1038 << name
1039 << " : not found in GLSL pseudo file system"
1040 << std::endl;
1041 geo_assert_not_reached;
1042 }
1043 if(it->second.text == nullptr) {
1044 Logger::err("GLSL")
1045 << name
1046 << " : is a pseudo-file"
1047 << std::endl;
1048 geo_assert_not_reached;
1049 }
1050 return it->second.text;
1051 }
1052
1053 GLuint compile_shader_with_includes(
1054 GLenum target, const char* source, PseudoFileProvider* provider
1055 ) {
1056 // TODO: if an import directive is right in the middle of the source,
1057 // push the source parts and the imported files in the correct order
1058 // (for now, imported files are necessarily at the beginning of the
1059 // source).
1060
1061 File F;
1062 F.text = source;
1063 get_depends(F);
1064
1065 std::vector<Source> sources;
1066 std::vector<const char*> sources_texts;
1067
1068 for(size_t dep=0; dep<F.depends.size(); ++dep) {
1069 if(F.depends[dep]->pseudo_file != nullptr) {
1070 F.depends[dep]->pseudo_file(provider,sources);
1071 } else {
1072 sources.push_back(F.depends[dep]->text);
1073 }
1074 }
1075 sources.push_back(source);
1076
1077 sources_texts.resize(sources.size());
1078 for(size_t i=0; i<sources.size(); ++i) {
1079 sources_texts[i] = sources[i].text();
1080 }
1081
1082 #ifndef GEO_OS_EMSCRIPTEN
1083 // If GL_debug is set, save shaders to file
1084 // It makes it easier testing and debugging
1085 // them with glslangValidator
1086 if(CmdLine::get_arg_bool("gfx:GL_debug")) {
1087 static int index = 0;
1088 ++index;
1089 std::string filename = String::format("shader_%03d",index);
1090 switch(target) {
1091 case GL_VERTEX_SHADER:
1092 filename += ".vert";
1093 break;
1094 case GL_TESS_CONTROL_SHADER:
1095 filename += ".tesc";
1096 break;
1097 case GL_TESS_EVALUATION_SHADER:
1098 filename += ".tese";
1099 break;
1100 case GL_GEOMETRY_SHADER:
1101 filename += ".geom";
1102 break;
1103 case GL_FRAGMENT_SHADER:
1104 filename += ".frag";
1105 break;
1106 case GL_COMPUTE_SHADER:
1107 filename += ".comp";
1108 break;
1109 default:
1110 filename += ".shader";
1111 break;
1112 }
1113
1114 std::ofstream out(filename.c_str());
1115 Logger::out("GLSLdbg") << "Saving shader " << filename << std::endl;
1116 for(index_t i=0; i<sources_texts.size(); ++i) {
1117 out << sources_texts[i];
1118 }
1119 }
1120 #endif
1121
1122 return compile_shader(
1123 target, &sources_texts[0], index_t(sources_texts.size())
1124 );
1125 }
1126
1127 GLuint compile_program_with_includes_no_link(
1128 PseudoFileProvider* provider,
1129 const char* shader1, const char* shader2, const char* shader3,
1130 const char* shader4, const char* shader5, const char* shader6
1131 ) {
1132 std::vector<const char*> sources;
1133
1134 GLuint program = glCreateProgram();
1135
1136 if(shader1 != nullptr) {
1137 sources.push_back(shader1);
1138 }
1139
1140 if(shader2 != nullptr) {
1141 sources.push_back(shader2);
1142 }
1143
1144 if(shader3 != nullptr) {
1145 sources.push_back(shader3);
1146 }
1147
1148 if(shader4 != nullptr) {
1149 sources.push_back(shader4);
1150 }
1151
1152 if(shader5 != nullptr) {
1153 sources.push_back(shader5);
1154 }
1155
1156 if(shader6 != nullptr) {
1157 sources.push_back(shader6);
1158 }
1159
1160 for(size_t i=0; i<sources.size(); ++i) {
1161 const char* p1 = strstr(sources[i], "//stage ");
1162 if(p1 == nullptr) {
1163 Logger::err("GLSL")
1164 << "Missing //stage GL_xxxxx declaration"
1165 << std::endl;
1166 GEO_THROW_GLSL_ERROR;
1167 }
1168 p1 += 8;
1169 const char* p2 = strchr(p1, '\n');
1170 if(p2 == nullptr) {
1171 Logger::err("GLSL")
1172 << "Missing CR in //stage GL_xxxxx declaration"
1173 << std::endl;
1174 GEO_THROW_GLSL_ERROR;
1175 }
1176 std::string stage_str(p1, size_t(p2-p1));
1177 GLenum stage = 0;
1178 if(stage_str == "GL_VERTEX_SHADER") {
1179 stage = GL_VERTEX_SHADER;
1180 } else if(stage_str == "GL_FRAGMENT_SHADER") {
1181 stage = GL_FRAGMENT_SHADER;
1182 }
1183
1184 #ifndef GEO_OS_EMSCRIPTEN
1185 else if(stage_str == "GL_GEOMETRY_SHADER") {
1186 stage = GL_GEOMETRY_SHADER;
1187 } else if(stage_str == "GL_TESS_CONTROL_SHADER") {
1188 stage = GL_TESS_CONTROL_SHADER;
1189 } else if(stage_str == "GL_TESS_EVALUATION_SHADER") {
1190 stage = GL_TESS_EVALUATION_SHADER;
1191 }
1192 #endif
1193 else {
1194 Logger::err("GLSL") << stage_str << ": unknown stage"
1195 << std::endl;
1196 GEO_THROW_GLSL_ERROR;
1197 }
1198
1199 GLuint shader =
1200 compile_shader_with_includes(stage, sources[i], provider);
1201
1202 glAttachShader(program, shader);
1203
1204 // It is reference-counted by OpenGL
1205 // (and it is attached to the program)
1206 glDeleteShader(shader);
1207
1208 }
1209 return program;
1210 }
1211
1212
1213
1214 }
1215 }
1216