cgv
Loading...
Searching...
No Matches
shader_code.cxx
1#include <cgv/base/base.h>
2#include "shader_code.h"
3#include <cgv/base/register.h>
4#include <cgv/base/import.h>
6#include <cgv/utils/tokenizer.h>
7#include <cgv/ppp/ph_processor.h>
8#include <cgv/utils/dir.h>
9#include <cgv/utils/file.h>
10#include <cgv/type/variant.h>
11
12#ifdef WIN32
13#pragma warning(disable:4996)
14#endif
15
16using namespace cgv::base;
17using namespace cgv::type;
18using namespace cgv::utils;
19
20namespace cgv {
21 namespace render {
22
23std::map<std::string, std::string> shader_code::code_cache;
24
25std::map<std::string, std::string> shader_code::shader_file_name_map;
26
28
34
36{
37 return "shader_config";
38}
39
42{
43 return
44 rh.reflect_member("shader_path", shader_path) &&
45 rh.reflect_member("show_file_paths", show_file_paths);
46}
47
50{
52 if (!config) {
54 if (getenv("CGV_SHADER_PATH"))
55 config->shader_path = getenv("CGV_SHADER_PATH");
56 else if (getenv("CGV_DIR"))
57 config->shader_path =
58 std::string(getenv("CGV_DIR"))+"/libs/cgv_gl/glsl;"+
59 std::string(getenv("CGV_DIR"))+"/libs/plot/glsl;"+
60 std::string(getenv("CGV_DIR")) + "/libs/cgv_app/glsl;" +
61 std::string(getenv("CGV_DIR")) + "/libs/cgv_g2d/glsl;" +
62 std::string(getenv("CGV_DIR")) + "/libs/cgv_gpgpu/glsl;" +
63 std::string(getenv("CGV_DIR")) + "/libs/holo_disp;" +
64 std::string(getenv("CGV_DIR")) + "/plugins/examples";
65 }
66 return config;
67}
68
69void shader_code::decode_if_base64(std::string& content) {
70//#ifdef _WIN32
71 if (!content.empty()) {
72 // test if the first character is equal to the base64 prefix (ANSI 'paragraph' char with hexcode A7)
73 if(content[0] == char(0xA7))
74 content = decode_base64(content.substr(1));
75 }
76/*#else
77 if (content.size() > 1) {
78 if ((uint8_t&)content[0]==0xC2 && (uint8_t&)content[1]==0xA7) // UTF-8 for '�'
79 content = decode_base64(content.substr(2));
80 else if (
81 content.size() > 2 &&
82 (int)content[0] == -17 &&
83 (int)content[1] == -65 &&
84 (int)content[2] == -67) {
85 content = decode_base64(content.substr(3));
86 }
87 }
88#endif*/
89}
90
93std::string shader_code::get_last_error(const std::string& file_name, const std::string& last_error)
94{
95 std::string fn = shader_code::find_file(file_name);
96 std::vector<line> lines;
98 std::string formated_error;
99 for (unsigned int i = 0; i<lines.size(); ++i) {
100 std::string l = to_string((const token&)(lines[i]));
101 if (to_upper(l.substr(0, 5)) == "ERROR") {
102 std::vector<token> toks;
103 tokenizer(l).set_sep(":").set_ws("").bite_all(toks);
104 formated_error += fn + "(";
105 if (toks.size() > 4) {
106 formated_error += to_string(toks[4])+") : error G0001: ";
107 if (toks.size() > 6)
108 formated_error += l.substr(toks[6].begin-&l[0]);
109 }
110 else {
111 formated_error += "1) : error G0000: ";
112 if (toks.size() > 2)
113 formated_error += l.substr(toks[2].begin-&l[0]);
114 }
115 formated_error += "\n";
116 }
117 else {
118 std::vector<token> toks;
119 tokenizer(l).set_sep(":()").set_ws("").bite_all(toks);
120 unsigned int i, j, k; int n;
121 for (i=0; i<toks.size(); ++i) {
122 if (to_string(toks[i]) == "(")
123 break;
124 }
125 for (j=0; j<toks.size(); ++j) {
126 if (to_string(toks[j]) == ")")
127 break;
128 }
129 for (k=0; k<toks.size(); ++k) {
130 if (to_string(toks[k]) == ":")
131 break;
132 }
133 if (k < toks.size() && i < j && j < k && is_integer(toks[i].end, toks[j].begin, n)) {
134 std::string _fn = fn;
135 int shader_id;
136 if (i > 0 && is_integer(toks[i-1].begin, toks[i-1].end, shader_id)) {
137 if (shader_id >= 1000) {
138 unsigned si = shader_id-1000;
139 if (si < get_shader_config()->inserted_shader_file_names.size())
140 _fn = get_shader_config()->inserted_shader_file_names[si];
141 }
142 else {
143 if ((unsigned)shader_id < get_shader_config()->shader_file_names.size())
144 _fn = get_shader_config()->shader_file_names[shader_id];
145 }
146 }
147 formated_error += _fn + l.substr(toks[i].begin - &l[0])+"\n";
148 }
149 else
150 formated_error += l+"\n";
151 }
152 }
153 return formated_error;
154}
155
158{
159 st = ST_DETECT;
160}
163{
164 if (ctx_ptr && ctx_ptr->make_current())
166 else
167 if (handle)
168 std::cerr << "shader code not destructed correctly" << std::endl;
169}
170
173{
174 if (!handle)
175 return;
176 ctx.shader_code_destruct(*this);
177 handle = 0;
178}
179
180std::string shader_code::find_file(const std::string& file_name, bool search_exhaustive)
181{
182 if (file_name.substr(0, 6) == "str://" || file_name.substr(0, 6) == "res://") {
183 std::map<std::string, resource_file_info>::const_iterator it = ref_resource_file_map().find(file_name.substr(6));
184 if (it != ref_resource_file_map().end())
185 return file_name;
186 else
187 return "";
188 }
189 if (file::exists(file_name))
190 return file_name;
191
192 std::string try_name = file::get_path(ref_prog_name()) + "/" + file_name;
193 if (file::exists(try_name))
194 return try_name;
195
196 std::map<std::string, resource_file_info>::const_iterator it =
197 ref_resource_file_map().find(file_name);
198 if (it != ref_resource_file_map().end()) {
199 if (it->second.file_offset == -1)
200 return std::string("str://") + file_name;
201 else
202 return std::string("res://") + file_name;
203 }
204 if (get_shader_config()->shader_path.empty()) {
205 try_name = std::string("glsl/") + file_name;
206 if (file::exists(try_name))
207 return try_name;
208 try_name = file::get_path(ref_prog_name()) + "/glsl/" + file_name;
209 if (file::exists(try_name))
210 return try_name;
211 return "";
212 }
213
215 std::string path_list = get_shader_config()->shader_path;
216
217 size_t pos = 0;
218 do {
219 size_t end_pos = path_list.find_first_of(';', pos);
220 std::string path;
221 if(end_pos == std::string::npos) {
222 path = path_list.substr(pos);
223 pos = path_list.length();
224 } else {
225 path = path_list.substr(pos, end_pos - pos);
226 pos = end_pos + 1;
227 }
228
229 std::vector<std::string> file_names;
230 dir::glob(path, file_names, "*gl*", true);
231
232 for(const auto& file_name : file_names) {
233 std::string ext = file::get_extension(file_name);
234 if(ext.length() > 2) {
235 if(ext[0] == 'g' && ext[1] == 'l' ||
236 ext[0] == 'p' && ext[1] == 'g' && ext[2] == 'l')
237 shader_file_name_map.emplace(file::get_file_name(file_name), file_name);
238 }
239 }
240 } while(pos < path_list.length());
241
243 }
244
245 std::map<std::string, std::string>::const_iterator file_name_map_it = shader_file_name_map.find(file_name);
247 try_name = file_name_map_it->second;
248 if(file::exists(try_name))
249 return try_name;
250 } else if(!search_exhaustive) {
251 return "";
252 }
253
254 return file::find_in_paths(file_name, get_shader_config()->shader_path, true);
255}
256
257std::string shader_code::retrieve_code(const std::string& file_name, bool use_cache, std::string* _last_error) {
258
259 std::string source = "";
260
261 if(use_cache) {
262 auto it = code_cache.find(file_name);
263 if(it != code_cache.end()) {
264 source = it->second;
265 }
266 } else {
267 code_cache.clear();
268 }
269
270 if(source.empty())
271 source = read_code_file(file_name, _last_error);
272
273 if(use_cache)
274 code_cache.emplace(file_name, source);
275
276 return source;
277}
278
279ShaderType shader_code::detect_shader_type(const std::string& file_name)
280{
281 std::string ext = to_lower(file::get_extension(file_name));
282 ShaderType st = ST_VERTEX;
283 if (ext == "glfs" || ext == "pglfs")
284 st = ST_FRAGMENT;
285 else if (ext == "glgs" || ext == "pglgs")
286 st = ST_GEOMETRY;
287 else if (ext == "glcs" || ext == "pglcs")
288 st = ST_COMPUTE;
289 else if (ext == "gltc" || ext == "pgltc")
290 st = ST_TESS_CONTROL;
291 else if (ext == "glte" || ext == "pglte")
292 st = ST_TESS_EVALUATION;
293 return st;
294}
295
296std::string shader_code::resolve_includes(const std::string& source, bool use_cache, std::set<std::string>& included_file_names, std::string* _last_error)
297{
298 const std::string identifier = "#include ";
299
300 std::string resolved_source = "";
301
302 std::vector<line> lines;
303 split_to_lines(source, lines);
304
305 for(size_t i = 0; i < lines.size(); ++i) {
306 std::string current_line = to_string(lines[i]);
307
308 // search for the include identifier
310 if(identifier_pos != std::string::npos) {
311 // remove identifier and all content before; an include directive must be the first and only statement on a line
312 current_line.erase(0, identifier_pos + identifier.length());
313
314 // trim whitespace
316
317 if(current_line.length() > 0) {
318 // remove quotation marks, leaving only the include path
319 std::string include_file_name = current_line.substr(1, current_line.length() - 2);
321
322 // check whether this file was already included and skip if this is the case
326 } else {
327 current_line = "";
328 }
329 }
330
331 // skip this line if nothing needs to be included (removes the include statement from the code)
332 if(current_line == "")
333 continue;
334 }
335
337 }
338
339 return resolved_source;
340}
341
342std::string shader_code::resolve_includes(const std::string& source, bool use_cache, std::string* _last_error) {
343 std::set<std::string> included_file_names;
344 return resolve_includes(source, use_cache, included_file_names, _last_error);
345}
346
347void shader_code::resolve_version(std::string& source) {
348 struct version_directive_t {
349 size_t position = 0;
350 size_t version_number = 0;
351 bool has_profile = false;
352 bool is_core_profile = false;
353 bool is_special_comment = false;
354 };
355
356 const std::string version_identifier = "#version";
357
358 std::vector<version_directive_t> version_directives;
359
360 // collect all version directives in source
361 size_t offset = 0;
362 while(true) {
363 offset = source.find(version_identifier, offset);
364 if(offset == std::string::npos) {
365 break;
366 } else {
367 version_directives.push_back({ offset });
368 offset += version_identifier.length();
369 }
370 }
371
372 // extract and set properties of each version directive
374 if(directive.position > 2) {
375 std::string prefix = source.substr(directive.position - 3, 3);
376 if(prefix == "//?" || prefix == "//!")
377 directive.is_special_comment = true;
378 }
379
380 size_t search_offset = directive.position + version_identifier.length();
381 size_t newline_pos = source.find('\n', search_offset);
382
383 std::string properties = cgv::utils::trim(source.substr(search_offset, newline_pos - search_offset));
384
385 std::vector<cgv::utils::token> tokens;
386 cgv::utils::split_to_tokens(properties, tokens, "", true, "", "", " \t");
387
388 if(!tokens.empty()) {
389 std::string number = to_string(tokens[0]);
390
391 if(!cgv::utils::from_string(directive.version_number, number))
392 directive.version_number = 0;
393 }
394
395 if(tokens.size() > 1) {
396 std::string profile = to_string(tokens[1]);
397 if(profile == "core") {
398 directive.has_profile = true;
399 directive.is_core_profile = true;
400 } else if(profile == "compatibility") {
401 directive.has_profile = true;
402 directive.is_core_profile = false;
403 }
404 }
405 }
406
407 // collect the highest version number and profile priority
410 if(directive.version_number > highest_version_directive.version_number) {
411 highest_version_directive.version_number = directive.version_number;
412 }
413
414 if(directive.has_profile) {
415 if(highest_version_directive.has_profile) {
416 if(directive.is_core_profile)
417 highest_version_directive.is_core_profile = true;
418 } else {
419 highest_version_directive.has_profile = true;
420 highest_version_directive.is_core_profile = directive.is_core_profile;
421 }
422 }
423 }
424
425 // disable all non-commented version directives by changing them to comments
426 for(auto it = version_directives.rbegin(); it != version_directives.rend(); ++it) {
428 if(!directive.is_special_comment)
429 source.insert(directive.position, "//");
430 }
431
432 // create and insert the new version directive as the first statement in the source
433 std::string version_str = "#version " + std::to_string(highest_version_directive.version_number);
434 if(highest_version_directive.has_profile) {
435 version_str += ' ';
436 version_str += highest_version_directive.is_core_profile ? "core" : "compatibility";
437 version_str += '\n';
438 }
439 source.insert(0, version_str);
440}
441
444{
445 return st;
446}
447
449std::string shader_code::read_code_file(const std::string &file_name, std::string* last_error)
450{
451 std::string source;
452 std::string fn = find_file(file_name);
453 if (fn.empty()) {
454 if (last_error) {
455 *last_error = "could not find shader file ";
456 *last_error += file_name;
457 }
458 return "";
459 }
460 if (!cgv::base::read_data_file(fn, source, true)) {
461 if (last_error) {
462 *last_error = "could not read shader file ";
463 *last_error += fn;
464 }
465 return "";
466 }
467 if (get_shader_config()->show_file_paths)
468 std::cout << "read shader code <" << fn << ">" << std::endl;
469
470 decode_if_base64(source);
471
472 if (file::get_extension(file_name)[0] == 'p') {
473 std::string code;
474 get_shader_config()->inserted_shader_file_names.clear();
475 std::string paths = file::get_path(fn);
476 if (!get_shader_config()->shader_path.empty())
477 paths = paths+";"+get_shader_config()->shader_path;
478
480 php.configure_insert_to_shader(&get_shader_config()->inserted_shader_file_names);
481 if (!php.parse_string(source))
482 return "";
483 if (!php.process_to_string(code))
484 return "";
485 cgv::ppp::clear_variables();
486 source = code;
487 }
488 return source;
489}
490
492bool shader_code::read_code(const context& ctx, const std::string &file_name, ShaderType st, const shader_compile_options& options)
493{
494 if (st == ST_DETECT)
495 st = detect_shader_type(file_name);
496
497 // get source code from cache or read file
498 std::string source = retrieve_code(file_name, ctx.is_shader_file_cache_enabled(), &last_error);
499
500 source = resolve_includes(source, ctx.is_shader_file_cache_enabled());
501
502 resolve_version(source);
503
504 set_defines_and_snippets(source, options);
505
506 if (st == ST_VERTEX && ctx.get_gpu_vendor_id() == GPUVendorID::GPU_VENDOR_AMD)
507 set_vertex_attrib_locations(source);
508
509 if (source.empty())
510 return false;
511
512 return set_code(ctx, source, st);
513}
514
516bool shader_code::set_code(const context& ctx, const std::string &source, ShaderType _st)
517{
518 st = _st;
519 destruct(ctx);
520 ctx_ptr = &ctx;
521 return ctx.shader_code_create(*this, st, source);
522}
523
525void shader_code::set_defines_and_snippets(std::string& source, const shader_compile_options& options)
526{
527 enum class DirectiveType {
529 kDefine,
531 };
532
533 struct directive_t {
534 DirectiveType type = DirectiveType::kUndefined;
535 std::string identifier;
536 std::string value;
537 };
538
539 if(options.defines.empty() && options.snippets.empty())
540 return;
541
542 std::map<std::string, directive_t*> directives;
543 std::map<size_t, directive_t*> directives_per_line;
544 size_t version_line_index = std::numeric_limits<size_t>::max();
545
546 std::vector<cgv::utils::line> lines;
547 split_to_lines(source, lines);
548 for(size_t i = 0; i < lines.size(); ++i) {
549 const cgv::utils::line& line = lines[i];
550
551 if(line.empty())
552 continue;
553
554 DirectiveType directive_type = DirectiveType::kUndefined;
555
556 // Pre-processor directives and snippet markers must appear on separate lines. Only whitespace characters are allowed to appear before them.
557 const char* ptr = line.begin;
558 for(; ptr < line.end; ++ptr) {
559 char c = *ptr;
560
561 if(c == '#') {
562 directive_type = DirectiveType::kDefine;
563 break;
564 } else if(c == '/') {
565 if(line.end - ptr > 7) {
566 if(cgv::utils::token(ptr, ptr + 8) == "//$cgv::") {
567 directive_type = DirectiveType::kSnippet;
568 break;
569 }
570 }
571 }
572
573 if(!is_space(c))
574 break;
575 }
576
577 if(directive_type != DirectiveType::kUndefined) {
578 std::vector<cgv::utils::token> tokens;
579 split_to_tokens(cgv::utils::token(ptr, line.end), tokens, "");
580
581 directive_t* directive = nullptr;
582
583 switch(directive_type) {
584 case DirectiveType::kDefine:
585 // A valid define directive will have at least two tokens (directive and name)
586 if(tokens.size() > 1) {
587 std::string first_token_str = to_string(tokens.front());
588
589 if(first_token_str == "#version") {
591 } else if(first_token_str == "#define" && !tokens[1].empty()) {
593 directive->identifier = to_string(tokens[1]);
594
595 // Any following tokens are considered to be the value of the define.
596 if(tokens.size() > 2) {
597 cgv::utils::token value_token(tokens[2].begin, tokens.back().end);
599 }
600 }
601 }
602 break;
603 case DirectiveType::kSnippet:
604 // A valid snippet directive will have one token (identifier).
605 if(tokens.size() == 1) {
606 tokens.front().begin += 3;
607 if(!tokens.front().empty())
608 directive = new directive_t{ directive_type, to_string(tokens.front()), "" };
609 }
610 break;
611 default:
612 break;
613 }
614
615 if(directive) {
616 directives[directive->identifier] = directive;
618 }
619 }
620 }
621
622 std::vector<std::pair<std::string, std::string>> additional_defines;
623
624 for(const auto& define : options.defines) {
625 auto it = directives.find(define.first);
626 if(it != directives.end() && it->second->type == DirectiveType::kDefine)
627 it->second->value = define.second;
628 else
629 additional_defines.push_back(define);
630 }
631
632 bool use_snippets = false;
633 if(!options.snippets.empty()) {
634 additional_defines.push_back({ "CGV_USE_SNIPPETS", "" });
635 use_snippets = true;
636 }
637
638 std::string out;
639 // The output string is going to have a similar length as the input.
640 out.reserve(source.length());
641
642 for(size_t i = 0; i < lines.size(); ++i) {
643 const cgv::utils::line& line = lines[i];
644
645 auto it = directives_per_line.find(i);
646 if(it != directives_per_line.end()) {
647 const directive_t* directive = it->second;
648
649 switch(directive->type) {
650 case DirectiveType::kDefine:
651 out += "#define " + directive->identifier + " " + directive->value;
652 break;
653 case DirectiveType::kSnippet:
654 if(use_snippets) {
655 auto it = std::find_if(options.snippets.begin(), options.snippets.end(), [directive](const shader_code_snippet& snippet) {
656 return directive->identifier == "cgv::" + snippet.id;
657 });
658 if(it != options.snippets.end())
659 out += it->content;
660 else
661 out += to_string(line);
662 }
663 break;
664 default:
665 break;
666 }
667 } else {
668 out += to_string(line);
669 }
670
671 out += '\n';
672
673 // Put additional defines after version directive.
674 if(version_line_index == i) {
675 out += '\n';
676 for(const auto& define : additional_defines)
677 out += "#define " + define.first + " " + define.second + '\n';
678 }
679 }
680
681 for(const auto& pair : directives)
683
684 directives.clear();
685 directives_per_line.clear();
686
687 source = std::move(out);
688}
689
691void shader_code::set_vertex_attrib_locations(std::string& source)
692{
693 struct vertex_attribute {
694 token tok;
695 int location = -1;
696 std::string type = "";
697 std::string name = "";
698
699 vertex_attribute(const token& tok) : tok(tok) {}
700
701 std::string to_string() {
702 std::string str = "layout (location = ";
703 str += std::to_string(location);
704 str += ") in ";
705 str += type + " ";
706 str += name + ";";
707 return str;
708 }
709 };
710
711 std::vector<token> parts;
712 std::vector<token> tokens;
713 std::vector<vertex_attribute> attribs;
714 std::vector<bool> attrib_flags;
715 size_t version_idx = 0;
716 int version_number = 0;
717 bool is_core = false;
718 bool no_upgrade = false;
719
720 source = strip_cpp_comments(source);
721
722 split_to_tokens(source, parts, "", true, "", "", ";\n");
723
724 attrib_flags.resize(parts.size(), false);
725
726 size_t part_idx = 0;
727
728 // First read the version. The only thing allowed before the version statement is comments and empty lines.
729 // Both get removed bevore splitting the source into parts, so the first part must be the version.
730 split_to_tokens(parts[part_idx], tokens, "", true, "", "", " \t");
731
732 if(tokens.size() > 1 && tokens[0] == "#version") {
734
735 std::string number_str = to_string(tokens[1]);
736 char* p_end;
737 const long num = std::strtol(number_str.c_str(), &p_end, 10);
738 if(number_str.c_str() != p_end)
739 version_number = static_cast<int>(num);
740
741 if(tokens.size() == 3 && tokens[2] == "core")
742 is_core = true;
743 }
744
745 ++part_idx;
746
747 // Search for the optional NO_UPGRADE define, which must come directly after the version statement.
748 tokens.clear();
749 split_to_tokens(parts[part_idx], tokens, "", true, "", "", " \t");
750
751 if(tokens.size() > 1 && tokens[0] == "#define") {
752 if(tokens[1] == "NO_UPGRADE") {
753 no_upgrade = true;
754 ++part_idx;
755 }
756 }
757
758 // return if the shader should not be upgraded
759 if(no_upgrade)
760 return;
761
762 // now get all vertex attributes
763 for(; part_idx < parts.size(); ++part_idx) {
764 auto& tok = parts[part_idx];
765
766 while(tok.begin < tok.end && is_space(*tok.begin))
767 ++tok.begin;
768
769 if(tok.size() > 2) {
770 int parentheses_count = 0;
771 bool is_new_word = true;
772 bool was_new_word = true;
773
774 for(unsigned i = 0; i < tok.size() - 2; ++i) {
775 char c = tok[i];
776
777 if(c == '(')
779
780 if(c == ')')
782
783 is_new_word = is_space(c) || c == ')';
784
785 if(c == 'i' && tok[i + 1] == 'n' && tok[i + 2] == ' ') {
786 if(was_new_word && parentheses_count == 0) {
787 attribs.push_back(vertex_attribute(tok));
788 attrib_flags[part_idx] = true;
789 break;
790 }
791 }
792
794 }
795 }
796 }
797
798 // return if no vertex attributes were found
799 if(attribs.size() == 0)
800 return;
801
802 int max_location = -1;
803 for(size_t i = 0; i < attribs.size(); ++i) {
804 auto& attrib = attribs[i];
805
806 tokens.clear();
807 split_to_tokens(attrib.tok, tokens, "", true, "", "", " \t()");
808
809 size_t size = tokens.size();
810
811 if(tokens.size() > 2) {
812 // last two entries must be type and name
813 attrib.type = to_string(tokens[size - 2]);
814 attrib.name = to_string(tokens[size - 1]);
815
816 // find location if present
817 size_t equals_idx = -1;
818 for(size_t j = 0; j < size; ++j) {
819 auto& tok = tokens[j];
820 if(tok.size() == 1 && tok[0] == '=')
821 equals_idx = j;
822 }
823
824 if(equals_idx != -1 && equals_idx > 0 && equals_idx < size - 1) {
825 if(to_string(tokens[equals_idx - 1]) == "location") {
826 std::string val_str = to_string(tokens[equals_idx + 1]);
827 char* p_end;
828 const long num = std::strtol(val_str.c_str(), &p_end, 10);
829 if(val_str.c_str() != p_end) {
830 attrib.location = static_cast<int>(num);
831 max_location = std::max(max_location, attrib.location);
832 }
833 }
834 }
835 }
836 }
837
838 for(size_t i = 0; i < attribs.size(); ++i) {
839 auto& attrib = attribs[i];
840 if(attrib.location < 0)
841 attrib.location = ++max_location;
842 }
843
844 size_t attrib_idx = 0;
845 size_t accumulate_offset = 0;
846 size_t content_offset = reinterpret_cast<size_t>(source.data());
847
848 for(size_t i = 0; i < parts.size(); ++i) {
849 auto& tok = parts[i];
850
851 if(i == version_idx || attrib_flags[i]) {
852 size_t token_begin = reinterpret_cast<size_t>(tok.begin);
853 size_t token_end = reinterpret_cast<size_t>(tok.end);
854
855 size_t offset = token_begin - content_offset;
856 size_t length = token_end - token_begin;
857
858 std::string str = "";
859
860 if(attrib_flags[i]) {
861 auto& attrib = attribs[attrib_idx];
862 ++attrib_idx;
863 str = attrib.to_string() + "\n";
864 } else {
865 version_number = std::max(version_number, 330);
866 str = "#version " + std::to_string(version_number) + (is_core ? " core" : "");
867 }
868
869 offset += accumulate_offset;
870 accumulate_offset += str.length() - length - 1;
871
872 std::string first_part = source.substr(0, offset);
873 std::string second_part = source.substr(offset + length + 1);
874
875 source = first_part + str + second_part;
876 }
877 }
878}
879
882{
883 if (!ctx.shader_code_compile(*this)) {
884 user_data = 0;
885 return false;
886 }
887 int id = 1;
888 user_data = (void*&) id;
889 return true;
890}
891
893bool shader_code::read_and_compile(const context& ctx, const std::string &file_name, ShaderType st, const shader_compile_options& options, bool show_error)
894{
895 if (!read_code(ctx, file_name, st, options))
896 return false;
897
898 if (!compile(ctx)) {
899 if (show_error)
900 std::cerr << get_last_error(file_name, last_error).c_str() << std::endl;
901 return false;
902 }
903
904 return true;
905}
906
909{
910 return user_data != 0;
911}
912
913
914 }
915}
916
918{
920 {
921 register_object(cgv::render::get_shader_config(), "register global shader config");
922 }
923};
924
925shader_config_registration shader_config_registration_instance;
More advanced text processing for splitting text into lines or tokens.
virtual bool end()
perform the leave part of the action on the current object
Definition action.cxx:48
complete implementation of method actions that only call one method when entering a node
Definition action.h:113
bool begin()
uses call_method of base class method_action to call the method refered to by the stored method point...
Definition action.h:122
reference counted pointer, which can work together with types that are derived from ref_counted,...
Definition ref_ptr.h:160
bool empty() const
check if pointer is not yet set
Definition ref_ptr.h:230
the pre header processor parses a pre header file and converts it to a header file or uses the inform...
the self reflection handler is passed to the virtual self_reflect() method of cgv::base::base.
bool reflect_member(const std::string &member_name, T &member_ref, bool hard_cast=false)
call this to reflect a member by member name and reference to the member.
base class for all drawables, which is independent of the used rendering API.
Definition context.h:626
virtual GPUVendorID get_gpu_vendor_id() const
device information
Definition context.cxx:230
bool is_shader_file_cache_enabled() const
whether the shader file caches are enabled
Definition context.cxx:467
const context * ctx_ptr
keep pointer to my context
Definition context.h:315
std::string last_error
a string that contains the last error
Definition context.h:317
static bool shader_file_name_map_initialized
whether the shader file name map is initialized
static std::string find_file(const std::string &file_name, bool search_exhaustive=false)
Find the full path to a shader by its file name.
shader_code()
create shader a shader code object
static std::string read_code_file(const std::string &file_name, std::string *_last_error=0)
read shader code from file and return string with content or empty string if read failed
static std::map< std::string, std::string > code_cache
map that caches shader file contents indexed by their file name
ShaderType get_shader_type() const
return the shader type of this code
bool is_compiled() const
return whether shader has been compiled successfully
void destruct(const context &ctx)
destruct shader code
~shader_code()
calls the destruct method
static void decode_if_base64(std::string &content)
decode a string if it is base64 encoded
static std::string retrieve_code(const std::string &file_name, bool use_cache, std::string *_last_error)
retreive shader code either by reading the file from disk or from the cache if enabled
ShaderType st
store the shader type
static std::string get_last_error(const std::string &file_name, const std::string &last_error)
format given last error in a way that developer environments can locate errors in the source file
static std::map< std::string, std::string > shader_file_name_map
map that caches full shader file paths indexed by the shader file name
bool compile(const context &ctx)
compile attached source; returns true if successful
bool set_code(const context &ctx, const std::string &source, ShaderType st)
set shader code from string
bool read_code(const context &ctx, const std::string &file_name, ShaderType st=ST_DETECT, const shader_compile_options &options={})
read shader code from file that is searched for with find_file.
bool read_and_compile(const context &ctx, const std::string &file_name, ShaderType st=ST_DETECT, const shader_compile_options &options={}, bool show_error=true)
read shader code with read_code and compile.
static ShaderType detect_shader_type(const std::string &file_name)
detect the shader type from the extension of the given file_name, i.e.
the tokenizer allows to split text into tokens in a convenient way.
Definition tokenizer.h:68
tokenizer & set_sep(const std::string &sep, bool merge)
set the list of separators and specify whether succeeding separators are merged into single tokens
Definition tokenizer.cxx:59
tokenizer & set_ws(const std::string &ws)
set the list of white spaces, that separate tokens and are skipped
Definition tokenizer.cxx:38
the base namespace holds the base hierarchy, support for plugin registration and signals
Definition action.cxx:4
bool read_data_file(const std::string &file_name, std::string &content, bool ascii)
read ascii file into a string
Definition import.cxx:388
std::map< std::string, resource_file_info > & ref_resource_file_map()
return a reference to a mapping of resource file names to resource file infos
Definition register.cxx:143
void register_object(base_ptr object, const std::string &options)
register an object and send event to all current registration ref_listeners()
Definition register.cxx:581
std::string & ref_prog_name()
return a refence to the name of the started executable
Definition register.cxx:125
shader_config_ptr get_shader_config()
return a reference to the current shader configuration
cgv::data::ref_ptr< shader_config > shader_config_ptr
type of ref counted pointer to shader configuration
Definition shader_code.h:46
ShaderType
different shader types
Definition context.h:495
namespace for compile time type information
namespace that holds tools that dont fit any other namespace
void split_to_tokens(const char *begin, const char *end, std::vector< token > &tokens, const std::string &separators, bool merge_separators, const std::string &open_parenthesis, const std::string &close_parenthesis, const std::string &whitespaces, unsigned int max_nr_tokens)
this function splits a text range into tokens.
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
bool is_integer(const char *begin, const char *end, int &value)
check if the text range (begin,end( defines an integer value. If yes, store the value in the passed r...
Definition scan.cxx:367
std::string & trim(std::string &str, const std::string &chars)
trim white space or other characters from start and end of string
Definition scan.cxx:709
void split_to_lines(const char *global_begin, const char *global_end, std::vector< line > &lines, bool truncate_trailing_spaces)
this function splits a text range at the newline characters into single lines.
char to_lower(char c)
convert char to lower case
Definition scan.cxx:39
char to_upper(char c)
convert char to upper case
Definition scan.cxx:106
bool is_space(char c)
check if char is a whitespace
Definition scan.cxx:12
std::string decode_base64(std::string const &encoded_string)
decode a base64 encoded string
Definition convert.cxx:105
std::string strip_cpp_comments(const std::string &source, bool correct_new_lines)
remove cpp-style comments from string
bool from_string(std::string &v, const std::string &s)
specialization to extract string value from string
the cgv namespace
Definition print.h:11
holds options applied before and during shader compilation, such as preprocessor defines and code sni...
Definition shader_code.h:87
std::vector< shader_code_snippet > snippets
shader code snippets
Definition shader_code.h:91
shader_define_map defines
map of pre-processor define names to values
Definition shader_code.h:89
a globally unique shader config is registered by default when the cgv library is used.
Definition shader_code.h:26
std::string get_type_name() const
return "shader_config"
bool show_file_paths
whether to output full paths of read shaders
Definition shader_code.h:32
shader_config()
construct config without file name tracing
bool trace_file_names
whether to keep track of file names
Definition shader_code.h:30
bool self_reflect(cgv::reflect::reflection_handler &srh)
reflect the shader_path member
std::string shader_path
the path used to find shaders with the cgv::utils::file::find_in_paths function
Definition shader_code.h:28
a line in a text is simply represented as a token
representation of a token in a text by two pointers begin and end, that point to the first character ...
Definition token.h:18
bool empty() const
return whether the token is empty
Definition token.h:56
const char * begin
pointers that define the range of characters
Definition token.h:20