Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/servers/rendering/rendering_device_binds.cpp
20831 views
1
/**************************************************************************/
2
/* rendering_device_binds.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "rendering_device_binds.h"
32
33
#include "modules/modules_enabled.gen.h" // For glslang.
34
#ifdef MODULE_GLSLANG_ENABLED
35
#include "modules/glslang/shader_compile.h"
36
#endif
37
38
#include "shader_include_db.h"
39
40
Error RDShaderFile::parse_versions_from_text(const String &p_text, const String p_defines, OpenIncludeFunction p_include_func, void *p_include_func_userdata) {
41
Vector<String> lines = p_text.split("\n");
42
43
bool reading_versions = false;
44
bool stage_found[RD::SHADER_STAGE_MAX] = {};
45
RD::ShaderStage stage = RD::SHADER_STAGE_MAX;
46
static const char *stage_str[RD::SHADER_STAGE_MAX] = {
47
"vertex",
48
"fragment",
49
"tesselation_control",
50
"tesselation_evaluation",
51
"compute",
52
"raygen",
53
"any_hit",
54
"closest_hit",
55
"miss",
56
"intersection",
57
};
58
String stage_code[RD::SHADER_STAGE_MAX];
59
int stages_found = 0;
60
HashMap<StringName, String> version_texts;
61
62
versions.clear();
63
base_error = "";
64
65
for (int lidx = 0; lidx < lines.size(); lidx++) {
66
String line = lines[lidx];
67
68
{
69
String ls = line.strip_edges();
70
if (ls.begins_with("#[") && ls.ends_with("]")) {
71
String section = ls.substr(2, ls.length() - 3).strip_edges();
72
if (section == "versions") {
73
if (stages_found) {
74
base_error = "Invalid shader file, #[versions] must be the first section found.";
75
break;
76
}
77
reading_versions = true;
78
} else {
79
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
80
if (section == stage_str[i]) {
81
if (stage_found[i]) {
82
base_error = "Invalid shader file, stage appears twice: " + section;
83
break;
84
}
85
86
stage_found[i] = true;
87
stages_found++;
88
89
stage = RD::ShaderStage(i);
90
reading_versions = false;
91
break;
92
}
93
}
94
95
if (!base_error.is_empty()) {
96
break;
97
}
98
}
99
100
continue;
101
}
102
}
103
104
if (stage == RD::SHADER_STAGE_MAX && !line.strip_edges().is_empty()) {
105
line = line.strip_edges();
106
if (line.begins_with("//") || line.begins_with("/*")) {
107
continue; //assuming comment (single line)
108
}
109
}
110
111
if (reading_versions) {
112
String l = line.strip_edges();
113
if (!l.is_empty()) {
114
if (!l.contains_char('=')) {
115
base_error = "Missing `=` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";
116
break;
117
}
118
if (!l.contains_char(';')) {
119
// We don't require a semicolon per se, but it's needed for clang-format to handle things properly.
120
base_error = "Missing `;` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";
121
break;
122
}
123
Vector<String> slices = l.get_slicec(';', 0).split("=");
124
String version = slices[0].strip_edges();
125
if (!version.is_valid_ascii_identifier()) {
126
base_error = "Version names must be valid identifiers, found '" + version + "' instead.";
127
break;
128
}
129
String define = slices[1].strip_edges();
130
if (!define.begins_with("\"") || !define.ends_with("\"")) {
131
base_error = "Version text must be quoted using \"\", instead found '" + define + "'.";
132
break;
133
}
134
define = "\n" + define.substr(1, define.length() - 2).c_unescape() + "\n"; // Add newline before and after just in case.
135
136
version_texts[version] = define + "\n" + p_defines;
137
}
138
} else {
139
if (stage == RD::SHADER_STAGE_MAX && !line.strip_edges().is_empty()) {
140
base_error = "Text was found that does not belong to a valid section: " + line;
141
break;
142
}
143
144
if (stage != RD::SHADER_STAGE_MAX) {
145
if (line.strip_edges().begins_with("#include")) {
146
if (p_include_func) {
147
//process include
148
String include = line.replace("#include", "").strip_edges();
149
if (!include.begins_with("\"") || !include.ends_with("\"")) {
150
base_error = "Malformed #include syntax, expected #include \"<path>\", found instead: " + include;
151
break;
152
}
153
include = include.substr(1, include.length() - 2).strip_edges();
154
155
String include_code = ShaderIncludeDB::get_built_in_include_file(include);
156
if (!include_code.is_empty()) {
157
stage_code[stage] += "\n" + include_code + "\n";
158
} else {
159
String include_text = p_include_func(include, p_include_func_userdata);
160
if (!include_text.is_empty()) {
161
stage_code[stage] += "\n" + include_text + "\n";
162
} else {
163
base_error = "#include failed for file '" + include + "'.";
164
}
165
}
166
} else {
167
base_error = "#include used, but no include function provided.";
168
}
169
} else {
170
stage_code[stage] += line + "\n";
171
}
172
}
173
}
174
}
175
176
if (base_error.is_empty()) {
177
if (stage_found[RD::SHADER_STAGE_COMPUTE] && stages_found > 1) {
178
ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "When writing compute shaders, [compute] mustbe the only stage present.");
179
}
180
181
if (version_texts.is_empty()) {
182
version_texts[""] = ""; //make sure a default version exists
183
}
184
185
bool errors_found = false;
186
187
/* STEP 2, Compile the versions, add to shader file */
188
189
for (const KeyValue<StringName, String> &E : version_texts) {
190
Ref<RDShaderSPIRV> bytecode;
191
bytecode.instantiate();
192
193
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
194
String code = stage_code[i];
195
if (code.is_empty()) {
196
continue;
197
}
198
code = code.replace("VERSION_DEFINES", E.value);
199
String error;
200
#ifdef MODULE_GLSLANG_ENABLED
201
Vector<uint8_t> spirv = compile_glslang_shader(RD::ShaderStage(i), ShaderIncludeDB::parse_include_files(code), RD::SHADER_LANGUAGE_VULKAN_VERSION_1_1, RD::SHADER_SPIRV_VERSION_1_4, &error);
202
bytecode->set_stage_bytecode(RD::ShaderStage(i), spirv);
203
#else
204
error = "Shader compilation is not supported because glslang was not enabled.";
205
#endif
206
if (!error.is_empty()) {
207
error += String() + "\n\nStage '" + stage_str[i] + "' source code: \n\n";
208
Vector<String> sclines = code.split("\n");
209
for (int j = 0; j < sclines.size(); j++) {
210
error += itos(j + 1) + "\t\t" + sclines[j] + "\n";
211
}
212
errors_found = true;
213
}
214
bytecode->set_stage_compile_error(RD::ShaderStage(i), error);
215
}
216
217
set_bytecode(bytecode, E.key);
218
}
219
220
return errors_found ? ERR_PARSE_ERROR : OK;
221
} else {
222
return ERR_PARSE_ERROR;
223
}
224
}
225
226