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>
13#pragma warning(disable:4996)
37 return "shader_config";
54 if (
getenv(
"CGV_SHADER_PATH"))
56 else if (
getenv(
"CGV_DIR"))
58 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_g2d/glsl;" +
59 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_gl/glsl;"+
60 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_gpgpu/glsl;" +
61 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_overlay/glsl;" +
62 std::string(
getenv(
"CGV_DIR")) +
"/libs/holo_disp;" +
63 std::string(
getenv(
"CGV_DIR")) +
"/libs/plot/glsl;"+
64 std::string(
getenv(
"CGV_DIR")) +
"/libs/cgv_proc/glsl;"+
65 std::string(
getenv(
"CGV_DIR")) +
"/plugins/examples";
72 if (!content.empty()) {
74 if(content[0] ==
char(0xA7))
97 std::vector<line> lines;
100 for (
unsigned int i = 0;
i<lines.size(); ++
i) {
102 if (
to_upper(
l.substr(0, 5)) ==
"ERROR") {
103 std::vector<token>
toks;
106 if (
toks.size() > 4) {
119 std::vector<token>
toks;
121 unsigned int i,
j, k;
int n;
122 for (
i=0;
i<
toks.size(); ++
i) {
126 for (
j=0;
j<
toks.size(); ++
j) {
130 for (k=0; k<
toks.size(); ++k) {
135 std::string
_fn =
fn;
169 std::cerr <<
"shader code not destructed correctly" << std::endl;
177 ctx.shader_code_destruct(*
this);
183 if (file_name.substr(0, 6) ==
"str://" || file_name.substr(0, 6) ==
"res://") {
184 std::map<std::string, resource_file_info>::const_iterator
it =
ref_resource_file_map().find(file_name.substr(6));
190 if (file::exists(file_name))
197 std::map<std::string, resource_file_info>::const_iterator
it =
200 if (
it->second.file_offset == -1)
201 return std::string(
"str://") + file_name;
203 return std::string(
"res://") + file_name;
206 try_name = std::string(
"glsl/") + file_name;
222 if(
end_pos == std::string::npos) {
234 std::string
ext = file::get_extension(file_name);
235 if(
ext.length() > 2) {
236 if(
ext[0] ==
'g' &&
ext[1] ==
'l' ||
237 ext[0] ==
'p' &&
ext[1] ==
'g' &&
ext[2] ==
'l')
260 std::string source =
"";
282 std::string
ext =
to_lower(file::get_extension(file_name));
284 if (
ext ==
"glfs" ||
ext ==
"pglfs")
286 else if (
ext ==
"glgs" ||
ext ==
"pglgs")
288 else if (
ext ==
"glcs" ||
ext ==
"pglcs")
290 else if (
ext ==
"gltc" ||
ext ==
"pgltc")
291 st = ST_TESS_CONTROL;
292 else if (
ext ==
"glte" ||
ext ==
"pglte")
293 st = ST_TESS_EVALUATION;
303 std::vector<line> lines;
306 for(
size_t i = 0;
i < lines.size(); ++
i) {
343std::string shader_code::resolve_includes(
const std::string& source,
bool use_cache, std::string*
_last_error) {
348void shader_code::resolve_version_and_extensions(std::string& source) {
357 size_t length()
const {
369 Type type = kUndefined;
376 static const std::map<std::string, preprocessor_directive_t::Type>
identifier_to_type = {
377 {
"#version", preprocessor_directive_t::Type::kVersion },
378 {
"#extension", preprocessor_directive_t::Type::kExtension },
388 std::vector<preprocessor_directive_t>
directives;
394 while(
i < source.length()) {
399 if(
comment == CommentType::kNone ||
comment == CommentType::kSpecial) {
403 end = source.find(
'\n', end + 1);
404 }
while(end != std::string::npos && source[end - 1] ==
'\\');
406 if(end == std::string::npos)
407 end = source.length();
414 size_t space = source.find(
' ',
i + 1);
433 if(
comment == CommentType::kNone && (
i + 1) < source.length()) {
434 if(source[
i + 1] ==
'/')
436 else if(source[
i + 1] ==
'*')
442 if(
comment == CommentType::kBlock && (
i + 1) < source.length()) {
443 if(source[
i + 1] ==
'/')
450 if(
comment == CommentType::kLine)
451 comment = CommentType::kSpecial;
454 if(
comment != CommentType::kBlock)
473 if(
directive.type != preprocessor_directive_t::Type::kVersion)
478 std::vector<cgv::utils::token> tokens;
483 if(!tokens.empty()) {
488 if(tokens.size() > 1) {
491 version.has_profile =
true;
492 version.is_core_profile =
true;
493 }
else if(
profile ==
"compatibility") {
494 version.has_profile =
true;
495 version.is_core_profile =
false;
502 if(version.has_profile) {
504 if(version.is_core_profile)
515 if(!
directive.is_special_comment && (
directive.type == preprocessor_directive_t::Type::kVersion ||
directive.type == preprocessor_directive_t::Type::kExtension)) {
533 if(
directive.type == preprocessor_directive_t::Type::kExtension)
567 std::cout <<
"read shader code <" <<
fn <<
">" << std::endl;
571 if (file::get_extension(file_name)[0] ==
'p') {
574 std::string
paths = file::get_path(
fn);
580 if (!
php.parse_string(source))
582 if (!
php.process_to_string(
code))
584 cgv::ppp::clear_variables();
601 resolve_version_and_extensions(source);
603 set_defines_and_snippets(source, options);
606 set_vertex_attrib_locations(source);
620 return ctx.shader_code_create(*
this,
st, source);
641 std::map<std::string, directive_t*>
directives;
645 std::vector<cgv::utils::line> lines;
647 for(
size_t i = 0;
i < lines.size(); ++
i) {
657 for(; ptr <
line.end; ++ptr) {
663 }
else if(c ==
'/') {
664 if(
line.end - ptr > 7) {
677 std::vector<cgv::utils::token> tokens;
683 case DirectiveType::kDefine:
685 if(tokens.size() > 1) {
695 if(tokens.size() > 2) {
702 case DirectiveType::kSnippet:
704 if(tokens.size() == 1) {
705 tokens.front().begin += 3;
706 if(!tokens.front().empty())
723 for(
const auto&
define : options.get_macros()) {
726 it->second->replacement_list =
define.second;
732 const shader_compile_options::string_map& snippets = options.
get_snippets();
733 if(!snippets.empty())
738 out.reserve(source.length());
740 for(
size_t i = 0;
i < lines.size(); ++
i) {
748 case DirectiveType::kDefine:
751 case DirectiveType::kSnippet:
752 if(!snippets.empty()) {
753 auto it = std::find_if(snippets.begin(), snippets.end(), [
directive](
const std::pair<std::string, std::string>&
snippet) {
754 return directive->identifier ==
"cgv::" + snippet.first;
756 if(
it != snippets.
end())
785 source = std::move(
out);
789void shader_code::set_vertex_attrib_locations(std::string& source)
791 struct vertex_attribute {
794 std::string type =
"";
795 std::string name =
"";
800 std::string
str =
"layout (location = ";
801 str += std::to_string(location);
809 std::vector<token>
parts;
810 std::vector<token> tokens;
811 std::vector<vertex_attribute>
attribs;
830 if(tokens.size() > 1 && tokens[0] ==
"#version") {
839 if(tokens.size() == 3 && tokens[2] ==
"core")
849 if(tokens.size() > 1 && tokens[0] ==
"#define") {
850 if(tokens[1] ==
"NO_UPGRADE") {
872 for(
unsigned i = 0;
i <
tok.size() - 2; ++
i) {
883 if(c ==
'i' &&
tok[
i + 1] ==
'n' &&
tok[
i + 2] ==
' ') {
907 size_t size = tokens.size();
909 if(tokens.size() > 2) {
916 for(
size_t j = 0;
j < size; ++
j) {
917 auto&
tok = tokens[
j];
918 if(
tok.size() == 1 &&
tok[0] ==
'=')
946 for(
size_t i = 0;
i <
parts.size(); ++
i) {
956 std::string
str =
"";
970 std::string
first_part = source.substr(0, offset);
971 std::string
second_part = source.substr(offset + length + 1);
981 if (!ctx.shader_code_compile(*
this)) {
986 user_data = (
void*&)
id;
1008 return user_data != 0;
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
complete implementation of method actions that only call one method when entering a node
bool begin()
uses call_method of base class method_action to call the method refered to by the stored method point...
reference counted pointer, which can work together with types that are derived from ref_counted,...
bool empty() const
check if pointer is not yet set
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.
virtual GPUVendorID get_gpu_vendor_id() const
device information
bool is_shader_file_cache_enabled() const
whether the shader file caches are enabled
const context * ctx_ptr
keep pointer to my context
std::string last_error
a string that contains the last error
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.
Stores preprocessor options used for conditionally compiling shader programs.
bool empty() const
Return true if no options are set.
const string_map & get_snippets() const
Return const reference to defined snippets.
the tokenizer allows to split text into tokens in a convenient way.
tokenizer & set_sep(const std::string &sep, bool merge)
set the list of separators and specify whether succeeding separators are merged into single tokens
tokenizer & set_ws(const std::string &ws)
set the list of white spaces, that separate tokens and are skipped
the base namespace holds the base hierarchy, support for plugin registration and signals
bool read_data_file(const std::string &file_name, std::string &content, bool ascii)
read ascii file into a string
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
void register_object(base_ptr object, const std::string &options)
register an object and send event to all current registration ref_listeners()
std::string & ref_prog_name()
return a refence to the name of the started executable
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
ShaderType
different shader types
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...
std::string & trim(std::string &str, const std::string &chars)
trim white space or other characters from start and end of string
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
char to_upper(char c)
convert char to upper case
bool is_space(char c)
check if char is a whitespace
std::string decode_base64(std::string const &encoded_string)
decode a base64 encoded string
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
this header is dependency free
a globally unique shader config is registered by default when the cgv library is used.
std::string get_type_name() const
return "shader_config"
bool show_file_paths
whether to output full paths of read shaders
shader_config()
construct config without file name tracing
bool trace_file_names
whether to keep track of file names
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
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 ...
bool empty() const
return whether the token is empty
const char * begin
pointers that define the range of characters