Clean up Shader Preprocessor
* Moved preprocessor to Shader and ShaderInclude * Clean up RenderingServer side * Preprocessor is separate from parser now, but it emits tokens with include location hints. * Improved ShaderEditor validation code * Added include file code completion * Added notification for all files affected by a broken include.
This commit is contained in:
parent
7b94603baa
commit
f649678402
32 changed files with 548 additions and 291 deletions
|
|
@ -63,6 +63,8 @@ public:
|
|||
virtual void shader_free(RID p_rid) override{};
|
||||
|
||||
virtual void shader_set_code(RID p_shader, const String &p_code) override {}
|
||||
virtual void shader_set_path_hint(RID p_shader, const String &p_code) override {}
|
||||
|
||||
virtual String shader_get_code(RID p_shader) const override { return ""; }
|
||||
virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override {}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@
|
|||
|
||||
using namespace RendererSceneRenderImplementation;
|
||||
|
||||
void SceneShaderForwardClustered::ShaderData::set_path_hint(const String &p_path) {
|
||||
path = p_path;
|
||||
}
|
||||
|
||||
void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ public:
|
|||
uint32_t index = 0;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual void set_path_hint(const String &p_path);
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
|
||||
void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ using namespace RendererSceneRenderImplementation;
|
|||
|
||||
/* ShaderData */
|
||||
|
||||
void SceneShaderForwardMobile::ShaderData::set_path_hint(const String &p_path) {
|
||||
path = p_path;
|
||||
}
|
||||
|
||||
void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ public:
|
|||
uint32_t index = 0;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual void set_path_hint(const String &p_path);
|
||||
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
|
||||
void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
|
||||
|
|
|
|||
|
|
@ -1968,6 +1968,10 @@ void RendererCanvasRenderRD::occluder_polygon_set_cull_mode(RID p_occluder, RS::
|
|||
oc->cull_mode = p_mode;
|
||||
}
|
||||
|
||||
void RendererCanvasRenderRD::CanvasShaderData::set_path_hint(const String &p_path) {
|
||||
path = p_path;
|
||||
}
|
||||
|
||||
void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
|
|||
bool uses_time = false;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual void set_path_hint(const String &p_path);
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
|
||||
virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
|
||||
|
|
|
|||
|
|
@ -3978,6 +3978,10 @@ void RendererSceneRenderRD::_setup_decals(const PagedArray<RID> &p_decals, const
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FOG SHADER
|
||||
|
||||
void RendererSceneRenderRD::FogShaderData::set_path_hint(const String &p_path) {
|
||||
path = p_path;
|
||||
}
|
||||
|
||||
void RendererSceneRenderRD::FogShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
|
|
|
|||
|
|
@ -947,6 +947,7 @@ private:
|
|||
bool uses_time = false;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual void set_path_hint(const String &p_hint);
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
|
||||
virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SKY SHADER
|
||||
|
||||
void RendererSceneSkyRD::SkyShaderData::set_path_hint(const String &p_path) {
|
||||
path = p_path;
|
||||
}
|
||||
|
||||
void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) {
|
||||
//compile
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ private:
|
|||
bool uses_light = false;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual void set_path_hint(const String &p_hint);
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
|
||||
virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
|
||||
|
|
|
|||
|
|
@ -2341,6 +2341,7 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) {
|
|||
}
|
||||
|
||||
if (shader->data) {
|
||||
shader->data->set_path_hint(shader->path_hint);
|
||||
shader->data->set_code(p_code);
|
||||
}
|
||||
|
||||
|
|
@ -2351,6 +2352,16 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) {
|
|||
}
|
||||
}
|
||||
|
||||
void MaterialStorage::shader_set_path_hint(RID p_shader, const String &p_path) {
|
||||
Shader *shader = shader_owner.get_or_null(p_shader);
|
||||
ERR_FAIL_COND(!shader);
|
||||
|
||||
shader->path_hint = p_path;
|
||||
if (shader->data) {
|
||||
shader->data->set_path_hint(p_path);
|
||||
}
|
||||
}
|
||||
|
||||
String MaterialStorage::shader_get_code(RID p_shader) const {
|
||||
Shader *shader = shader_owner.get_or_null(p_shader);
|
||||
ERR_FAIL_COND_V(!shader, String());
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ enum ShaderType {
|
|||
|
||||
struct ShaderData {
|
||||
virtual void set_code(const String &p_Code) = 0;
|
||||
virtual void set_path_hint(const String &p_hint) = 0;
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) = 0;
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const = 0;
|
||||
|
||||
|
|
@ -77,6 +78,7 @@ struct Material;
|
|||
struct Shader {
|
||||
ShaderData *data = nullptr;
|
||||
String code;
|
||||
String path_hint;
|
||||
ShaderType type;
|
||||
HashMap<StringName, HashMap<int, RID>> default_texture_parameter;
|
||||
HashSet<Material *> owners;
|
||||
|
|
@ -364,6 +366,7 @@ public:
|
|||
virtual void shader_free(RID p_rid) override;
|
||||
|
||||
virtual void shader_set_code(RID p_shader, const String &p_code) override;
|
||||
virtual void shader_set_path_hint(RID p_shader, const String &p_path) override;
|
||||
virtual String shader_get_code(RID p_shader) const override;
|
||||
virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -1512,6 +1512,9 @@ bool ParticlesStorage::particles_is_inactive(RID p_particles) const {
|
|||
|
||||
/* Particles SHADER */
|
||||
|
||||
void ParticlesStorage::ParticlesShaderData::set_path_hint(const String &p_path) {
|
||||
path = p_path;
|
||||
}
|
||||
void ParticlesStorage::ParticlesShaderData::set_code(const String &p_code) {
|
||||
ParticlesStorage *particles_storage = ParticlesStorage::get_singleton();
|
||||
//compile
|
||||
|
|
|
|||
|
|
@ -363,6 +363,7 @@ private:
|
|||
uint32_t userdata_count = 0;
|
||||
|
||||
virtual void set_code(const String &p_Code);
|
||||
virtual void set_path_hint(const String &p_hint);
|
||||
virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index);
|
||||
virtual void get_param_list(List<PropertyInfo> *p_param_list) const;
|
||||
virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ public:
|
|||
FUNCRIDSPLIT(shader)
|
||||
|
||||
FUNC2(shader_set_code, RID, const String &)
|
||||
FUNC2(shader_set_path_hint, RID, const String &)
|
||||
FUNC1RC(String, shader_get_code, RID)
|
||||
|
||||
FUNC2SC(shader_get_param_list, RID, List<PropertyInfo> *)
|
||||
|
|
|
|||
|
|
@ -1323,18 +1323,76 @@ Error ShaderCompiler::compile(RS::ShaderMode p_mode, const String &p_code, Ident
|
|||
Error err = parser.compile(p_code, info);
|
||||
|
||||
if (err != OK) {
|
||||
Vector<ShaderLanguage::FilePosition> include_positions = parser.get_include_positions();
|
||||
|
||||
String current;
|
||||
HashMap<String, Vector<String>> includes;
|
||||
includes[""] = Vector<String>();
|
||||
Vector<String> include_stack;
|
||||
Vector<String> shader = p_code.split("\n");
|
||||
|
||||
// Reconstruct the files.
|
||||
for (int i = 0; i < shader.size(); i++) {
|
||||
if (i + 1 == parser.get_error_line()) {
|
||||
// Mark the error line to be visible without having to look at
|
||||
// the trace at the end.
|
||||
print_line(vformat("E%4d-> %s", i + 1, shader[i]));
|
||||
String l = shader[i];
|
||||
if (l.begins_with("@@>")) {
|
||||
String inc_path = l.replace_first("@@>", "");
|
||||
|
||||
l = "#include \"" + inc_path + "\"";
|
||||
includes[current].append("#include \"" + inc_path + "\""); // Restore the include directive
|
||||
include_stack.push_back(current);
|
||||
current = inc_path;
|
||||
includes[inc_path] = Vector<String>();
|
||||
|
||||
} else if (l.begins_with("@@<")) {
|
||||
if (include_stack.size()) {
|
||||
current = include_stack[include_stack.size() - 1];
|
||||
include_stack.resize(include_stack.size() - 1);
|
||||
}
|
||||
} else {
|
||||
print_line(vformat("%5d | %s", i + 1, shader[i]));
|
||||
includes[current].push_back(l);
|
||||
}
|
||||
}
|
||||
|
||||
_err_print_error(nullptr, p_path.utf8().get_data(), parser.get_error_line(), parser.get_error_text().utf8().get_data(), false, ERR_HANDLER_SHADER);
|
||||
// Print the files.
|
||||
for (const KeyValue<String, Vector<String>> &E : includes) {
|
||||
if (E.key.is_empty()) {
|
||||
if (p_path == "") {
|
||||
print_line("--Main Shader--");
|
||||
} else {
|
||||
print_line("--" + p_path + "--");
|
||||
}
|
||||
} else {
|
||||
print_line("--" + E.key + "--");
|
||||
}
|
||||
int err_line = -1;
|
||||
for (int i = 0; i < include_positions.size(); i++) {
|
||||
if (include_positions[i].file == E.key) {
|
||||
err_line = include_positions[i].line;
|
||||
}
|
||||
}
|
||||
const Vector<String> &V = E.value;
|
||||
for (int i = 0; i < V.size(); i++) {
|
||||
if (i == err_line - 1) {
|
||||
// Mark the error line to be visible without having to look at
|
||||
// the trace at the end.
|
||||
print_line(vformat("E%4d-> %s", i + 1, V[i]));
|
||||
} else {
|
||||
print_line(vformat("%5d | %s", i + 1, V[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String file;
|
||||
int line;
|
||||
if (include_positions.size() > 1) {
|
||||
file = include_positions[include_positions.size() - 1].file;
|
||||
line = include_positions[include_positions.size() - 1].line;
|
||||
} else {
|
||||
file = p_path;
|
||||
line = parser.get_error_line();
|
||||
}
|
||||
|
||||
_err_print_error(nullptr, file.utf8().get_data(), line, parser.get_error_text().utf8().get_data(), false, ERR_HANDLER_SHADER);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@
|
|||
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "servers/rendering_server.h"
|
||||
#include "shader_preprocessor.h"
|
||||
|
||||
#define HAS_WARNING(flag) (warning_flags & flag)
|
||||
|
||||
|
|
@ -575,6 +575,37 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
|
||||
return _make_token(TK_OP_MOD);
|
||||
} break;
|
||||
case '@': {
|
||||
if (GETCHAR(0) == '@' && GETCHAR(1) == '>') {
|
||||
char_idx += 2;
|
||||
|
||||
LocalVector<char32_t> incp;
|
||||
while (GETCHAR(0) != '\n') {
|
||||
incp.push_back(GETCHAR(0));
|
||||
char_idx++;
|
||||
}
|
||||
incp.push_back(0); // Zero end it.
|
||||
String include_path(incp.ptr());
|
||||
include_positions.write[include_positions.size() - 1].line = tk_line;
|
||||
FilePosition fp;
|
||||
fp.file = include_path;
|
||||
fp.line = 0;
|
||||
tk_line = 0;
|
||||
include_positions.push_back(fp);
|
||||
|
||||
} else if (GETCHAR(0) == '@' && GETCHAR(1) == '<') {
|
||||
if (include_positions.size() == 1) {
|
||||
return _make_token(TK_ERROR, "Invalid include exit hint @@< without matching enter hint.");
|
||||
}
|
||||
char_idx += 2;
|
||||
|
||||
include_positions.resize(include_positions.size() - 1); // Pop back.
|
||||
tk_line = include_positions[include_positions.size() - 1].line; // Restore line.
|
||||
|
||||
} else {
|
||||
return _make_token(TK_ERROR, "Invalid include enter/exit hint token (@@> and @@<)");
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
char_idx--; //go back one, since we have no idea what this is
|
||||
|
||||
|
|
@ -1123,6 +1154,9 @@ void ShaderLanguage::clear() {
|
|||
completion_base = TYPE_VOID;
|
||||
completion_base_array = false;
|
||||
|
||||
include_positions.clear();
|
||||
include_positions.push_back(FilePosition());
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
keyword_completion_context = CF_GLOBAL_SPACE;
|
||||
used_constants.clear();
|
||||
|
|
@ -4119,10 +4153,6 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
|
|||
}
|
||||
}
|
||||
|
||||
void ShaderLanguage::get_preprocessor_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords) {
|
||||
ShaderPreprocessor::get_keyword_list(r_keywords, p_include_shader_keywords);
|
||||
}
|
||||
|
||||
bool ShaderLanguage::is_control_flow_keyword(String p_keyword) {
|
||||
return p_keyword == "break" ||
|
||||
p_keyword == "case" ||
|
||||
|
|
@ -7682,27 +7712,6 @@ Error ShaderLanguage::_validate_precision(DataType p_type, DataPrecision p_preci
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error ShaderLanguage::_preprocess_shader(const String &p_code, String &r_result, int *r_completion_type) {
|
||||
Error error = OK;
|
||||
|
||||
ShaderPreprocessor processor(p_code);
|
||||
processor.preprocess(r_result);
|
||||
|
||||
ShaderPreprocessor::State *state = processor.get_state();
|
||||
if (!state->error.is_empty()) {
|
||||
error_line = state->error_line;
|
||||
error_set = true;
|
||||
error_str = state->error;
|
||||
error = FAILED;
|
||||
}
|
||||
|
||||
if (r_completion_type != nullptr) {
|
||||
*r_completion_type = (int)state->completion_type;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const HashSet<String> &p_shader_types, bool p_is_include) {
|
||||
Token tk;
|
||||
TkPos prev_pos;
|
||||
|
|
@ -9500,97 +9509,6 @@ String ShaderLanguage::get_shader_type(const String &p_code) {
|
|||
return String();
|
||||
}
|
||||
|
||||
void ShaderLanguage::get_shader_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies) {
|
||||
bool reading_inc = false;
|
||||
String cur_identifier;
|
||||
|
||||
for (int i = _get_first_ident_pos(p_code); i < p_code.length(); i++) {
|
||||
if (p_code[i] == ';') {
|
||||
continue;
|
||||
|
||||
} else if (p_code[i] <= 32) {
|
||||
if (cur_identifier == "#include") {
|
||||
reading_inc = true;
|
||||
cur_identifier = String();
|
||||
} else {
|
||||
if (reading_inc) {
|
||||
String path = cur_identifier;
|
||||
if (path.begins_with("\"") && path.ends_with("\"")) {
|
||||
path = path.substr(1, path.length() - 2);
|
||||
if (!path.begins_with("res://")) {
|
||||
path = path.insert(0, "res://");
|
||||
}
|
||||
Ref<ShaderInclude> inc = ResourceLoader::load(path);
|
||||
if (inc.is_valid()) {
|
||||
r_dependencies->insert(inc);
|
||||
}
|
||||
}
|
||||
reading_inc = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cur_identifier += String::chr(p_code[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String ShaderLanguage::get_shader_type_and_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies) {
|
||||
bool read_type = true;
|
||||
bool reading_type = false;
|
||||
bool reading_inc = false;
|
||||
String type;
|
||||
|
||||
String cur_identifier;
|
||||
|
||||
for (int i = _get_first_ident_pos(p_code); i < p_code.length(); i++) {
|
||||
if (p_code[i] == ';') {
|
||||
continue;
|
||||
|
||||
} else if (p_code[i] <= 32) {
|
||||
if (!cur_identifier.is_empty()) {
|
||||
if (read_type) {
|
||||
if (!reading_type) {
|
||||
if (cur_identifier == "shader_type") {
|
||||
reading_type = true;
|
||||
cur_identifier = String();
|
||||
}
|
||||
} else {
|
||||
type = cur_identifier;
|
||||
read_type = false;
|
||||
cur_identifier = String();
|
||||
}
|
||||
} else if (cur_identifier == "#include") {
|
||||
reading_inc = true;
|
||||
cur_identifier = String();
|
||||
} else {
|
||||
if (reading_inc) {
|
||||
String path = cur_identifier;
|
||||
if (path.begins_with("\"") && path.ends_with("\"")) {
|
||||
path = path.substr(1, path.length() - 2);
|
||||
if (!path.begins_with("res://")) {
|
||||
path = path.insert(0, "res://");
|
||||
}
|
||||
Ref<ShaderInclude> inc = ResourceLoader::load(path);
|
||||
if (inc.is_valid()) {
|
||||
r_dependencies->insert(inc);
|
||||
}
|
||||
}
|
||||
reading_inc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cur_identifier += String::chr(p_code[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (reading_type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void ShaderLanguage::_check_warning_accums() {
|
||||
for (const KeyValue<ShaderWarning::Code, HashMap<StringName, HashMap<StringName, Usage>> *> &E : warnings_check_map2) {
|
||||
|
|
@ -9630,21 +9548,15 @@ uint32_t ShaderLanguage::get_warning_flags() const {
|
|||
Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_info) {
|
||||
clear();
|
||||
|
||||
Error err = _preprocess_shader(p_code, code);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Clear after preprocessing. Because preprocess uses the resource loader, it means if this instance is held in a singleton, it can have a changed state after.
|
||||
clear();
|
||||
|
||||
code = p_code;
|
||||
global_var_get_type_func = p_info.global_variable_type_func;
|
||||
|
||||
varying_function_names = p_info.varying_function_names;
|
||||
|
||||
nodes = nullptr;
|
||||
|
||||
shader = alloc_node<ShaderNode>();
|
||||
err = _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types, p_info.is_include);
|
||||
Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types, p_info.is_include);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings) {
|
||||
|
|
@ -9661,44 +9573,7 @@ Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_i
|
|||
Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_info, List<ScriptLanguage::CodeCompletionOption> *r_options, String &r_call_hint) {
|
||||
clear();
|
||||
|
||||
int preprocessor_completion_type;
|
||||
Error error = _preprocess_shader(p_code, code, &preprocessor_completion_type);
|
||||
|
||||
switch (preprocessor_completion_type) {
|
||||
case ShaderPreprocessor::COMPLETION_TYPE_DIRECTIVE: {
|
||||
static List<String> options;
|
||||
|
||||
if (options.is_empty()) {
|
||||
ShaderPreprocessor::get_keyword_list(&options, true);
|
||||
}
|
||||
|
||||
for (const String &E : options) {
|
||||
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
|
||||
r_options->push_back(option);
|
||||
}
|
||||
|
||||
return OK;
|
||||
} break;
|
||||
case ShaderPreprocessor::COMPLETION_TYPE_PRAGMA: {
|
||||
static List<String> options;
|
||||
|
||||
if (options.is_empty()) {
|
||||
ShaderPreprocessor::get_pragma_list(&options);
|
||||
}
|
||||
|
||||
for (const String &E : options) {
|
||||
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
|
||||
r_options->push_back(option);
|
||||
}
|
||||
|
||||
return OK;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (error != OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
code = p_code;
|
||||
varying_function_names = p_info.varying_function_names;
|
||||
|
||||
nodes = nullptr;
|
||||
|
|
@ -10231,6 +10106,10 @@ String ShaderLanguage::get_error_text() {
|
|||
return error_str;
|
||||
}
|
||||
|
||||
Vector<ShaderLanguage::FilePosition> ShaderLanguage::get_include_positions() {
|
||||
return include_positions;
|
||||
}
|
||||
|
||||
int ShaderLanguage::get_error_line() {
|
||||
return error_line;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -777,7 +777,6 @@ public:
|
|||
static uint32_t get_datatype_size(DataType p_type);
|
||||
|
||||
static void get_keyword_list(List<String> *r_keywords);
|
||||
static void get_preprocessor_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords);
|
||||
static bool is_control_flow_keyword(String p_keyword);
|
||||
static void get_builtin_funcs(List<String> *r_keywords);
|
||||
|
||||
|
|
@ -869,6 +868,11 @@ public:
|
|||
|
||||
typedef DataType (*GlobalVariableGetTypeFunc)(const StringName &p_name);
|
||||
|
||||
struct FilePosition {
|
||||
String file;
|
||||
int line = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
struct KeyWord {
|
||||
TokenType token;
|
||||
|
|
@ -886,6 +890,8 @@ private:
|
|||
String error_str;
|
||||
int error_line = 0;
|
||||
|
||||
Vector<FilePosition> include_positions;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
struct Usage {
|
||||
int decl_line;
|
||||
|
|
@ -953,6 +959,7 @@ private:
|
|||
error_line = tk_line;
|
||||
error_set = true;
|
||||
error_str = p_str;
|
||||
include_positions.write[include_positions.size() - 1].line = tk_line;
|
||||
}
|
||||
|
||||
void _set_expected_error(const String &p_what) {
|
||||
|
|
@ -1072,7 +1079,6 @@ private:
|
|||
String _get_shader_type_list(const HashSet<String> &p_shader_types) const;
|
||||
String _get_qualifier_str(ArgumentQualifier p_qualifier) const;
|
||||
|
||||
Error _preprocess_shader(const String &p_code, String &r_result, int *r_completion_type = nullptr);
|
||||
Error _parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const HashSet<String> &p_shader_types, bool p_is_include);
|
||||
|
||||
Error _find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op);
|
||||
|
|
@ -1094,8 +1100,6 @@ public:
|
|||
void clear();
|
||||
|
||||
static String get_shader_type(const String &p_code);
|
||||
static void get_shader_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies);
|
||||
static String get_shader_type_and_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies);
|
||||
|
||||
struct ShaderCompileInfo {
|
||||
HashMap<StringName, FunctionInfo> functions;
|
||||
|
|
@ -1110,6 +1114,7 @@ public:
|
|||
Error complete(const String &p_code, const ShaderCompileInfo &p_info, List<ScriptLanguage::CodeCompletionOption> *r_options, String &r_call_hint);
|
||||
|
||||
String get_error_text();
|
||||
Vector<FilePosition> get_include_positions();
|
||||
int get_error_line();
|
||||
|
||||
ShaderNode *get_shader();
|
||||
|
|
|
|||
|
|
@ -555,6 +555,15 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) {
|
|||
|
||||
p_tokenizer->advance('"');
|
||||
String path = tokens_to_string(p_tokenizer->advance('"'));
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
if (path[i] == '\n') {
|
||||
break; //stop parsing
|
||||
}
|
||||
if (path[i] == CURSOR) {
|
||||
state->completion_type = COMPLETION_TYPE_INCLUDE_PATH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
path = path.substr(0, path.length() - 1);
|
||||
p_tokenizer->skip_whitespace();
|
||||
|
||||
|
|
@ -569,7 +578,7 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) {
|
|||
return;
|
||||
}
|
||||
|
||||
Ref<ShaderInclude> shader_inc = Object::cast_to<ShaderInclude>(*res);
|
||||
Ref<ShaderInclude> shader_inc = res;
|
||||
if (shader_inc.is_null()) {
|
||||
set_error(RTR("Shader include resource type is wrong."), line);
|
||||
return;
|
||||
|
|
@ -584,6 +593,8 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) {
|
|||
}
|
||||
}
|
||||
|
||||
state->shader_includes.insert(shader_inc);
|
||||
|
||||
const String real_path = shader_inc->get_path();
|
||||
if (state->includes.has(real_path)) {
|
||||
// Already included, skip.
|
||||
|
|
@ -603,18 +614,26 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) {
|
|||
|
||||
String old_include = state->current_include;
|
||||
state->current_include = real_path;
|
||||
ShaderPreprocessor processor(included);
|
||||
ShaderPreprocessor processor;
|
||||
|
||||
int prev_condition_depth = state->condition_depth;
|
||||
state->condition_depth = 0;
|
||||
|
||||
FilePosition fp;
|
||||
fp.file = state->current_include;
|
||||
fp.line = line;
|
||||
state->include_positions.push_back(fp);
|
||||
|
||||
String result;
|
||||
processor.preprocess(state, result);
|
||||
processor.preprocess(state, included, result);
|
||||
add_to_output("@@>" + real_path + "\n"); // Add token for enter include path
|
||||
add_to_output(result);
|
||||
add_to_output("\n@@<\n"); // Add token for exit include path
|
||||
|
||||
// Reset to last include if there are no errors. We want to use this as context.
|
||||
if (state->error.is_empty()) {
|
||||
state->current_include = old_include;
|
||||
state->include_positions.pop_back();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
@ -855,7 +874,9 @@ void ShaderPreprocessor::add_to_output(const String &p_str) {
|
|||
void ShaderPreprocessor::set_error(const String &p_error, int p_line) {
|
||||
if (state->error.is_empty()) {
|
||||
state->error = p_error;
|
||||
state->error_line = p_line + 1;
|
||||
FilePosition fp;
|
||||
fp.line = p_line + 1;
|
||||
state->include_positions.push_back(fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -867,18 +888,6 @@ bool ShaderPreprocessor::check_directive_before_type(Tokenizer *p_tokenizer, con
|
|||
return true;
|
||||
}
|
||||
|
||||
ShaderPreprocessor::State *ShaderPreprocessor::create_state() {
|
||||
State *new_state = memnew(State);
|
||||
|
||||
String platform = OS::get_singleton()->get_name().replace(" ", "_").to_upper();
|
||||
new_state->defines[platform] = create_define("true");
|
||||
|
||||
Engine *engine = Engine::get_singleton();
|
||||
new_state->defines["EDITOR"] = create_define(engine->is_editor_hint() ? "true" : "false");
|
||||
|
||||
return new_state;
|
||||
}
|
||||
|
||||
ShaderPreprocessor::Define *ShaderPreprocessor::create_define(const String &p_body) {
|
||||
ShaderPreprocessor::Define *define = memnew(Define);
|
||||
define->body = p_body;
|
||||
|
|
@ -903,18 +912,14 @@ void ShaderPreprocessor::clear() {
|
|||
state = nullptr;
|
||||
}
|
||||
|
||||
Error ShaderPreprocessor::preprocess(State *p_state, String &r_result) {
|
||||
Error ShaderPreprocessor::preprocess(State *p_state, const String &p_code, String &r_result) {
|
||||
clear();
|
||||
|
||||
output.clear();
|
||||
|
||||
state = p_state;
|
||||
if (state == nullptr) {
|
||||
state = create_state();
|
||||
state_owner = true;
|
||||
}
|
||||
|
||||
CommentRemover remover(code);
|
||||
CommentRemover remover(p_code);
|
||||
String stripped = remover.strip();
|
||||
String error = remover.get_error();
|
||||
if (!error.is_empty()) {
|
||||
|
|
@ -923,7 +928,7 @@ Error ShaderPreprocessor::preprocess(State *p_state, String &r_result) {
|
|||
}
|
||||
|
||||
// Track code hashes to prevent cyclic include.
|
||||
uint64_t code_hash = code.hash64();
|
||||
uint64_t code_hash = p_code.hash64();
|
||||
state->cyclic_include_hashes.push_back(code_hash);
|
||||
|
||||
Tokenizer p_tokenizer(stripped);
|
||||
|
|
@ -990,12 +995,55 @@ Error ShaderPreprocessor::preprocess(State *p_state, String &r_result) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error ShaderPreprocessor::preprocess(String &r_result) {
|
||||
return preprocess(nullptr, r_result);
|
||||
}
|
||||
Error ShaderPreprocessor::preprocess(const String &p_code, String &r_result, String *r_error_text, List<FilePosition> *r_error_position, HashSet<Ref<ShaderInclude>> *r_includes, List<ScriptLanguage::CodeCompletionOption> *r_completion_options, IncludeCompletionFunction p_include_completion_func) {
|
||||
State pp_state;
|
||||
Error err = preprocess(&pp_state, p_code, r_result);
|
||||
if (err != OK) {
|
||||
if (r_error_text) {
|
||||
*r_error_text = pp_state.error;
|
||||
}
|
||||
if (r_error_position) {
|
||||
*r_error_position = pp_state.include_positions;
|
||||
}
|
||||
}
|
||||
if (r_includes) {
|
||||
*r_includes = pp_state.shader_includes;
|
||||
}
|
||||
|
||||
ShaderPreprocessor::State *ShaderPreprocessor::get_state() {
|
||||
return state;
|
||||
if (r_completion_options) {
|
||||
switch (pp_state.completion_type) {
|
||||
case COMPLETION_TYPE_DIRECTIVE: {
|
||||
List<String> options;
|
||||
get_keyword_list(&options, true);
|
||||
|
||||
for (const String &E : options) {
|
||||
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
|
||||
r_completion_options->push_back(option);
|
||||
}
|
||||
|
||||
} break;
|
||||
case COMPLETION_TYPE_PRAGMA: {
|
||||
List<String> options;
|
||||
|
||||
ShaderPreprocessor::get_pragma_list(&options);
|
||||
|
||||
for (const String &E : options) {
|
||||
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
|
||||
r_completion_options->push_back(option);
|
||||
}
|
||||
|
||||
} break;
|
||||
case COMPLETION_TYPE_INCLUDE_PATH: {
|
||||
if (p_include_completion_func && r_completion_options) {
|
||||
p_include_completion_func(r_completion_options);
|
||||
}
|
||||
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void ShaderPreprocessor::get_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords) {
|
||||
|
|
@ -1018,8 +1066,7 @@ void ShaderPreprocessor::get_pragma_list(List<String> *r_pragmas) {
|
|||
r_pragmas->push_back("disable_preprocessor");
|
||||
}
|
||||
|
||||
ShaderPreprocessor::ShaderPreprocessor(const String &p_code) :
|
||||
code(p_code) {
|
||||
ShaderPreprocessor::ShaderPreprocessor() {
|
||||
}
|
||||
|
||||
ShaderPreprocessor::~ShaderPreprocessor() {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,20 @@
|
|||
#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_INCLUDE_PATH,
|
||||
};
|
||||
|
||||
struct FilePosition {
|
||||
String file;
|
||||
int line = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
struct Token {
|
||||
char32_t text;
|
||||
|
|
@ -113,14 +127,6 @@ private:
|
|||
int end_line = -1;
|
||||
};
|
||||
|
||||
public:
|
||||
enum CompletionType {
|
||||
COMPLETION_TYPE_NONE,
|
||||
COMPLETION_TYPE_DIRECTIVE,
|
||||
COMPLETION_TYPE_PRAGMA_DIRECTIVE,
|
||||
COMPLETION_TYPE_PRAGMA,
|
||||
};
|
||||
|
||||
struct State {
|
||||
RBMap<String, Define *> defines;
|
||||
Vector<bool> skip_stack_else;
|
||||
|
|
@ -132,14 +138,14 @@ public:
|
|||
String current_shader_type;
|
||||
int shader_type_pos = -1;
|
||||
String error;
|
||||
int error_line = -1;
|
||||
List<FilePosition> include_positions;
|
||||
RBMap<String, Vector<SkippedCondition *>> skipped_conditions;
|
||||
bool disabled = false;
|
||||
CompletionType completion_type = COMPLETION_TYPE_NONE;
|
||||
HashSet<Ref<ShaderInclude>> shader_includes;
|
||||
};
|
||||
|
||||
private:
|
||||
String code;
|
||||
LocalVector<char32_t> output;
|
||||
State *state = nullptr;
|
||||
bool state_owner = false;
|
||||
|
|
@ -175,21 +181,21 @@ private:
|
|||
void set_error(const String &p_error, int p_line);
|
||||
bool check_directive_before_type(Tokenizer *p_tokenizer, const String &p_directive);
|
||||
|
||||
static State *create_state();
|
||||
static Define *create_define(const String &p_body);
|
||||
|
||||
void clear();
|
||||
|
||||
Error preprocess(State *p_state, const String &p_code, String &r_result);
|
||||
|
||||
public:
|
||||
Error preprocess(State *p_state, String &r_result);
|
||||
Error preprocess(String &r_result);
|
||||
typedef void (*IncludeCompletionFunction)(List<ScriptLanguage::CodeCompletionOption> *);
|
||||
|
||||
State *get_state();
|
||||
Error preprocess(const String &p_code, String &r_result, String *r_error_text = nullptr, List<FilePosition> *r_error_position = nullptr, HashSet<Ref<ShaderInclude>> *r_includes = nullptr, List<ScriptLanguage::CodeCompletionOption> *r_completion_options = nullptr, IncludeCompletionFunction p_include_completion_func = nullptr);
|
||||
|
||||
static void get_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords = false);
|
||||
static void get_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords);
|
||||
static void get_pragma_list(List<String> *r_pragmas);
|
||||
|
||||
ShaderPreprocessor(const String &p_code);
|
||||
ShaderPreprocessor();
|
||||
~ShaderPreprocessor();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public:
|
|||
virtual void shader_free(RID p_rid) = 0;
|
||||
|
||||
virtual void shader_set_code(RID p_shader, const String &p_code) = 0;
|
||||
virtual void shader_set_path_hint(RID p_shader, const String &p_path) = 0;
|
||||
virtual String shader_get_code(RID p_shader) const = 0;
|
||||
virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1709,6 +1709,8 @@ void RenderingServer::_bind_methods() {
|
|||
/* SHADER */
|
||||
|
||||
ClassDB::bind_method(D_METHOD("shader_create"), &RenderingServer::shader_create);
|
||||
ClassDB::bind_method(D_METHOD("shader_set_code", "shader", "code"), &RenderingServer::shader_set_code);
|
||||
ClassDB::bind_method(D_METHOD("shader_set_path_hint", "shader", "path"), &RenderingServer::shader_set_path_hint);
|
||||
ClassDB::bind_method(D_METHOD("shader_get_code", "shader"), &RenderingServer::shader_get_code);
|
||||
ClassDB::bind_method(D_METHOD("shader_get_param_list", "shader"), &RenderingServer::_shader_get_param_list);
|
||||
ClassDB::bind_method(D_METHOD("shader_get_param_default", "shader", "param"), &RenderingServer::shader_get_param_default);
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ public:
|
|||
virtual RID shader_create() = 0;
|
||||
|
||||
virtual void shader_set_code(RID p_shader, const String &p_code) = 0;
|
||||
virtual void shader_set_path_hint(RID p_shader, const String &p_path) = 0;
|
||||
virtual String shader_get_code(RID p_shader) const = 0;
|
||||
virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const = 0;
|
||||
virtual Variant shader_get_param_default(RID p_shader, const StringName &p_param) const = 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue