Convert 3.x shaders
This commit is contained in:
parent
324ad7571a
commit
018f1ed97e
|
@ -42,6 +42,10 @@
|
|||
#include "servers/rendering/shader_preprocessor.h"
|
||||
#include "servers/rendering/shader_types.h"
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#include "servers/rendering/shader_converter.h"
|
||||
#endif
|
||||
|
||||
/*** SHADER SYNTAX HIGHLIGHTER ****/
|
||||
|
||||
Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
|
||||
|
@ -699,6 +703,28 @@ void TextShaderEditor::_menu_option(int p_option) {
|
|||
case EDIT_COMPLETE: {
|
||||
code_editor->get_text_editor()->request_code_completion();
|
||||
} break;
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
case EDIT_CONVERT: {
|
||||
if (shader.is_null()) {
|
||||
return;
|
||||
}
|
||||
String code = code_editor->get_text_editor()->get_text();
|
||||
if (code.is_empty()) {
|
||||
return;
|
||||
}
|
||||
ShaderDeprecatedConverter converter;
|
||||
if (!converter.is_code_deprecated(code)) {
|
||||
if (converter.get_error_text() != String()) {
|
||||
shader_convert_error_dialog->set_text(vformat(RTR("Line %d: %s"), converter.get_error_line(), converter.get_error_text()));
|
||||
shader_convert_error_dialog->popup_centered();
|
||||
ERR_PRINT("Shader conversion failed: " + converter.get_error_text());
|
||||
}
|
||||
confirm_convert_shader->popup_centered();
|
||||
return;
|
||||
}
|
||||
_convert_shader();
|
||||
} break;
|
||||
#endif
|
||||
case SEARCH_FIND: {
|
||||
code_editor->get_find_replace_bar()->popup_search();
|
||||
} break;
|
||||
|
@ -755,6 +781,36 @@ void TextShaderEditor::_notification(int p_what) {
|
|||
} break;
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void TextShaderEditor::_convert_shader() {
|
||||
if (shader.is_null()) {
|
||||
return;
|
||||
}
|
||||
String code = code_editor->get_text_editor()->get_text();
|
||||
if (code.is_empty()) {
|
||||
return;
|
||||
}
|
||||
ShaderDeprecatedConverter converter;
|
||||
if (!converter.convert_code(code)) {
|
||||
String err_text = converter.get_error_text();
|
||||
if (err_text.is_empty()) {
|
||||
err_text = TTR("Unknown error occurred while converting the shader.");
|
||||
} else if (converter.get_error_line() > 0) {
|
||||
err_text = vformat("%s (line %d)", err_text, converter.get_error_line());
|
||||
}
|
||||
|
||||
shader_convert_error_dialog->set_text(err_text);
|
||||
shader_convert_error_dialog->popup_centered();
|
||||
ERR_PRINT("Shader conversion failed: " + err_text);
|
||||
return;
|
||||
}
|
||||
String new_code = converter.emit_code();
|
||||
// Ensure undoable.
|
||||
code_editor->get_text_editor()->set_text(new_code);
|
||||
code_editor->get_text_editor()->tag_saved_version();
|
||||
code_editor->_validate_script();
|
||||
}
|
||||
#endif
|
||||
|
||||
void TextShaderEditor::_editor_settings_changed() {
|
||||
if (!EditorThemeManager::is_generated_theme_outdated() &&
|
||||
|
@ -1172,6 +1228,11 @@ TextShaderEditor::TextShaderEditor() {
|
|||
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE);
|
||||
edit_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option));
|
||||
|
||||
edit_menu->get_popup()->add_separator();
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
edit_menu->get_popup()->add_item(TTR("Convert 3.x Shader"), EDIT_CONVERT);
|
||||
edit_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option));
|
||||
#endif
|
||||
search_menu = memnew(MenuButton);
|
||||
search_menu->set_shortcut_context(this);
|
||||
search_menu->set_text(TTR("Search"));
|
||||
|
@ -1254,7 +1315,20 @@ TextShaderEditor::TextShaderEditor() {
|
|||
disk_changed->connect("custom_action", callable_mp(this, &TextShaderEditor::save_external_data));
|
||||
|
||||
add_child(disk_changed);
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
shader_convert_error_dialog = memnew(AcceptDialog);
|
||||
shader_convert_error_dialog->set_title(TTR("Error converting shader"));
|
||||
shader_convert_error_dialog->set_hide_on_ok(true);
|
||||
add_child(shader_convert_error_dialog);
|
||||
|
||||
confirm_convert_shader = memnew(ConfirmationDialog);
|
||||
confirm_convert_shader->set_title(TTR("Confirm Convert 3.x Shader"));
|
||||
confirm_convert_shader->set_text(TTR("This shader does not appear to be a 3.x shader.\nAre you sure you want to convert it?"));
|
||||
confirm_convert_shader->get_ok_button()->set_text(TTR("Convert"));
|
||||
confirm_convert_shader->get_cancel_button()->set_text(TTR("Cancel"));
|
||||
confirm_convert_shader->connect("confirmed", callable_mp(this, &TextShaderEditor::_convert_shader));
|
||||
add_child(confirm_convert_shader);
|
||||
#endif
|
||||
_editor_settings_changed();
|
||||
code_editor->show_toggle_scripts_button(); // TODO: Disabled for now, because it doesn't work properly.
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ class TextShaderEditor : public ShaderEditor {
|
|||
EDIT_TOGGLE_WORD_WRAP,
|
||||
EDIT_TOGGLE_COMMENT,
|
||||
EDIT_COMPLETE,
|
||||
EDIT_CONVERT,
|
||||
SEARCH_FIND,
|
||||
SEARCH_FIND_NEXT,
|
||||
SEARCH_FIND_PREV,
|
||||
|
@ -150,13 +151,20 @@ class TextShaderEditor : public ShaderEditor {
|
|||
ConfirmationDialog *disk_changed = nullptr;
|
||||
|
||||
ShaderTextEditor *code_editor = nullptr;
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
AcceptDialog *shader_convert_error_dialog = nullptr;
|
||||
ConfirmationDialog *confirm_convert_shader = nullptr;
|
||||
#endif
|
||||
|
||||
bool compilation_success = true;
|
||||
|
||||
void _menu_option(int p_option);
|
||||
void _prepare_edit_menu();
|
||||
mutable Ref<Shader> shader;
|
||||
mutable Ref<ShaderInclude> shader_inc;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _convert_shader();
|
||||
#endif
|
||||
void _editor_settings_changed();
|
||||
void _apply_editor_settings();
|
||||
void _project_settings_changed();
|
||||
|
|
|
@ -32,8 +32,11 @@
|
|||
#include "shader.compat.inc"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "servers/rendering/rendering_server_globals.h"
|
||||
#include "servers/rendering/shader_language.h"
|
||||
#include "servers/rendering/shader_preprocessor.h"
|
||||
#include "servers/rendering/shader_types.h"
|
||||
#include "servers/rendering_server.h"
|
||||
#include "texture.h"
|
||||
|
||||
|
@ -46,6 +49,12 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#include "servers/rendering/shader_converter.h"
|
||||
#endif
|
||||
|
||||
#define _LOAD_COMPAT_META_PROPERTY "_load_compat"
|
||||
|
||||
Shader::Mode Shader::get_mode() const {
|
||||
return mode;
|
||||
}
|
||||
|
@ -70,6 +79,13 @@ void Shader::set_include_path(const String &p_path) {
|
|||
include_path = p_path;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
ShaderLanguage::DataType _get_global_shader_uniform_type(const StringName &p_name) {
|
||||
RS::GlobalShaderParameterType gvt = RenderingServerGlobals::material_storage->global_shader_parameter_get_type(p_name);
|
||||
return (ShaderLanguage::DataType)RS::global_shader_uniform_type_get_shader_datatype(gvt);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Shader::set_code(const String &p_code) {
|
||||
for (const Ref<ShaderInclude> &E : include_dependencies) {
|
||||
E->disconnect_changed(callable_mp(this, &Shader::_dependency_changed));
|
||||
|
@ -77,6 +93,43 @@ void Shader::set_code(const String &p_code) {
|
|||
|
||||
code = p_code;
|
||||
String pp_code = p_code;
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (get_meta(_LOAD_COMPAT_META_PROPERTY, false)) {
|
||||
// check if the Shader code compiles; if not, it's probably an old shader.
|
||||
|
||||
ShaderLanguage sl;
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
String mode_string = ShaderLanguage::get_shader_type(p_code);
|
||||
|
||||
RS::ShaderMode new_mode;
|
||||
if (mode_string == "canvas_item") {
|
||||
new_mode = RS::SHADER_CANVAS_ITEM;
|
||||
} else if (mode_string == "particles") {
|
||||
new_mode = RS::SHADER_PARTICLES;
|
||||
} else if (mode_string == "spatial") {
|
||||
new_mode = RS::SHADER_SPATIAL;
|
||||
} else {
|
||||
new_mode = RS::SHADER_MAX;
|
||||
}
|
||||
if (new_mode != RS::SHADER_MAX) {
|
||||
info.functions = ShaderTypes::get_singleton()->get_functions(new_mode);
|
||||
info.render_modes = ShaderTypes::get_singleton()->get_modes(new_mode);
|
||||
info.shader_types = ShaderTypes::get_singleton()->get_types();
|
||||
info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
|
||||
Error err = sl.compile(p_code, info);
|
||||
if (err) {
|
||||
ShaderDeprecatedConverter sdc;
|
||||
if (sdc.is_code_deprecated(p_code)) {
|
||||
ERR_FAIL_COND_MSG(!sdc.convert_code(p_code), vformat("Shader conversion failed (line %d): %s", sdc.get_error_line(), sdc.get_error_text()));
|
||||
code = sdc.emit_code();
|
||||
pp_code = code;
|
||||
} else if (sdc.get_error_text() != "") { // Preprocessing failed.
|
||||
WARN_PRINT(vformat("Shader conversion failed (line %d): %s", sdc.get_error_line(), sdc.get_error_text()));
|
||||
} // If the code is reported as not deprecated, let it fall through to the compile step after this if block so that we get the full compile error.
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
String path = get_path();
|
||||
|
@ -88,7 +141,7 @@ void Shader::set_code(const String &p_code) {
|
|||
// 2) Server does not do interaction with Resource filetypes, this is a scene level feature.
|
||||
HashSet<Ref<ShaderInclude>> new_include_dependencies;
|
||||
ShaderPreprocessor preprocessor;
|
||||
Error result = preprocessor.preprocess(p_code, path, pp_code, nullptr, nullptr, nullptr, &new_include_dependencies);
|
||||
Error result = preprocessor.preprocess(code, path, pp_code, nullptr, nullptr, nullptr, &new_include_dependencies);
|
||||
if (result == OK) {
|
||||
// This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower)
|
||||
include_dependencies = new_include_dependencies;
|
||||
|
@ -237,6 +290,20 @@ Array Shader::_get_shader_uniform_list(bool p_get_groups) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void Shader::_start_load(const StringName &p_res_format_type, int p_res_format_version) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if ((p_res_format_type == "binary" && p_res_format_version == 3) || (p_res_format_type == "text" && p_res_format_version == 2)) {
|
||||
set_meta(_LOAD_COMPAT_META_PROPERTY, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Shader::_finish_load(const StringName &p_res_format_type, int p_res_format_version) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
set_meta(_LOAD_COMPAT_META_PROPERTY, Variant());
|
||||
#endif
|
||||
}
|
||||
|
||||
void Shader::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_mode"), &Shader::get_mode);
|
||||
|
||||
|
|
|
@ -94,6 +94,9 @@ public:
|
|||
|
||||
virtual RID get_rid() const override;
|
||||
|
||||
virtual void _start_load(const StringName &p_res_format_type, int p_res_format_version) override;
|
||||
virtual void _finish_load(const StringName &p_res_format_type, int p_res_format_version) override;
|
||||
|
||||
Shader();
|
||||
~Shader();
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,284 @@
|
|||
/**************************************************************************/
|
||||
/* shader_converter.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_CONVERTER_H
|
||||
#define SHADER_CONVERTER_H
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
#include "core/templates/pair.h"
|
||||
#include "servers/rendering/shader_language.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
class ShaderDeprecatedConverter {
|
||||
public:
|
||||
using TokenType = ShaderLanguage::TokenType;
|
||||
using Token = ShaderLanguage::Token;
|
||||
using TT = TokenType;
|
||||
using TokenE = List<Token>::Element;
|
||||
|
||||
ShaderDeprecatedConverter() {};
|
||||
bool is_code_deprecated(const String &p_code);
|
||||
bool convert_code(const String &p_code);
|
||||
String get_error_text() const;
|
||||
int get_error_line() const;
|
||||
String emit_code() const;
|
||||
void set_warning_comments(bool p_add_comments);
|
||||
void set_fail_on_unported(bool p_fail_on_unported);
|
||||
void set_assume_correct(bool p_assume_correct);
|
||||
void set_force_reserved_word_replacement(bool p_force_reserved_word_replacement);
|
||||
void set_verbose_comments(bool p_verbose_comments);
|
||||
|
||||
static bool tokentype_is_identifier(const TokenType &p_tk_type);
|
||||
static bool tokentype_is_new_reserved_keyword(const TokenType &p_tk_type);
|
||||
static bool tokentype_is_new_type(const TokenType &p_tk_type);
|
||||
static bool tokentype_is_new_hint(const TokenType &p_tk);
|
||||
|
||||
static bool id_is_new_builtin_func(const String &p_name);
|
||||
|
||||
static String get_tokentype_text(TokenType p_tk_type);
|
||||
|
||||
static bool has_builtin_rename(RS::ShaderMode p_mode, const String &p_name, const String &p_function = "");
|
||||
static String get_builtin_rename(const String &p_name);
|
||||
|
||||
static bool has_hint_replacement(const String &p_name);
|
||||
static TokenType get_hint_replacement(const String &p_name);
|
||||
|
||||
static bool is_renamed_render_mode(RS::ShaderMode p_mode, const String &p_name);
|
||||
static String get_render_mode_rename(const String &p_name);
|
||||
|
||||
static bool has_removed_render_mode(RS::ShaderMode p_mode, const String &p_name);
|
||||
static bool can_remove_render_mode(const String &p_name);
|
||||
|
||||
static bool has_removed_type(const String &p_name);
|
||||
|
||||
static bool is_renamed_main_function(RS::ShaderMode p_mode, const String &p_name);
|
||||
static bool is_renamee_main_function(RS::ShaderMode p_mode, const String &p_name);
|
||||
static String get_main_function_rename(const String &p_name);
|
||||
static TokenType get_renamed_function_type(const String &p_name);
|
||||
static int get_renamed_function_arg_count(const String &p_name);
|
||||
|
||||
static bool is_removed_builtin(RS::ShaderMode p_mode, const String &p_name, const String &p_function = "");
|
||||
static TokenType get_removed_builtin_uniform_type(const String &p_name);
|
||||
static Vector<TokenType> get_removed_builtin_hints(const String &p_name);
|
||||
|
||||
static bool _rename_has_special_handling(const String &p_name);
|
||||
|
||||
static void _get_builtin_renames_list(List<String> *r_list);
|
||||
static void _get_render_mode_renames_list(List<String> *r_list);
|
||||
static void _get_hint_renames_list(List<String> *r_list);
|
||||
static void _get_function_renames_list(List<String> *r_list);
|
||||
static void _get_render_mode_removals_list(List<String> *r_list);
|
||||
static void _get_builtin_removals_list(List<String> *r_list);
|
||||
static void _get_type_removals_list(List<String> *r_list);
|
||||
static void _get_new_builtin_funcs_list(List<String> *r_list);
|
||||
static Vector<String> _get_funcs_builtin_rename(RS::ShaderMode p_mode, const String &p_name);
|
||||
static Vector<String> _get_funcs_builtin_removal(RS::ShaderMode p_mode, const String &p_name);
|
||||
|
||||
struct RenamedBuiltins {
|
||||
const char *name;
|
||||
const char *replacement;
|
||||
const Vector<Pair<RS::ShaderMode, Vector<String>>> mode_functions;
|
||||
const bool special_handling;
|
||||
};
|
||||
|
||||
struct RenamedRenderModes {
|
||||
const RS::ShaderMode mode;
|
||||
const char *name;
|
||||
const char *replacement;
|
||||
};
|
||||
|
||||
struct RenamedHints {
|
||||
const char *name;
|
||||
const ShaderLanguage::TokenType replacement;
|
||||
};
|
||||
|
||||
struct RenamedFunctions {
|
||||
const RS::ShaderMode mode;
|
||||
const ShaderLanguage::TokenType type;
|
||||
const int arg_count;
|
||||
const char *name;
|
||||
const char *replacement;
|
||||
};
|
||||
|
||||
struct RemovedRenderModes {
|
||||
const RS::ShaderMode mode;
|
||||
const char *name;
|
||||
const bool can_remove;
|
||||
};
|
||||
|
||||
struct RemovedBuiltins {
|
||||
const char *name;
|
||||
const ShaderLanguage::TokenType uniform_type;
|
||||
const Vector<ShaderLanguage::TokenType> hints;
|
||||
const Vector<Pair<RS::ShaderMode, Vector<String>>> mode_functions;
|
||||
};
|
||||
|
||||
private:
|
||||
struct UniformDecl {
|
||||
List<Token>::Element *start_pos = nullptr;
|
||||
List<Token>::Element *uniform_stmt_pos = nullptr;
|
||||
List<Token>::Element *end_pos = nullptr;
|
||||
List<Token>::Element *interp_qual_pos = nullptr;
|
||||
List<Token>::Element *type_pos = nullptr;
|
||||
List<Token>::Element *name_pos = nullptr;
|
||||
Vector<List<Token>::Element *> hint_poses;
|
||||
|
||||
bool is_array = false;
|
||||
bool has_uniform_qual() const {
|
||||
return start_pos != nullptr && ShaderLanguage::is_token_uniform_qual(start_pos->get().type);
|
||||
}
|
||||
bool has_interp_qual() const {
|
||||
return interp_qual_pos != nullptr;
|
||||
}
|
||||
};
|
||||
struct VarDecl {
|
||||
List<Token>::Element *start_pos = nullptr;
|
||||
List<Token>::Element *end_pos = nullptr; // semicolon or comma or right paren
|
||||
List<Token>::Element *type_pos = nullptr;
|
||||
List<Token>::Element *name_pos = nullptr;
|
||||
bool is_array = false;
|
||||
bool new_arr_style_decl = false;
|
||||
bool is_func_arg = false;
|
||||
void clear() {
|
||||
start_pos = nullptr;
|
||||
end_pos = nullptr;
|
||||
type_pos = nullptr;
|
||||
name_pos = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionDecl {
|
||||
List<Token>::Element *start_pos = nullptr;
|
||||
List<Token>::Element *type_pos = nullptr;
|
||||
List<Token>::Element *name_pos = nullptr;
|
||||
List<Token>::Element *args_start_pos = nullptr; // left paren
|
||||
List<Token>::Element *args_end_pos = nullptr; // right paren
|
||||
List<Token>::Element *body_start_pos = nullptr; // left curly
|
||||
List<Token>::Element *body_end_pos = nullptr; // right curly - end of function
|
||||
bool is_renamed_main_function(RS::ShaderMode p_mode) const;
|
||||
bool is_new_main_function(RS::ShaderMode p_mode) const;
|
||||
|
||||
int arg_count = 0;
|
||||
bool has_array_return_type = false;
|
||||
void clear() {
|
||||
type_pos = nullptr;
|
||||
name_pos = nullptr;
|
||||
args_start_pos = nullptr;
|
||||
args_end_pos = nullptr;
|
||||
body_start_pos = nullptr;
|
||||
body_end_pos = nullptr;
|
||||
}
|
||||
};
|
||||
static const RenamedBuiltins renamed_builtins[];
|
||||
static const RenamedRenderModes renamed_render_modes[];
|
||||
static const RenamedHints renamed_hints[];
|
||||
static const RenamedFunctions renamed_functions[];
|
||||
static const RemovedRenderModes removed_render_modes[];
|
||||
static const RemovedBuiltins removed_builtins[];
|
||||
static const char *removed_types[];
|
||||
static const char *old_builtin_funcs[];
|
||||
static HashSet<String> _new_builtin_funcs;
|
||||
String old_code;
|
||||
List<Token> code_tokens;
|
||||
List<Token>::Element *curr_ptr = nullptr;
|
||||
List<Token>::Element *after_shader_decl = nullptr;
|
||||
HashMap<String, UniformDecl> uniform_decls;
|
||||
HashMap<String, Vector<VarDecl>> var_decls;
|
||||
HashMap<String, FunctionDecl> function_decls;
|
||||
HashMap<String, HashSet<String>> scope_declarations;
|
||||
RenderingServer::ShaderMode shader_mode = RenderingServer::ShaderMode::SHADER_MAX;
|
||||
|
||||
bool warning_comments = true;
|
||||
bool verbose_comments = false;
|
||||
bool fail_on_unported = true;
|
||||
|
||||
bool function_pass_failed = false;
|
||||
bool var_pass_failed = false;
|
||||
String err_str;
|
||||
int err_line = 0;
|
||||
|
||||
Token eof_token{ ShaderLanguage::TK_EOF, {}, 0, 0, 0, 0 };
|
||||
|
||||
static RS::ShaderMode get_shader_mode_from_string(const String &p_mode);
|
||||
|
||||
String get_token_literal_text(const Token &p_tk) const;
|
||||
static Token mkTok(TokenType p_type, const StringName &p_text = StringName(), double constant = 0, uint16_t p_line = 0);
|
||||
static bool token_is_skippable(const Token &p_tk);
|
||||
static bool token_is_type(const Token &p_tk);
|
||||
static bool token_is_hint(const Token &p_tk);
|
||||
|
||||
void reset();
|
||||
bool _preprocess_code();
|
||||
List<Token>::Element *get_next_token();
|
||||
List<Token>::Element *get_prev_token();
|
||||
List<Token>::Element *remove_cur_and_get_next();
|
||||
TokenType peek_next_tk_type(uint32_t p_count = 1) const;
|
||||
TokenType peek_prev_tk_type(uint32_t p_count = 1) const;
|
||||
List<Token>::Element *get_pos() const;
|
||||
bool reset_to(List<Token>::Element *p_pos);
|
||||
bool insert_after(const Vector<Token> &p_token_list, List<Token>::Element *p_pos);
|
||||
bool insert_before(const Vector<Token> &p_token_list, List<Token>::Element *p_pos);
|
||||
bool insert_after(const Token &p_token, List<Token>::Element *p_pos);
|
||||
bool insert_before(const Token &p_token, List<Token>::Element *p_pos);
|
||||
List<Token>::Element *replace_curr(const Token &p_token);
|
||||
List<Token>::Element *_get_next_token_ptr(List<Token>::Element *p_curr_ptr) const;
|
||||
List<Token>::Element *_get_prev_token_ptr(List<Token>::Element *p_curr_ptr) const;
|
||||
TokenType _peek_tk_type(int64_t p_count, List<Token>::Element **r_pos = nullptr) const;
|
||||
|
||||
bool scope_has_decl(const String &p_scope, const String &p_name) const;
|
||||
bool _handle_new_keyword_rename(TokenType p_tk_type, const String &p_name, bool p_detected_3x, HashMap<TokenType, String> &p_func_renames);
|
||||
|
||||
bool _has_any_preprocessor_directives();
|
||||
bool _is_code_deprecated();
|
||||
bool _parse_uniform();
|
||||
static bool _tok_is_start_of_decl(const Token &p_tk);
|
||||
bool _skip_uniform();
|
||||
bool _parse_uniforms();
|
||||
bool _skip_array_size();
|
||||
bool _skip_struct();
|
||||
bool _check_deprecated_type(TokenE *p_type_tok);
|
||||
bool _add_warning_comment_before(const String &p_comment, List<Token>::Element *p_pos);
|
||||
bool _add_comment_at_eol(const String &p_comment, List<Token>::Element *p_pos);
|
||||
bool _process_func_decl_statement(TokenE *p_start_tok, TokenE *p_type_tok, bool p_second_pass = false);
|
||||
bool _process_decl_statement(TokenE *p_start_tok, TokenE *p_type_tok, const String &p_scope = "<global>", bool p_func_args = false);
|
||||
bool _parse_decls(bool p_first_pass);
|
||||
bool _insert_uniform_declaration(const String &p_name);
|
||||
List<Token>::Element *_remove_from_curr_to(List<Token>::Element *p_end);
|
||||
List<Token>::Element *_get_end_of_closure();
|
||||
static HashSet<String> _construct_new_builtin_funcs();
|
||||
|
||||
enum {
|
||||
NEW_IDENT = -1
|
||||
};
|
||||
};
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
#endif // SHADER_CONVERTER_H
|
|
@ -230,6 +230,13 @@ const char *ShaderLanguage::token_names[TK_MAX] = {
|
|||
"CURSOR",
|
||||
"ERROR",
|
||||
"EOF",
|
||||
"TAB",
|
||||
"CR",
|
||||
"SPACE",
|
||||
"NEWLINE",
|
||||
"BLOCK_COMMENT",
|
||||
"LINE_COMMENT",
|
||||
"PREPROC_DIRECTIVE",
|
||||
};
|
||||
|
||||
String ShaderLanguage::get_token_text(Token p_token) {
|
||||
|
@ -250,6 +257,8 @@ ShaderLanguage::Token ShaderLanguage::_make_token(TokenType p_type, const String
|
|||
tk.type = p_type;
|
||||
tk.text = p_text;
|
||||
tk.line = tk_line;
|
||||
tk.pos = tk_start_pos;
|
||||
tk.length = char_idx - tk_start_pos;
|
||||
if (tk.type == TK_ERROR) {
|
||||
_set_error(p_text);
|
||||
}
|
||||
|
@ -402,8 +411,11 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
|
|||
|
||||
ShaderLanguage::Token ShaderLanguage::_get_token() {
|
||||
#define GETCHAR(m_idx) (((char_idx + m_idx) < code.length()) ? code[char_idx + m_idx] : char32_t(0))
|
||||
|
||||
#define IF_DBG_MK_TK(m_type) \
|
||||
if (unlikely(debug_parse)) \
|
||||
return _make_token(m_type);
|
||||
while (true) {
|
||||
tk_start_pos = char_idx;
|
||||
char_idx++;
|
||||
switch (GETCHAR(-1)) {
|
||||
case 0:
|
||||
|
@ -411,11 +423,17 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
case 0xFFFF:
|
||||
return _make_token(TK_CURSOR); //for completion
|
||||
case '\t':
|
||||
IF_DBG_MK_TK(TK_TAB);
|
||||
continue;
|
||||
case '\r':
|
||||
IF_DBG_MK_TK(TK_CR);
|
||||
continue;
|
||||
case ' ':
|
||||
IF_DBG_MK_TK(TK_SPACE);
|
||||
continue;
|
||||
case '\n':
|
||||
tk_line++;
|
||||
IF_DBG_MK_TK(TK_NEWLINE);
|
||||
continue;
|
||||
case '/': {
|
||||
switch (GETCHAR(0)) {
|
||||
|
@ -424,10 +442,12 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
char_idx++;
|
||||
while (true) {
|
||||
if (GETCHAR(0) == 0) {
|
||||
IF_DBG_MK_TK(TK_BLOCK_COMMENT);
|
||||
return _make_token(TK_EOF);
|
||||
}
|
||||
if (GETCHAR(0) == '*' && GETCHAR(1) == '/') {
|
||||
char_idx += 2;
|
||||
IF_DBG_MK_TK(TK_BLOCK_COMMENT);
|
||||
break;
|
||||
} else if (GETCHAR(0) == '\n') {
|
||||
tk_line++;
|
||||
|
@ -441,11 +461,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
|
||||
while (true) {
|
||||
if (GETCHAR(0) == '\n') {
|
||||
IF_DBG_MK_TK(TK_LINE_COMMENT);
|
||||
tk_line++;
|
||||
char_idx++;
|
||||
break;
|
||||
}
|
||||
if (GETCHAR(0) == 0) {
|
||||
IF_DBG_MK_TK(TK_LINE_COMMENT);
|
||||
return _make_token(TK_EOF);
|
||||
}
|
||||
char_idx++;
|
||||
|
@ -702,6 +724,23 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
return _make_token(TK_ERROR, "Invalid include enter/exit hint token (@@> and @@<)");
|
||||
}
|
||||
} break;
|
||||
case '#': {
|
||||
if (!debug_parse) { // We shouldn't get here if the preprocessor is enabled and doing a non-debug parse.
|
||||
return _make_token(TK_ERROR, "Unexpected pre-processor directive (Is the pre-processor enabled?)");
|
||||
}
|
||||
while (true) {
|
||||
char32_t c = GETCHAR(0);
|
||||
if (c == '\\' && GETCHAR(1) == '\n') {
|
||||
char_idx += 2;
|
||||
tk_line++;
|
||||
continue;
|
||||
}
|
||||
if (c == '\n' || c == 0) {
|
||||
return _make_token(TK_PREPROC_DIRECTIVE);
|
||||
}
|
||||
char_idx++;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
char_idx--; //go back one, since we have no idea what this is
|
||||
|
||||
|
@ -767,7 +806,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
lut_case = CASE_EXPONENT;
|
||||
} else if (symbol == 'f' && !hexa_found) {
|
||||
if (!period_found && !exponent_found) {
|
||||
error = true;
|
||||
error = !debug_parse;
|
||||
}
|
||||
float_suffix_found = true;
|
||||
end_suffix_found = true;
|
||||
|
@ -886,6 +925,8 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
tk.constant = str.to_float();
|
||||
}
|
||||
tk.line = tk_line;
|
||||
tk.pos = tk_start_pos;
|
||||
tk.length = char_idx - tk_start_pos;
|
||||
|
||||
return tk;
|
||||
}
|
||||
|
@ -934,6 +975,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
|||
return Token();
|
||||
|
||||
#undef GETCHAR
|
||||
#undef IF_DBG_MK_TK
|
||||
}
|
||||
|
||||
bool ShaderLanguage::_lookup_next(Token &r_tk) {
|
||||
|
@ -956,22 +998,46 @@ ShaderLanguage::Token ShaderLanguage::_peek() {
|
|||
return tk;
|
||||
}
|
||||
|
||||
String ShaderLanguage::token_debug(const String &p_code) {
|
||||
String ShaderLanguage::token_debug(const String &p_code, bool p_debug_parse) {
|
||||
clear();
|
||||
|
||||
code = p_code;
|
||||
debug_parse = p_debug_parse;
|
||||
|
||||
String output;
|
||||
|
||||
Token tk = _get_token();
|
||||
|
||||
while (tk.type != TK_EOF && tk.type != TK_ERROR) {
|
||||
output += itos(tk_line) + ": " + get_token_text(tk) + "\n";
|
||||
String literal_text = p_code.substr(tk.pos, tk.length);
|
||||
output += itos(tk_line) + " (" + itos(tk.pos) + ":" + itos(tk.pos + tk.length) + "): " + get_token_text(tk) + " [" + literal_text;
|
||||
String suffix = "]\n";
|
||||
// add error string if invalid float constant
|
||||
if (debug_parse && tk.type == TK_FLOAT_CONSTANT && literal_text.ends_with("f") && !literal_text.contains(".") && !literal_text.contains("e")) {
|
||||
output += " (Invalid float constant)]\n";
|
||||
} else {
|
||||
output += "]\n";
|
||||
}
|
||||
tk = _get_token();
|
||||
}
|
||||
debug_parse = false;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void ShaderLanguage::token_debug_stream(const String &p_code, List<Token> &r_output, bool p_debug_parse) {
|
||||
clear();
|
||||
|
||||
code = p_code;
|
||||
debug_parse = p_debug_parse;
|
||||
Token tk = _get_token();
|
||||
|
||||
while (tk.type != TK_EOF && tk.type != TK_ERROR) {
|
||||
r_output.push_back(tk);
|
||||
tk = _get_token();
|
||||
}
|
||||
debug_parse = false;
|
||||
}
|
||||
|
||||
bool ShaderLanguage::is_token_variable_datatype(TokenType p_type) {
|
||||
return (
|
||||
p_type == TK_TYPE_VOID ||
|
||||
|
@ -1064,6 +1130,12 @@ bool ShaderLanguage::is_token_arg_qual(TokenType p_type) {
|
|||
p_type == TK_ARG_INOUT);
|
||||
}
|
||||
|
||||
bool ShaderLanguage::is_token_uniform_qual(TokenType p_type) {
|
||||
return (
|
||||
p_type == TK_INSTANCE ||
|
||||
p_type == TK_GLOBAL);
|
||||
}
|
||||
|
||||
ShaderLanguage::DataPrecision ShaderLanguage::get_token_precision(TokenType p_type) {
|
||||
if (p_type == TK_PRECISION_LOW) {
|
||||
return PRECISION_LOWP;
|
||||
|
@ -4118,6 +4190,17 @@ bool ShaderLanguage::is_token_hint(TokenType p_type) {
|
|||
return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE);
|
||||
}
|
||||
|
||||
bool ShaderLanguage::is_token_keyword(TokenType p_type) {
|
||||
int idx = 0;
|
||||
while (keyword_list[idx].text) {
|
||||
if (keyword_list[idx].token == p_type) {
|
||||
return true;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) {
|
||||
if (p_constant->datatype == p_to_type) {
|
||||
if (p_value) {
|
||||
|
|
|
@ -194,7 +194,15 @@ public:
|
|||
TK_CURSOR,
|
||||
TK_ERROR,
|
||||
TK_EOF,
|
||||
TK_MAX
|
||||
TK_TAB, // for debug purposes
|
||||
TK_CR,
|
||||
TK_SPACE,
|
||||
TK_NEWLINE,
|
||||
TK_BLOCK_COMMENT,
|
||||
TK_LINE_COMMENT,
|
||||
TK_PREPROC_DIRECTIVE,
|
||||
TK_MAX,
|
||||
TK_REG_MAX = TK_TAB,
|
||||
};
|
||||
|
||||
/* COMPILER */
|
||||
|
@ -788,6 +796,8 @@ public:
|
|||
StringName text;
|
||||
double constant;
|
||||
uint16_t line;
|
||||
uint16_t length;
|
||||
int32_t pos;
|
||||
bool is_integer_constant() const {
|
||||
return type == TK_INT_CONSTANT || type == TK_UINT_CONSTANT;
|
||||
}
|
||||
|
@ -803,6 +813,7 @@ public:
|
|||
static DataInterpolation get_token_interpolation(TokenType p_type);
|
||||
static bool is_token_precision(TokenType p_type);
|
||||
static bool is_token_arg_qual(TokenType p_type);
|
||||
static bool is_token_uniform_qual(TokenType p_type);
|
||||
static DataPrecision get_token_precision(TokenType p_type);
|
||||
static String get_precision_name(DataPrecision p_type);
|
||||
static String get_interpolation_name(DataInterpolation p_interpolation);
|
||||
|
@ -814,6 +825,7 @@ public:
|
|||
static bool is_token_operator(TokenType p_type);
|
||||
static bool is_token_operator_assign(TokenType p_type);
|
||||
static bool is_token_hint(TokenType p_type);
|
||||
static bool is_token_keyword(TokenType p_type);
|
||||
|
||||
static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value = nullptr);
|
||||
static DataType get_scalar_type(DataType p_type);
|
||||
|
@ -1006,6 +1018,7 @@ private:
|
|||
String code;
|
||||
int char_idx = 0;
|
||||
int tk_line = 0;
|
||||
int tk_start_pos = 0;
|
||||
|
||||
StringName shader_type_identifier;
|
||||
StringName current_function;
|
||||
|
@ -1140,6 +1153,8 @@ private:
|
|||
StringName completion_struct;
|
||||
int completion_argument = 0;
|
||||
|
||||
bool debug_parse = false;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint32_t keyword_completion_context;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
@ -1222,7 +1237,10 @@ public:
|
|||
|
||||
ShaderNode *get_shader();
|
||||
|
||||
String token_debug(const String &p_code);
|
||||
String token_debug(const String &p_code, bool p_debug_parse = false);
|
||||
void token_debug_stream(const String &p_code, List<Token> &r_output, bool p_debug_parse);
|
||||
|
||||
ShaderLanguage::Operator get_op(const TokenType &p_token) const;
|
||||
|
||||
ShaderLanguage();
|
||||
~ShaderLanguage();
|
||||
|
|
|
@ -0,0 +1,799 @@
|
|||
/**************************************************************************/
|
||||
/* test_shader_converter.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 TEST_SHADER_CONVERTER_H
|
||||
#define TEST_SHADER_CONVERTER_H
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#include "servers/rendering/shader_converter.h"
|
||||
#include "servers/rendering/shader_language.h"
|
||||
#include "servers/rendering/shader_types.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace TestShaderConverter {
|
||||
|
||||
void erase_all_empty(Vector<String> &p_vec) {
|
||||
int idx = p_vec.find(" ");
|
||||
while (idx >= 0) {
|
||||
p_vec.remove_at(idx);
|
||||
idx = p_vec.find(" ");
|
||||
}
|
||||
}
|
||||
|
||||
bool is_variable_char(unsigned char c) {
|
||||
return std::isalnum(c) || c == '_';
|
||||
}
|
||||
|
||||
bool is_operator_char(unsigned char c) {
|
||||
return (c == '*') || (c == '+') || (c == '-') || (c == '/') || ((c >= '<') && (c <= '>'));
|
||||
}
|
||||
|
||||
// Remove unnecessary spaces from a line.
|
||||
String remove_spaces(String &p_str) {
|
||||
String res;
|
||||
// Result is guaranteed to not be longer than the input.
|
||||
res.resize(p_str.size());
|
||||
int wp = 0;
|
||||
char32_t last = 0;
|
||||
bool has_removed = false;
|
||||
|
||||
for (int n = 0; n < p_str.size(); n++) {
|
||||
// These test cases only use ASCII.
|
||||
unsigned char c = static_cast<unsigned char>(p_str[n]);
|
||||
if (std::isblank(c)) {
|
||||
has_removed = true;
|
||||
} else {
|
||||
if (has_removed) {
|
||||
// Insert a space to avoid joining things that could potentially form a new token.
|
||||
// E.g. "float x" or "- -".
|
||||
if ((is_variable_char(c) && is_variable_char(last)) ||
|
||||
(is_operator_char(c) && is_operator_char(last))) {
|
||||
res[wp++] = ' ';
|
||||
}
|
||||
has_removed = false;
|
||||
}
|
||||
res[wp++] = c;
|
||||
last = c;
|
||||
}
|
||||
}
|
||||
res.resize(wp);
|
||||
return res;
|
||||
}
|
||||
|
||||
// The pre-processor changes indentation and inserts spaces when inserting macros.
|
||||
// Re-format the code, without changing its meaning, to make it easier to compare.
|
||||
String compact_spaces(String &p_str) {
|
||||
Vector<String> lines = p_str.split("\n", false);
|
||||
erase_all_empty(lines);
|
||||
for (String &line : lines) {
|
||||
line = remove_spaces(line);
|
||||
}
|
||||
return String("\n").join(lines);
|
||||
}
|
||||
|
||||
void get_keyword_set(HashSet<String> &p_keywords) {
|
||||
List<String> keywords;
|
||||
ShaderLanguage::get_keyword_list(&keywords);
|
||||
for (const String &keyword : keywords) {
|
||||
p_keywords.insert(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_SHADER_EQ(a, b) CHECK_EQ(compact_spaces(a), compact_spaces(b))
|
||||
#define CHECK_SHADER_NE(a, b) CHECK_NE(compact_spaces(a), compact_spaces(b))
|
||||
|
||||
void get_compile_info(ShaderLanguage::ShaderCompileInfo &info, RenderingServer::ShaderMode p_mode) {
|
||||
info.functions = ShaderTypes::get_singleton()->get_functions(p_mode);
|
||||
info.render_modes = ShaderTypes::get_singleton()->get_modes(p_mode);
|
||||
info.shader_types = ShaderTypes::get_singleton()->get_types();
|
||||
// Only used by editor for completion, so it's not important for these tests.
|
||||
info.global_shader_uniform_type_func = [](const StringName &p_name) -> ShaderLanguage::DataType {
|
||||
return ShaderLanguage::TYPE_SAMPLER2D;
|
||||
};
|
||||
}
|
||||
|
||||
RenderingServer::ShaderMode get_shader_mode(const String &p_mode_string) {
|
||||
if (p_mode_string == "canvas_item") {
|
||||
return RS::SHADER_CANVAS_ITEM;
|
||||
} else if (p_mode_string == "particles") {
|
||||
return RS::SHADER_PARTICLES;
|
||||
} else if (p_mode_string == "spatial") {
|
||||
return RS::SHADER_SPATIAL;
|
||||
} else if (p_mode_string == "sky") {
|
||||
return RS::SHADER_SKY;
|
||||
} else if (p_mode_string == "fog") {
|
||||
return RS::SHADER_FOG;
|
||||
} else {
|
||||
return RS::SHADER_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
String get_shader_mode_name(const RenderingServer::ShaderMode &p_mode_string) {
|
||||
switch (p_mode_string) {
|
||||
case RS::SHADER_CANVAS_ITEM:
|
||||
return "canvas_item";
|
||||
case RS::SHADER_PARTICLES:
|
||||
return "particles";
|
||||
case RS::SHADER_SPATIAL:
|
||||
return "spatial";
|
||||
case RS::SHADER_SKY:
|
||||
return "sky";
|
||||
case RS::SHADER_FOG:
|
||||
return "fog";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
using SL = ShaderLanguage;
|
||||
using SDC = ShaderDeprecatedConverter;
|
||||
|
||||
#define TEST_CONVERSION(m_old_code, m_expected, m_is_deprecated) \
|
||||
{ \
|
||||
ShaderDeprecatedConverter _i_converter; \
|
||||
CHECK_EQ(_i_converter.is_code_deprecated(m_old_code), m_is_deprecated); \
|
||||
CHECK(_i_converter.convert_code(m_old_code)); \
|
||||
String _i_new_code = _i_converter.emit_code(); \
|
||||
CHECK_EQ(_i_new_code, m_expected); \
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Simple conversion with arrays") {
|
||||
String code = "shader_type particles; void vertex() { float xy[2] = {1.0,1.1}; }";
|
||||
String expected = "shader_type particles; void process() { float xy[2] = {1.0,1.1}; }";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Test warning comments") {
|
||||
// Test that warning comments are added when fail_on_unported is false and warning_comments is true
|
||||
String code = "shader_type spatial;\nrender_mode specular_phong;";
|
||||
String expected = "shader_type spatial;\n/* !convert WARNING: Deprecated render mode 'specular_phong' is not supported by this version of Godot. */\nrender_mode specular_phong;";
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK(converter.is_code_deprecated(code));
|
||||
converter.set_warning_comments(true);
|
||||
converter.set_fail_on_unported(false);
|
||||
CHECK(converter.convert_code(code));
|
||||
String new_code = converter.emit_code();
|
||||
CHECK_EQ(new_code, expected);
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Simple conversion with arrays") {
|
||||
String code = "shader_type particles; struct foo{float bar;} void vertex() { float xy[2] = {1.0,1.1}; }";
|
||||
String expected = "shader_type particles; struct foo{float bar;} void process() { float xy[2] = {1.0,1.1}; }";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] new-style array declaration") {
|
||||
String code = "shader_type spatial; void vertex() { float[2] xy = {1.0,1.1}; }";
|
||||
// code should be the same
|
||||
TEST_CONVERSION(code, code, false);
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Simple conversion") {
|
||||
String code = "shader_type particles; void vertex() { float x = 1.0; }";
|
||||
String expected = "shader_type particles; void process() { float x = 1.0; }";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Replace non-conformant float literals") {
|
||||
String code = "shader_type spatial; const float x = 1f;";
|
||||
String expected = "shader_type spatial; const float x = 1.0f;";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] particles::vertex() -> particles::process()") {
|
||||
SUBCASE("basic") {
|
||||
String code = "shader_type particles; void vertex() { float x = 1.0; }";
|
||||
String expected = "shader_type particles; void process() { float x = 1.0; }";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("with another function named `process` without correct signature") {
|
||||
String code = "shader_type particles; void vertex() {} float process() { return 1.0; }";
|
||||
String expected = "shader_type particles; void process() {} float process_() { return 1.0; }";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("with another function named `process` with correct signature") {
|
||||
String code = "shader_type particles; void vertex() {} void process() {}";
|
||||
// Should be unchanged.
|
||||
TEST_CONVERSION(code, code, false);
|
||||
}
|
||||
|
||||
SUBCASE("with another function named `process` that is called") {
|
||||
String code = "shader_type particles; float process() { return 1.0; } void vertex() { float foo = process(); }";
|
||||
String expected = "shader_type particles; float process_() { return 1.0; } void process() { float foo = process_(); }";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("with another function named `process` which calls `vertex`") {
|
||||
String code = "shader_type particles; float process() {foo(); return 1.0;} void vertex() {} void foo() { vertex(); }";
|
||||
String expected = "shader_type particles; float process_() {foo(); return 1.0;} void process() {} void foo() { process(); }";
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("No function named `vertex`") {
|
||||
String code = "shader_type particles; void process() {}";
|
||||
// Should be unchanged.
|
||||
TEST_CONVERSION(code, code, false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] CLEARCOAT_GLOSS -> CLEARCOAT_ROUGHNESS") {
|
||||
SUBCASE("Left-hand simple assignment") {
|
||||
String code("shader_type spatial; void fragment() {\n"
|
||||
"CLEARCOAT_GLOSS = 1.0;\n"
|
||||
"}\n");
|
||||
String expected("shader_type spatial; void fragment() {\n"
|
||||
"CLEARCOAT_ROUGHNESS = (1.0 - (1.0));\n"
|
||||
"}\n");
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("Left-hand *= assignment") {
|
||||
String code("shader_type spatial; void fragment() {\n"
|
||||
"CLEARCOAT_GLOSS *= 0.5;\n"
|
||||
"}\n");
|
||||
String expected("shader_type spatial; void fragment() {\n"
|
||||
"CLEARCOAT_ROUGHNESS = (1.0 - ((1.0 - CLEARCOAT_ROUGHNESS) * 0.5));\n"
|
||||
"}\n");
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("Right-hand usage") {
|
||||
String code("shader_type spatial; void fragment() {\n"
|
||||
"float foo = CLEARCOAT_GLOSS;\n"
|
||||
"}\n");
|
||||
String expected("shader_type spatial; void fragment() {\n"
|
||||
"float foo = (1.0 - CLEARCOAT_ROUGHNESS);\n"
|
||||
"}\n");
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("both usages") {
|
||||
String code("shader_type spatial; void fragment() {\n"
|
||||
"float foo = (CLEARCOAT_GLOSS *= 0.5);\n"
|
||||
"}\n");
|
||||
String expected("shader_type spatial; void fragment() {\n"
|
||||
"float foo = ((1.0 - (CLEARCOAT_ROUGHNESS = (1.0 - ((1.0 - CLEARCOAT_ROUGHNESS) * 0.5)))));\n"
|
||||
"}\n");
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
}
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Wrap INDEX in int()") {
|
||||
SUBCASE("basic") {
|
||||
String code("shader_type particles; void vertex() {\n"
|
||||
"float foo = INDEX/2;\n"
|
||||
"}\n");
|
||||
String expected("shader_type particles; void process() {\n"
|
||||
"float foo = int(INDEX)/2;\n"
|
||||
"}\n");
|
||||
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
SUBCASE("Without clobbering existing casts") {
|
||||
String code("shader_type particles; void vertex() {\n"
|
||||
"float foo = int(INDEX/2) * int(INDEX) * 2 * float(INDEX);\n"
|
||||
"}\n");
|
||||
String expected("shader_type particles; void process() {\n"
|
||||
"float foo = int(int(INDEX)/2) * int(INDEX) * 2 * float(INDEX);\n"
|
||||
"}\n");
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] All hint renames") {
|
||||
String code_template = "shader_type spatial; uniform sampler2D foo : %s;";
|
||||
// get all the hint renames
|
||||
List<String> hints;
|
||||
ShaderDeprecatedConverter::_get_hint_renames_list(&hints);
|
||||
|
||||
SUBCASE("No renamed hints present in current keyword list") {
|
||||
HashSet<String> keywords_set;
|
||||
get_keyword_set(keywords_set);
|
||||
for (const String &hint : hints) {
|
||||
CHECK_FALSE(keywords_set.has(hint));
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("All renamed hints are replaced") {
|
||||
for (const String &hint : hints) {
|
||||
ShaderDeprecatedConverter::TokenType type = ShaderDeprecatedConverter::get_hint_replacement(hint);
|
||||
String rename = ShaderDeprecatedConverter::get_tokentype_text(type);
|
||||
String code = vformat(code_template, hint);
|
||||
String expected = vformat(code_template, rename);
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Built-in renames") {
|
||||
// Get all the built-in renames.
|
||||
List<String> builtins;
|
||||
ShaderDeprecatedConverter::_get_builtin_renames_list(&builtins);
|
||||
// remove built-ins that have special handling, we test those above
|
||||
for (List<String>::Element *E = builtins.front(); E; E = E->next()) {
|
||||
if (ShaderDeprecatedConverter::_rename_has_special_handling(E->get())) {
|
||||
List<String>::Element *prev = E->prev();
|
||||
builtins.erase(E);
|
||||
E = prev;
|
||||
}
|
||||
}
|
||||
Vector<RS::ShaderMode> modes = { RS::SHADER_SPATIAL, RS::SHADER_CANVAS_ITEM, RS::SHADER_PARTICLES };
|
||||
HashMap<RS::ShaderMode, HashMap<String, Vector<String>>> rename_func_map;
|
||||
for (RS::ShaderMode mode : modes) {
|
||||
rename_func_map[mode] = HashMap<String, Vector<String>>();
|
||||
for (const String &builtin : builtins) {
|
||||
rename_func_map[mode][builtin] = ShaderDeprecatedConverter::_get_funcs_builtin_rename(mode, builtin);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("All renamed built-ins are not currently built-in") {
|
||||
for (RS::ShaderMode mode : modes) {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, mode);
|
||||
for (const String &builtin : builtins) {
|
||||
// Now get the funcs applicable for this mode and built-in.
|
||||
for (const String &func : rename_func_map[mode][builtin]) {
|
||||
// The built-in should not be present in the built-ins list.
|
||||
auto &finfo = info.functions[func];
|
||||
if (finfo.built_ins.has(builtin)) {
|
||||
WARN_PRINT(vformat("Renamed 3.x Built-in %s is present in function %s", builtin, func));
|
||||
}
|
||||
CHECK_FALSE(finfo.built_ins.has(builtin));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("All renamed built-ins are replaced") {
|
||||
String code_template = "shader_type %s; void %s() { %s; }";
|
||||
for (RS::ShaderMode mode : modes) {
|
||||
for (const String &builtin : builtins) {
|
||||
// Now get the funcs applicable for this mode and built-in.
|
||||
String rename = ShaderDeprecatedConverter::get_builtin_rename(builtin);
|
||||
for (const String &func : rename_func_map[mode][builtin]) {
|
||||
String code = vformat(code_template, get_shader_mode_name(mode), func, builtin);
|
||||
String expected = vformat(code_template, get_shader_mode_name(mode), func, rename);
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE("No renaming built-ins in non-candidate functions") {
|
||||
String code_template = "shader_type %s; void %s() { float %s = 1.0; %s += 1.0; }";
|
||||
for (RS::ShaderMode mode : modes) {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, mode);
|
||||
for (const String &builtin : builtins) {
|
||||
Vector<String> non_funcs;
|
||||
for (KeyValue<StringName, ShaderLanguage::FunctionInfo> &func : info.functions) {
|
||||
if (func.key == "global") {
|
||||
continue;
|
||||
}
|
||||
if (!rename_func_map[mode][builtin].has(func.key)) {
|
||||
non_funcs.push_back(func.key);
|
||||
}
|
||||
}
|
||||
String rename = ShaderDeprecatedConverter::get_builtin_rename(builtin);
|
||||
for (const String &func : non_funcs) {
|
||||
String code = vformat(code_template, get_shader_mode_name(mode), func, builtin, builtin);
|
||||
// The code should not change.
|
||||
TEST_CONVERSION(code, code, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE("No renaming built-ins in candidate functions with built-in declared") {
|
||||
String code_template = "shader_type %s; void %s() { float %s = 1.0; %s += 1.0; }";
|
||||
for (RS::ShaderMode mode : modes) {
|
||||
for (const String &builtin : builtins) {
|
||||
for (const String &func : rename_func_map[mode][builtin]) {
|
||||
String code = vformat(code_template, get_shader_mode_name(mode), func, builtin, builtin);
|
||||
// The code should not change.
|
||||
TEST_CONVERSION(code, code, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this when the MODULATE built-in PR lands.
|
||||
// If this fails, remove the MODULATE entry from ShaderDeprecatedConverter::removed_builtins, then remove this test and the following test.
|
||||
TEST_CASE("[ShaderDeprecatedConverter] MODULATE is not a built-in") {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, RS::ShaderMode::SHADER_CANVAS_ITEM);
|
||||
SUBCASE("MODULATE is not a built-in") {
|
||||
for (const String &func : Vector<String>{ "vertex", "fragment", "light" }) {
|
||||
auto &finfo = info.functions[func];
|
||||
CHECK_FALSE(finfo.built_ins.has("MODULATE"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't remove this one if the above doesn't fail too.
|
||||
TEST_CASE("[ShaderDeprecatedConverter] MODULATE handling") {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, RS::ShaderMode::SHADER_CANVAS_ITEM);
|
||||
SUBCASE("Fails to compile") {
|
||||
for (const String &func : Vector<String>{ "vertex", "fragment", "light" }) {
|
||||
String code = vformat("shader_type canvas_item; void %s() { MODULATE; }", func);
|
||||
ShaderLanguage sl;
|
||||
CHECK_NE(sl.compile(code, info), Error::OK);
|
||||
}
|
||||
}
|
||||
SUBCASE("Fails to convert on fail_on_unported=true") {
|
||||
for (const String &func : Vector<String>{ "vertex", "fragment", "light" }) {
|
||||
String code = vformat("shader_type canvas_item; void %s() { MODULATE; }", func);
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK(converter.is_code_deprecated(code));
|
||||
converter.set_fail_on_unported(true);
|
||||
CHECK_FALSE(converter.convert_code(code));
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Conversion succeeds on fail_on_unported=false") {
|
||||
for (const String &func : Vector<String>{ "vertex", "fragment", "light" }) {
|
||||
String code = vformat("shader_type canvas_item; void %s() { MODULATE; }", func);
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK(converter.is_code_deprecated(code));
|
||||
converter.set_fail_on_unported(false);
|
||||
CHECK(converter.convert_code(code));
|
||||
String new_code = converter.emit_code();
|
||||
CHECK(new_code.find("/*") != -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Uniform declarations for removed builtins") {
|
||||
// Test uniform declaration inserts for removed builtins for all shader types.
|
||||
String code_template = "shader_type %s;%s void %s() { %s; }";
|
||||
String uniform_template = "\nuniform %s %s : %s;\n";
|
||||
// Get all the removed built-ins.
|
||||
List<String> builtins;
|
||||
ShaderDeprecatedConverter::_get_builtin_removals_list(&builtins);
|
||||
Vector<RS::ShaderMode> modes = { RS::SHADER_SPATIAL, RS::SHADER_CANVAS_ITEM, RS::SHADER_PARTICLES };
|
||||
HashMap<RS::ShaderMode, ShaderLanguage::ShaderCompileInfo> compiler_infos;
|
||||
|
||||
SUBCASE("Removed built-ins are not currently built-in") {
|
||||
for (RS::ShaderMode mode : modes) {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, mode);
|
||||
for (const String &builtin : builtins) {
|
||||
Vector<String> funcs = ShaderDeprecatedConverter::_get_funcs_builtin_removal(mode, builtin);
|
||||
for (const String &func : funcs) {
|
||||
const ShaderLanguage::FunctionInfo &finfo = info.functions[func];
|
||||
if (finfo.built_ins.has(builtin)) {
|
||||
WARN_PRINT(vformat("Removed 3.x Built-in %s is present in function %s", builtin, func));
|
||||
}
|
||||
CHECK_FALSE(finfo.built_ins.has(builtin));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("All removed built-ins have uniform declarations") {
|
||||
for (RS::ShaderMode mode : modes) {
|
||||
for (const String &builtin : builtins) {
|
||||
// now get the funcs applicable for this mode and builtins
|
||||
ShaderLanguage::TokenType type = ShaderDeprecatedConverter::get_removed_builtin_uniform_type(builtin);
|
||||
if (type == ShaderDeprecatedConverter::TokenType::TK_ERROR) {
|
||||
continue;
|
||||
}
|
||||
Vector<ShaderLanguage::TokenType> hints = ShaderDeprecatedConverter::get_removed_builtin_hints(builtin);
|
||||
Vector<String> funcs = ShaderDeprecatedConverter::_get_funcs_builtin_removal(mode, builtin);
|
||||
String hint_string = "";
|
||||
for (int i = 0; i < hints.size(); i++) {
|
||||
hint_string += ShaderDeprecatedConverter::get_tokentype_text(hints[i]);
|
||||
if (i < hints.size() - 1) {
|
||||
hint_string += ", ";
|
||||
}
|
||||
}
|
||||
String uniform_decl = vformat(uniform_template, ShaderDeprecatedConverter::get_tokentype_text(type), builtin, hint_string);
|
||||
for (const String &func : funcs) {
|
||||
String code = vformat(code_template, get_shader_mode_name(mode), "", func, builtin);
|
||||
if (type == ShaderDeprecatedConverter::TokenType::TK_ERROR) { // Unported builtins with no uniform declaration
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK(converter.is_code_deprecated(code));
|
||||
CHECK_FALSE(converter.convert_code(code));
|
||||
converter.set_fail_on_unported(false);
|
||||
CHECK(converter.convert_code(code));
|
||||
continue;
|
||||
}
|
||||
String expected = vformat(code_template, get_shader_mode_name(mode), uniform_decl, func, builtin);
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reserved keywords (i.e. non-built-in function keywords that have a discrete token type)
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Replacement of reserved keywords used as identifiers") {
|
||||
Vector<String> keywords;
|
||||
for (int i = 0; i < ShaderLanguage::TK_MAX; i++) {
|
||||
if (ShaderDeprecatedConverter::tokentype_is_new_reserved_keyword(static_cast<ShaderLanguage::TokenType>(i))) {
|
||||
keywords.push_back(ShaderDeprecatedConverter::get_tokentype_text(static_cast<ShaderLanguage::TokenType>(i)));
|
||||
}
|
||||
}
|
||||
Vector<String> hint_keywords;
|
||||
for (int i = 0; i < SL::TK_MAX; i++) {
|
||||
if (SDC::tokentype_is_new_hint(static_cast<SL::TokenType>(i))) {
|
||||
hint_keywords.push_back(SDC::get_tokentype_text(static_cast<SL::TokenType>(i)));
|
||||
}
|
||||
}
|
||||
Vector<String> uniform_quals;
|
||||
for (int i = 0; i < SL::TK_MAX; i++) {
|
||||
if (SL::is_token_uniform_qual(static_cast<SL::TokenType>(i))) {
|
||||
uniform_quals.push_back(SDC::get_tokentype_text(static_cast<SL::TokenType>(i)));
|
||||
}
|
||||
}
|
||||
Vector<String> shader_types_to_test = { "spatial", "canvas_item", "particles" };
|
||||
|
||||
static const char *decl_test_template[]{
|
||||
"shader_type %s;\nvoid %s() {}\n",
|
||||
"shader_type %s;\nvoid test_func() {float %s;}\n",
|
||||
"shader_type %s;\nuniform sampler2D %s;\n",
|
||||
"shader_type %s;\nconst float %s = 1.0;\n",
|
||||
"shader_type %s;\nvarying float %s;\n",
|
||||
nullptr
|
||||
};
|
||||
// NOTE: if this fails, the current behavior of the converter to replace these has to be changed.
|
||||
SUBCASE("Code with reserved keywords used as identifiers fail to compile") {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, RS::SHADER_SPATIAL);
|
||||
for (const String &shader_type : shader_types_to_test) {
|
||||
for (const String &keyword : keywords) {
|
||||
for (int i = 0; decl_test_template[i] != nullptr; i++) {
|
||||
String code = vformat(decl_test_template[i], shader_type, keyword);
|
||||
ShaderLanguage sl;
|
||||
CHECK_NE(sl.compile(code, info), Error::OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE("Code with reserved keywords used as identifiers is converted successfully") {
|
||||
for (const String &shader_type : shader_types_to_test) {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, get_shader_mode(shader_type));
|
||||
for (const String &keyword : keywords) {
|
||||
for (int i = 0; decl_test_template[i] != nullptr; i++) {
|
||||
if (shader_type == "particles" && String(decl_test_template[i]).contains("varying")) {
|
||||
continue;
|
||||
}
|
||||
String code = vformat(decl_test_template[i], shader_type, keyword);
|
||||
String expected = vformat(decl_test_template[i], shader_type, keyword + "_");
|
||||
TEST_CONVERSION(code, expected, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static const char *new_hint_test = "shader_type spatial;\nuniform sampler2D foo : %s; const float %s = 1.0;\n";
|
||||
SUBCASE("New hints used as hints are not replaced") {
|
||||
for (const String &hint : hint_keywords) {
|
||||
String code = vformat(new_hint_test, hint, "bar");
|
||||
// Code should not change.
|
||||
TEST_CONVERSION(code, code, false);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Mixed new hints used as hints and new hints used as identifiers") {
|
||||
for (const String &hint : hint_keywords) {
|
||||
String code = vformat(new_hint_test, hint, hint);
|
||||
// Should not change.
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK_FALSE(converter.is_code_deprecated(code)); // Should be detected as not deprecated.
|
||||
converter.set_warning_comments(false);
|
||||
CHECK(converter.convert_code(code));
|
||||
String new_code = converter.emit_code();
|
||||
// Code should not change
|
||||
CHECK_EQ(new_code, code);
|
||||
// Check for warning comment
|
||||
converter.set_warning_comments(true);
|
||||
new_code = converter.emit_code();
|
||||
CHECK(new_code.contains("/* !convert WARNING:"));
|
||||
}
|
||||
}
|
||||
static const char *non_id_keyword_test = "shader_type spatial;\n%s uniform sampler2D foo; const float %s = 1.0;\n";
|
||||
SUBCASE("New keywords not used as identifiers are not replaced") {
|
||||
for (const String &qual : uniform_quals) {
|
||||
// e.g. "shader_type spatial;\nglobal uniform sampler2D foo; const float bar = 1.0;\n"
|
||||
String code = vformat(non_id_keyword_test, qual, "bar");
|
||||
// Code should not change.
|
||||
TEST_CONVERSION(code, code, false);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Mixed idiomatic new reserved words and new reserved words used as identifiers") {
|
||||
for (const String &qual : uniform_quals) {
|
||||
// e.g. "shader_type spatial;\nglobal uniform sampler2D foo; const float global = 1.0;\n"
|
||||
String code = vformat(non_id_keyword_test, qual, qual);
|
||||
// Should not change.
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK_FALSE(converter.is_code_deprecated(code)); // Should be detected as not deprecated.
|
||||
converter.set_warning_comments(false);
|
||||
CHECK(converter.convert_code(code));
|
||||
String new_code = converter.emit_code();
|
||||
// Code should not change
|
||||
CHECK_EQ(new_code, code);
|
||||
// Check for warning comment
|
||||
converter.set_warning_comments(true);
|
||||
new_code = converter.emit_code();
|
||||
CHECK(new_code.contains("/* !convert WARNING:"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderDeprecatedConverter] Convert default 3.x nodetree shader") {
|
||||
static const char *default_3x_nodtree_shader =
|
||||
R"(shader_type spatial;
|
||||
render_mode blend_mix, depth_draw_always, cull_back, diffuse_burley, specular_schlick_ggx;
|
||||
|
||||
uniform sampler2D texture_0: hint_albedo;
|
||||
|
||||
|
||||
void node_bsdf_principled(vec4 color, float subsurface, vec4 subsurface_color,
|
||||
float metallic, float specular, float roughness, float clearcoat,
|
||||
float clearcoat_roughness, float anisotropy, float transmission,
|
||||
float IOR, out vec3 albedo, out float sss_strength_out,
|
||||
out float metallic_out, out float specular_out,
|
||||
out float roughness_out, out float clearcoat_out,
|
||||
out float clearcoat_gloss_out, out float anisotropy_out,
|
||||
out float transmission_out, out float ior) {
|
||||
metallic = clamp(metallic, 0.0, 1.0);
|
||||
transmission = clamp(transmission, 0.0, 1.0);
|
||||
|
||||
subsurface = subsurface * (1.0 - metallic);
|
||||
|
||||
albedo = mix(color.rgb, subsurface_color.rgb, subsurface);
|
||||
sss_strength_out = subsurface;
|
||||
metallic_out = metallic;
|
||||
specular_out = pow((IOR - 1.0)/(IOR + 1.0), 2)/0.08;
|
||||
roughness_out = roughness;
|
||||
clearcoat_out = clearcoat * (1.0 - transmission);
|
||||
clearcoat_gloss_out = 1.0 - clearcoat_roughness;
|
||||
anisotropy_out = clamp(anisotropy, 0.0, 1.0);
|
||||
transmission_out = (1.0 - transmission) * (1.0 - metallic);
|
||||
ior = IOR;
|
||||
}
|
||||
|
||||
|
||||
void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha) {
|
||||
color = texture(ima, co.xy);
|
||||
alpha = color.a;
|
||||
}
|
||||
|
||||
void vertex () {
|
||||
}
|
||||
|
||||
void fragment () {
|
||||
|
||||
// node: 'Image Texture'
|
||||
// type: 'ShaderNodeTexImage'
|
||||
// input sockets handling
|
||||
vec3 node0_in0_vector = vec3(0.0, 0.0, 0.0);
|
||||
// output sockets definitions
|
||||
vec4 node0_out0_color;
|
||||
float node0_out1_alpha;
|
||||
|
||||
node0_in0_vector = vec3(UV, 0.0);
|
||||
node_tex_image(node0_in0_vector, texture_0, node0_out0_color, node0_out1_alpha);
|
||||
|
||||
|
||||
// node: 'Principled BSDF'
|
||||
// type: 'ShaderNodeBsdfPrincipled'
|
||||
// input sockets handling
|
||||
vec4 node1_in0_basecolor = node0_out0_color;
|
||||
float node1_in1_subsurface = float(0.0);
|
||||
vec3 node1_in2_subsurfaceradius = vec3(1.0, 0.20000000298023224,
|
||||
0.10000000149011612);
|
||||
vec4 node1_in3_subsurfacecolor = vec4(0.800000011920929, 0.800000011920929,
|
||||
0.800000011920929, 1.0);
|
||||
float node1_in4_metallic = float(0.0);
|
||||
float node1_in5_specular = float(0.5);
|
||||
float node1_in6_speculartint = float(0.0);
|
||||
float node1_in7_roughness = float(1.0);
|
||||
float node1_in8_anisotropic = float(0.0);
|
||||
float node1_in9_anisotropicrotation = float(0.0);
|
||||
float node1_in10_sheen = float(0.0);
|
||||
float node1_in11_sheentint = float(0.5);
|
||||
float node1_in12_clearcoat = float(0.0);
|
||||
float node1_in13_clearcoatroughness = float(0.029999999329447746);
|
||||
float node1_in14_ior = float(1.4500000476837158);
|
||||
float node1_in15_transmission = float(0.0);
|
||||
float node1_in16_transmissionroughness = float(0.0);
|
||||
vec4 node1_in17_emission = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
float node1_in18_emissionstrength = float(1.0);
|
||||
float node1_in19_alpha = float(1.0);
|
||||
vec3 node1_in20_normal = NORMAL;
|
||||
vec3 node1_in21_clearcoatnormal = vec3(0.0, 0.0, 0.0);
|
||||
vec3 node1_in22_tangent = TANGENT;
|
||||
// output sockets definitions
|
||||
vec3 node1_bsdf_out0_albedo;
|
||||
float node1_bsdf_out1_sss_strength;
|
||||
float node1_bsdf_out3_specular;
|
||||
float node1_bsdf_out2_metallic;
|
||||
float node1_bsdf_out4_roughness;
|
||||
float node1_bsdf_out5_clearcoat;
|
||||
float node1_bsdf_out6_clearcoat_gloss;
|
||||
float node1_bsdf_out7_anisotropy;
|
||||
float node1_bsdf_out8_transmission;
|
||||
float node1_bsdf_out9_ior;
|
||||
|
||||
node_bsdf_principled(node1_in0_basecolor, node1_in1_subsurface,
|
||||
node1_in3_subsurfacecolor, node1_in4_metallic, node1_in5_specular,
|
||||
node1_in7_roughness, node1_in12_clearcoat, node1_in13_clearcoatroughness,
|
||||
node1_in8_anisotropic, node1_in15_transmission, node1_in14_ior,
|
||||
node1_bsdf_out0_albedo, node1_bsdf_out1_sss_strength, node1_bsdf_out2_metallic,
|
||||
node1_bsdf_out3_specular, node1_bsdf_out4_roughness, node1_bsdf_out5_clearcoat,
|
||||
node1_bsdf_out6_clearcoat_gloss, node1_bsdf_out7_anisotropy,
|
||||
node1_bsdf_out8_transmission, node1_bsdf_out9_ior);
|
||||
|
||||
|
||||
ALBEDO = node1_bsdf_out0_albedo;
|
||||
SSS_STRENGTH = node1_bsdf_out1_sss_strength;
|
||||
SPECULAR = node1_bsdf_out3_specular;
|
||||
METALLIC = node1_bsdf_out2_metallic;
|
||||
ROUGHNESS = node1_bsdf_out4_roughness;
|
||||
CLEARCOAT = node1_bsdf_out5_clearcoat;
|
||||
CLEARCOAT_GLOSS = node1_bsdf_out6_clearcoat_gloss;
|
||||
NORMAL = node1_in20_normal;
|
||||
// uncomment it when you need it
|
||||
// TRANSMISSION = vec3(1.0, 1.0, 1.0) * node1_bsdf_out8_transmission;
|
||||
// uncomment it when you are modifing TANGENT
|
||||
// TANGENT = normalize(cross(cross(node1_in22_tangent, NORMAL), NORMAL));
|
||||
// BINORMAL = cross(TANGENT, NORMAL);
|
||||
// uncomment it when you have tangent(UV) set
|
||||
// ANISOTROPY = node1_bsdf_out7_anisotropy;
|
||||
}
|
||||
)";
|
||||
|
||||
ShaderLanguage sl;
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, RS::SHADER_SPATIAL);
|
||||
SUBCASE("Default 3.x nodetree shader does not compile") {
|
||||
CHECK_NE(sl.compile(default_3x_nodtree_shader, info), Error::OK);
|
||||
}
|
||||
sl.clear();
|
||||
SUBCASE("Convert default 3.x nodetree shader") {
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK(converter.convert_code(default_3x_nodtree_shader));
|
||||
String new_code = converter.emit_code();
|
||||
CHECK(new_code.find("/*") == -1);
|
||||
CHECK_FALSE(converter.get_error_line());
|
||||
}
|
||||
|
||||
SUBCASE("Converted default 3.x nodetree shader compiles") {
|
||||
ShaderDeprecatedConverter converter;
|
||||
CHECK(converter.convert_code(default_3x_nodtree_shader));
|
||||
String new_code = converter.emit_code();
|
||||
CHECK_EQ(sl.compile(new_code, info), Error::OK);
|
||||
}
|
||||
sl.clear();
|
||||
}
|
||||
|
||||
} // namespace TestShaderConverter
|
||||
#undef TEST_CONVERSION
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
#endif // TEST_SHADER_CONVERTER_H
|
|
@ -0,0 +1,144 @@
|
|||
/**************************************************************************/
|
||||
/* test_shader_language.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 TEST_SHADER_LANGUAGE_H
|
||||
#define TEST_SHADER_LANGUAGE_H
|
||||
|
||||
#include "servers/rendering/shader_language.h"
|
||||
#include "servers/rendering/shader_types.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace TestShaderLanguage {
|
||||
|
||||
void get_compile_info(ShaderLanguage::ShaderCompileInfo &info, RenderingServer::ShaderMode p_mode) {
|
||||
info.functions = ShaderTypes::get_singleton()->get_functions(p_mode);
|
||||
info.render_modes = ShaderTypes::get_singleton()->get_modes(p_mode);
|
||||
info.shader_types = ShaderTypes::get_singleton()->get_types();
|
||||
// Only used by editor for completion, so it's not important for these tests.
|
||||
info.global_shader_uniform_type_func = [](const StringName &p_name) -> ShaderLanguage::DataType {
|
||||
return ShaderLanguage::TYPE_SAMPLER2D;
|
||||
};
|
||||
}
|
||||
|
||||
RenderingServer::ShaderMode get_shader_mode(const String &p_mode_string) {
|
||||
if (p_mode_string == "canvas_item") {
|
||||
return RS::SHADER_CANVAS_ITEM;
|
||||
} else if (p_mode_string == "particles") {
|
||||
return RS::SHADER_PARTICLES;
|
||||
} else if (p_mode_string == "spatial") {
|
||||
return RS::SHADER_SPATIAL;
|
||||
} else if (p_mode_string == "sky") {
|
||||
return RS::SHADER_SKY;
|
||||
} else if (p_mode_string == "fog") {
|
||||
return RS::SHADER_FOG;
|
||||
} else {
|
||||
return RS::SHADER_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[ShaderLanguage] Minimal Script") {
|
||||
ShaderLanguage sl;
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, RS::SHADER_SPATIAL);
|
||||
String code = "shader_type spatial;";
|
||||
CHECK_EQ(sl.compile(code, info), Error::OK);
|
||||
}
|
||||
|
||||
// No keywords (except for built-in functions) should be valid identifiers.
|
||||
TEST_CASE("[ShaderLanguage] Ensure no reserved keywords are valid identifiers") {
|
||||
List<String> keywords;
|
||||
List<String> builtin_functions;
|
||||
ShaderLanguage::get_keyword_list(&keywords);
|
||||
ShaderLanguage::get_builtin_funcs(&builtin_functions);
|
||||
|
||||
HashSet<String> builtin_set;
|
||||
for (const String &keyword : builtin_functions) {
|
||||
builtin_set.insert(keyword);
|
||||
}
|
||||
|
||||
HashSet<String> non_func_keywords_set;
|
||||
for (const String &keyword : keywords) {
|
||||
if (!builtin_set.has(keyword)) {
|
||||
non_func_keywords_set.insert(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *decl_test_template[]{
|
||||
"shader_type %s;\nvoid %s() {}\n",
|
||||
"shader_type %s;\nvoid vertex() {float %s;}\n",
|
||||
"shader_type %s;\nuniform sampler2D %s;\n",
|
||||
"shader_type %s;\nconst float %s = 1.0;\n",
|
||||
nullptr
|
||||
};
|
||||
static const char *varying_template = "shader_type %s;\nvarying float %s;\n";
|
||||
Vector<String> non_varying_types = { "particles", "sky", "fog" };
|
||||
|
||||
auto shader_types_to_test = ShaderTypes::get_singleton()->get_types();
|
||||
for (auto shader_type : shader_types_to_test) {
|
||||
ShaderLanguage::ShaderCompileInfo info;
|
||||
get_compile_info(info, get_shader_mode(shader_type));
|
||||
// test templates with non-keyword identifiers
|
||||
|
||||
for (int i = 0; decl_test_template[i] != nullptr; i++) {
|
||||
String code = vformat(decl_test_template[i], shader_type, "foo");
|
||||
String result;
|
||||
ShaderLanguage sl;
|
||||
CHECK_EQ(sl.compile(code, info), Error::OK);
|
||||
}
|
||||
if (!non_varying_types.has(shader_type)) {
|
||||
String code = vformat(varying_template, shader_type, "foo");
|
||||
String result;
|
||||
ShaderLanguage sl;
|
||||
CHECK_EQ(sl.compile(code, info), Error::OK);
|
||||
}
|
||||
|
||||
for (const String &keyword : non_func_keywords_set) {
|
||||
for (int i = 0; decl_test_template[i] != nullptr; i++) {
|
||||
String code = vformat(decl_test_template[i], shader_type, keyword);
|
||||
String result;
|
||||
ShaderLanguage sl;
|
||||
CHECK_NE(sl.compile(code, info), Error::OK);
|
||||
}
|
||||
if (!non_varying_types.has(shader_type)) {
|
||||
String code = vformat(varying_template, shader_type, keyword);
|
||||
String result;
|
||||
ShaderLanguage sl;
|
||||
CHECK_NE(sl.compile(code, info), Error::OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TestShaderLanguage
|
||||
|
||||
#endif // TEST_SHADER_LANGUAGE_H
|
|
@ -129,6 +129,8 @@
|
|||
#include "tests/scene/test_viewport.h"
|
||||
#include "tests/scene/test_visual_shader.h"
|
||||
#include "tests/scene/test_window.h"
|
||||
#include "tests/servers/rendering/test_shader_converter.h"
|
||||
#include "tests/servers/rendering/test_shader_language.h"
|
||||
#include "tests/servers/rendering/test_shader_preprocessor.h"
|
||||
#include "tests/servers/test_text_server.h"
|
||||
#include "tests/test_validate_testing.h"
|
||||
|
@ -266,7 +268,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
|
|||
String name = String(p_in.m_name);
|
||||
String suite_name = String(p_in.m_test_suite);
|
||||
|
||||
if (name.contains("[SceneTree]") || name.contains("[Editor]")) {
|
||||
if (name.contains("[SceneTree]") || name.contains("[Editor]") || name.contains("[ShaderDeprecatedConverter]") || name.contains("[ShaderLanguage]")) {
|
||||
memnew(MessageQueue);
|
||||
|
||||
memnew(Input);
|
||||
|
|
Loading…
Reference in New Issue