summaryrefslogtreecommitdiff
path: root/servers/rendering/shader_preprocessor.h
blob: f198af66f0e09b44ae388dc83a3e9c17ecc308ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/**************************************************************************/
/*  shader_preprocessor.h                                                 */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* Permission is hereby granted, free of charge, to any person obtaining  */
/* a copy of this software and associated documentation files (the        */
/* "Software"), to deal in the Software without restriction, including    */
/* without limitation the rights to use, copy, modify, merge, publish,    */
/* distribute, sublicense, and/or sell copies of the Software, and to     */
/* permit persons to whom the Software is furnished to do so, subject to  */
/* the following conditions:                                              */
/*                                                                        */
/* The above copyright notice and this permission notice shall be         */
/* included in all copies or substantial portions of the Software.        */
/*                                                                        */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
/**************************************************************************/

#ifndef SHADER_PREPROCESSOR_H
#define SHADER_PREPROCESSOR_H

#include "core/string/ustring.h"
#include "core/templates/list.h"
#include "core/templates/local_vector.h"
#include "core/templates/rb_map.h"
#include "core/templates/rb_set.h"
#include "core/typedefs.h"

#include "core/io/resource_loader.h"
#include "core/os/os.h"
#include "scene/resources/shader.h"
#include "scene/resources/shader_include.h"

class ShaderPreprocessor {
public:
	enum CompletionType {
		COMPLETION_TYPE_NONE,
		COMPLETION_TYPE_DIRECTIVE,
		COMPLETION_TYPE_PRAGMA_DIRECTIVE,
		COMPLETION_TYPE_PRAGMA,
		COMPLETION_TYPE_CONDITION,
		COMPLETION_TYPE_INCLUDE_PATH,
	};

	struct FilePosition {
		String file;
		int line = 0;
	};

	struct Region {
		String file;
		int from_line = -1;
		int to_line = -1;
		bool enabled = false;
		Region *parent = nullptr;
	};

private:
	struct Token {
		char32_t text;
		int line;

		Token();
		Token(char32_t p_text, int p_line);
	};

	// The real preprocessor that understands basic shader and preprocessor language syntax.
	class Tokenizer {
	public:
		String code;
		int line;
		int index;
		int size;
		Vector<Token> generated;

	private:
		void add_generated(const Token &p_t);
		char32_t next();

	public:
		int get_line() const;
		int get_index() const;
		char32_t peek();
		int consume_line_continuations(int p_offset);

		void get_and_clear_generated(Vector<Token> *r_out);
		void backtrack(char32_t p_what);
		LocalVector<Token> advance(char32_t p_what);
		void skip_whitespace();
		bool consume_empty_line();
		String get_identifier(bool *r_is_cursor = nullptr, bool p_started = false);
		String peek_identifier();
		Token get_token();

		Tokenizer(const String &p_code);
	};

	class CommentRemover {
	private:
		LocalVector<char32_t> stripped;
		String code;
		int index;
		int line;
		int comment_line_open;
		int comments_open;
		int strings_open;

	public:
		String get_error() const;
		int get_error_line() const;
		char32_t peek() const;

		bool advance(char32_t p_what);
		String strip();

		CommentRemover(const String &p_code);
	};

	struct Define {
		Vector<String> arguments;
		String body;
	};

	struct Branch {
		Vector<bool> conditions;
		Branch *parent = nullptr;
		bool else_defined = false;

		Branch() {}

		Branch(bool p_condition, Branch *p_parent) :
				parent(p_parent) {
			conditions.push_back(p_condition);
		}
	};

	struct State {
		RBMap<String, Define *> defines;
		List<Branch> branches;
		Branch *current_branch = nullptr;
		int condition_depth = 0;
		RBSet<String> includes;
		List<uint64_t> cyclic_include_hashes; // Holds code hash of includes.
		int include_depth = 0;
		String current_filename;
		String current_shader_type;
		String error;
		List<FilePosition> include_positions;
		bool save_regions = false;
		RBMap<String, List<Region>> regions;
		Region *previous_region = nullptr;
		bool disabled = false;
		CompletionType completion_type = COMPLETION_TYPE_NONE;
		HashSet<Ref<ShaderInclude>> shader_includes;
	};

private:
	LocalVector<char32_t> output;
	State *state = nullptr;

private:
	static bool is_char_word(char32_t p_char);
	static bool is_char_space(char32_t p_char);
	static bool is_char_end(char32_t p_char);
	static String vector_to_string(const LocalVector<char32_t> &p_v, int p_start = 0, int p_end = -1);
	static String tokens_to_string(const LocalVector<Token> &p_tokens);

	void _set_expected_error(const String &p_what, int p_line) {
		set_error(vformat(RTR("Expected a '%s'."), p_what), p_line);
	}

	void _set_unexpected_token_error(const String &p_what, int p_line) {
		set_error(vformat(RTR("Unexpected token: '%s'."), p_what), p_line);
	}

	void process_directive(Tokenizer *p_tokenizer);
	void process_define(Tokenizer *p_tokenizer);
	void process_elif(Tokenizer *p_tokenizer);
	void process_else(Tokenizer *p_tokenizer);
	void process_endif(Tokenizer *p_tokenizer);
	void process_if(Tokenizer *p_tokenizer);
	void process_ifdef(Tokenizer *p_tokenizer);
	void process_ifndef(Tokenizer *p_tokenizer);
	void process_include(Tokenizer *p_tokenizer);
	void process_pragma(Tokenizer *p_tokenizer);
	void process_undef(Tokenizer *p_tokenizer);

	void add_region(int p_line, bool p_enabled, Region *p_parent_region);
	void start_branch_condition(Tokenizer *p_tokenizer, bool p_success, bool p_continue = false);

	Error expand_condition(const String &p_string, int p_line, String &r_result);
	void expand_output_macros(int p_start, int p_line);
	Error expand_macros(const String &p_string, int p_line, String &r_result);
	bool expand_macros_once(const String &p_line, int p_line_number, const RBMap<String, Define *>::Element *p_define_pair, String &r_expanded);
	bool find_match(const String &p_string, const String &p_value, int &r_index, int &r_index_start);

	String next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives);
	void add_to_output(const String &p_str);
	void set_error(const String &p_error, int p_line);

	static Define *create_define(const String &p_body);

	void clear_state();

	Error preprocess(State *p_state, const String &p_code, String &r_result);

public:
	typedef void (*IncludeCompletionFunction)(List<ScriptLanguage::CodeCompletionOption> *);

	Error preprocess(const String &p_code, const String &p_filename, String &r_result, String *r_error_text = nullptr, List<FilePosition> *r_error_position = nullptr, List<Region> *r_regions = nullptr, HashSet<Ref<ShaderInclude>> *r_includes = nullptr, List<ScriptLanguage::CodeCompletionOption> *r_completion_options = nullptr, List<ScriptLanguage::CodeCompletionOption> *r_completion_defines = nullptr, IncludeCompletionFunction p_include_completion_func = nullptr);

	static void get_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords, bool p_ignore_context_keywords = false);
	static void get_pragma_list(List<String> *r_pragmas);

	ShaderPreprocessor();
	~ShaderPreprocessor();
};

#endif // SHADER_PREPROCESSOR_H