Basic warning support implementation for the Godot Shading Language.
This commit is contained in:
parent
4828667759
commit
084648bd18
6 changed files with 569 additions and 18 deletions
|
@ -37,12 +37,18 @@
|
|||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/project_settings_editor.h"
|
||||
#include "editor/property_editor.h"
|
||||
#include "servers/display_server.h"
|
||||
#include "servers/rendering/shader_types.h"
|
||||
|
||||
/*** SHADER SCRIPT EDITOR ****/
|
||||
|
||||
static bool saved_warnings_enabled = false;
|
||||
static bool saved_treat_warning_as_errors = false;
|
||||
static Map<ShaderWarning::Code, bool> saved_warnings;
|
||||
static uint32_t saved_warning_flags = 0U;
|
||||
|
||||
Ref<Shader> ShaderTextEditor::get_edited_shader() const {
|
||||
return shader;
|
||||
}
|
||||
|
@ -82,6 +88,10 @@ void ShaderTextEditor::reload_text() {
|
|||
update_line_and_column();
|
||||
}
|
||||
|
||||
void ShaderTextEditor::set_warnings_panel(RichTextLabel *p_warnings_panel) {
|
||||
warnings_panel = p_warnings_panel;
|
||||
}
|
||||
|
||||
void ShaderTextEditor::_load_theme_settings() {
|
||||
CodeEdit *text_editor = get_text_editor();
|
||||
Color updated_marked_line_color = EDITOR_GET("text_editor/highlighting/mark_color");
|
||||
|
@ -141,6 +151,12 @@ void ShaderTextEditor::_load_theme_settings() {
|
|||
syntax_highlighter->clear_color_regions();
|
||||
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
|
||||
syntax_highlighter->add_color_region("//", "", comment_color, true);
|
||||
|
||||
if (warnings_panel) {
|
||||
// Warnings panel
|
||||
warnings_panel->add_theme_font_override("normal_font", EditorNode::get_singleton()->get_gui_base()->get_theme_font("main", "EditorFonts"));
|
||||
warnings_panel->add_theme_font_size_override("normal_font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size("main_size", "EditorFonts"));
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderTextEditor::_check_shader_mode() {
|
||||
|
@ -187,6 +203,9 @@ void ShaderTextEditor::_validate_script() {
|
|||
|
||||
ShaderLanguage sl;
|
||||
|
||||
sl.enable_warning_checking(saved_warnings_enabled);
|
||||
sl.set_warning_flags(saved_warning_flags);
|
||||
|
||||
Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type);
|
||||
|
||||
if (err != OK) {
|
||||
|
@ -197,7 +216,6 @@ void ShaderTextEditor::_validate_script() {
|
|||
get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
|
||||
}
|
||||
get_text_editor()->set_line_background_color(sl.get_error_line() - 1, marked_line_color);
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
|
||||
get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
|
||||
|
@ -205,9 +223,60 @@ void ShaderTextEditor::_validate_script() {
|
|||
set_error("");
|
||||
}
|
||||
|
||||
if (warnings.size() > 0 || err != OK) {
|
||||
warnings_panel->clear();
|
||||
}
|
||||
warnings.clear();
|
||||
for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) {
|
||||
warnings.push_back(E->get());
|
||||
}
|
||||
if (warnings.size() > 0 && err == OK) {
|
||||
warnings.sort_custom<WarningsComparator>();
|
||||
_update_warning_panel();
|
||||
} else {
|
||||
set_warning_nb(0);
|
||||
}
|
||||
emit_signal("script_changed");
|
||||
}
|
||||
|
||||
void ShaderTextEditor::_update_warning_panel() {
|
||||
int warning_count = 0;
|
||||
|
||||
warnings_panel->push_table(2);
|
||||
for (int i = 0; i < warnings.size(); i++) {
|
||||
ShaderWarning &w = warnings[i];
|
||||
|
||||
if (warning_count == 0) {
|
||||
if (saved_treat_warning_as_errors) {
|
||||
String error_text = "error(" + itos(w.get_line()) + "): " + w.get_message() + " " + TTR("Warnings should be fixed to prevent errors.");
|
||||
set_error_pos(w.get_line() - 1, 0);
|
||||
set_error(error_text);
|
||||
get_text_editor()->set_line_background_color(w.get_line() - 1, marked_line_color);
|
||||
}
|
||||
}
|
||||
|
||||
warning_count++;
|
||||
|
||||
// First cell.
|
||||
warnings_panel->push_cell();
|
||||
warnings_panel->push_meta(w.get_line() - 1);
|
||||
warnings_panel->push_color(warnings_panel->get_theme_color("warning_color", "Editor"));
|
||||
warnings_panel->add_text(TTR("Line") + " " + itos(w.get_line()));
|
||||
warnings_panel->add_text(" (" + w.get_name() + "):");
|
||||
warnings_panel->pop(); // Color.
|
||||
warnings_panel->pop(); // Meta goto.
|
||||
warnings_panel->pop(); // Cell.
|
||||
|
||||
// Second cell.
|
||||
warnings_panel->push_cell();
|
||||
warnings_panel->add_text(w.get_message());
|
||||
warnings_panel->pop(); // Cell.
|
||||
}
|
||||
warnings_panel->pop(); // Table.
|
||||
|
||||
set_warning_nb(warning_count);
|
||||
}
|
||||
|
||||
void ShaderTextEditor::_bind_methods() {
|
||||
}
|
||||
|
||||
|
@ -321,10 +390,6 @@ void ShaderEditor::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void ShaderEditor::_params_changed() {
|
||||
shader_editor->_validate_script();
|
||||
}
|
||||
|
||||
void ShaderEditor::_editor_settings_changed() {
|
||||
shader_editor->update_editor_settings();
|
||||
|
||||
|
@ -333,8 +398,19 @@ void ShaderEditor::_editor_settings_changed() {
|
|||
shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false);
|
||||
}
|
||||
|
||||
void ShaderEditor::_show_warnings_panel(bool p_show) {
|
||||
warnings_panel->set_visible(p_show);
|
||||
}
|
||||
|
||||
void ShaderEditor::_warning_clicked(Variant p_line) {
|
||||
if (p_line.get_type() == Variant::INT) {
|
||||
shader_editor->get_text_editor()->cursor_set_line(p_line.operator int64_t());
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderEditor::_bind_methods() {
|
||||
ClassDB::bind_method("_params_changed", &ShaderEditor::_params_changed);
|
||||
ClassDB::bind_method("_show_warnings_panel", &ShaderEditor::_show_warnings_panel);
|
||||
ClassDB::bind_method("_warning_clicked", &ShaderEditor::_warning_clicked);
|
||||
}
|
||||
|
||||
void ShaderEditor::ensure_select_current() {
|
||||
|
@ -352,6 +428,47 @@ void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
|
|||
shader_editor->goto_line_selection(p_line, p_begin, p_end);
|
||||
}
|
||||
|
||||
void ShaderEditor::_project_settings_changed() {
|
||||
_update_warnings(true);
|
||||
}
|
||||
|
||||
void ShaderEditor::_update_warnings(bool p_validate) {
|
||||
bool changed = false;
|
||||
|
||||
bool warnings_enabled = GLOBAL_GET("debug/shader_language/warnings/enable").booleanize();
|
||||
if (warnings_enabled != saved_warnings_enabled) {
|
||||
saved_warnings_enabled = warnings_enabled;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
bool treat_warning_as_errors = GLOBAL_GET("debug/shader_language/warnings/treat_warnings_as_errors").booleanize();
|
||||
if (treat_warning_as_errors != saved_treat_warning_as_errors) {
|
||||
saved_treat_warning_as_errors = treat_warning_as_errors;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
bool update_flags = false;
|
||||
|
||||
for (int i = 0; i < ShaderWarning::WARNING_MAX; i++) {
|
||||
ShaderWarning::Code code = (ShaderWarning::Code)i;
|
||||
bool value = GLOBAL_GET("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code(code).to_lower());
|
||||
|
||||
if (saved_warnings[code] != value) {
|
||||
saved_warnings[code] = value;
|
||||
update_flags = true;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_flags) {
|
||||
saved_warning_flags = (uint32_t)ShaderWarning::get_flags_from_codemap(saved_warnings);
|
||||
}
|
||||
|
||||
if (p_validate && changed && shader_editor && shader_editor->get_edited_shader().is_valid()) {
|
||||
shader_editor->validate_script();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderEditor::_check_for_external_edit() {
|
||||
if (shader.is_null() || !shader.is_valid()) {
|
||||
return;
|
||||
|
@ -523,13 +640,22 @@ void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) {
|
|||
}
|
||||
|
||||
ShaderEditor::ShaderEditor(EditorNode *p_node) {
|
||||
GLOBAL_DEF("debug/shader_language/warnings/enable", true);
|
||||
GLOBAL_DEF("debug/shader_language/warnings/treat_warnings_as_errors", false);
|
||||
for (int i = 0; i < (int)ShaderWarning::WARNING_MAX; i++) {
|
||||
GLOBAL_DEF("debug/shader_language/warnings/" + ShaderWarning::get_name_from_code((ShaderWarning::Code)i).to_lower(), true);
|
||||
}
|
||||
_update_warnings(false);
|
||||
|
||||
shader_editor = memnew(ShaderTextEditor);
|
||||
shader_editor->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
shader_editor->add_theme_constant_override("separation", 0);
|
||||
shader_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
|
||||
|
||||
shader_editor->connect("show_warnings_panel", callable_mp(this, &ShaderEditor::_show_warnings_panel));
|
||||
shader_editor->connect("script_changed", callable_mp(this, &ShaderEditor::apply_shaders));
|
||||
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ShaderEditor::_editor_settings_changed));
|
||||
ProjectSettingsEditor::get_singleton()->connect("confirmed", callable_mp(this, &ShaderEditor::_project_settings_changed));
|
||||
|
||||
shader_editor->get_text_editor()->set_callhint_settings(
|
||||
EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"),
|
||||
|
@ -614,7 +740,23 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) {
|
|||
hbc->add_child(goto_menu);
|
||||
hbc->add_child(help_menu);
|
||||
hbc->add_theme_style_override("panel", p_node->get_gui_base()->get_theme_stylebox("ScriptEditorPanel", "EditorStyles"));
|
||||
main_container->add_child(shader_editor);
|
||||
|
||||
VSplitContainer *editor_box = memnew(VSplitContainer);
|
||||
main_container->add_child(editor_box);
|
||||
editor_box->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
|
||||
editor_box->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
editor_box->add_child(shader_editor);
|
||||
|
||||
warnings_panel = memnew(RichTextLabel);
|
||||
warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
|
||||
warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
warnings_panel->set_meta_underline(true);
|
||||
warnings_panel->set_selection_enabled(true);
|
||||
warnings_panel->set_focus_mode(FOCUS_CLICK);
|
||||
warnings_panel->hide();
|
||||
warnings_panel->connect("meta_clicked", callable_mp(this, &ShaderEditor::_warning_clicked));
|
||||
editor_box->add_child(warnings_panel);
|
||||
shader_editor->set_warnings_panel(warnings_panel);
|
||||
|
||||
goto_line_dialog = memnew(GotoLineDialog);
|
||||
add_child(goto_line_dialog);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "editor/editor_plugin.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
#include "scene/main/timer.h"
|
||||
|
@ -46,10 +47,17 @@ class ShaderTextEditor : public CodeTextEditor {
|
|||
|
||||
Color marked_line_color = Color(1, 1, 1);
|
||||
|
||||
struct WarningsComparator {
|
||||
_ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); }
|
||||
};
|
||||
|
||||
Ref<CodeHighlighter> syntax_highlighter;
|
||||
RichTextLabel *warnings_panel = nullptr;
|
||||
Ref<Shader> shader;
|
||||
List<ShaderWarning> warnings;
|
||||
|
||||
void _check_shader_mode();
|
||||
void _update_warning_panel();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -61,6 +69,7 @@ public:
|
|||
virtual void _validate_script() override;
|
||||
|
||||
void reload_text();
|
||||
void set_warnings_panel(RichTextLabel *p_warnings_panel);
|
||||
|
||||
Ref<Shader> get_edited_shader() const;
|
||||
void set_edited_shader(const Ref<Shader> &p_shader);
|
||||
|
@ -102,6 +111,7 @@ class ShaderEditor : public PanelContainer {
|
|||
PopupMenu *bookmarks_menu;
|
||||
MenuButton *help_menu;
|
||||
PopupMenu *context_menu;
|
||||
RichTextLabel *warnings_panel = nullptr;
|
||||
uint64_t idle;
|
||||
|
||||
GotoLineDialog *goto_line_dialog;
|
||||
|
@ -111,13 +121,16 @@ class ShaderEditor : public PanelContainer {
|
|||
ShaderTextEditor *shader_editor;
|
||||
|
||||
void _menu_option(int p_option);
|
||||
void _params_changed();
|
||||
mutable Ref<Shader> shader;
|
||||
|
||||
void _editor_settings_changed();
|
||||
void _project_settings_changed();
|
||||
|
||||
void _check_for_external_edit();
|
||||
void _reload_shader_from_disk();
|
||||
void _show_warnings_panel(bool p_show);
|
||||
void _warning_clicked(Variant p_line);
|
||||
void _update_warnings(bool p_validate);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "core/string/print_string.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
#define HAS_WARNING(flag) (warning_flags & flag)
|
||||
|
||||
static bool _is_text_char(char32_t c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
|
||||
}
|
||||
|
@ -901,6 +903,8 @@ bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) {
|
|||
|
||||
void ShaderLanguage::clear() {
|
||||
current_function = StringName();
|
||||
last_name = StringName();
|
||||
last_type = IDENTIFIER_MAX;
|
||||
|
||||
completion_type = COMPLETION_NONE;
|
||||
completion_block = nullptr;
|
||||
|
@ -908,12 +912,20 @@ void ShaderLanguage::clear() {
|
|||
completion_class = SubClassTag::TAG_GLOBAL;
|
||||
completion_struct = StringName();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
used_constants.clear();
|
||||
used_varyings.clear();
|
||||
used_uniforms.clear();
|
||||
used_functions.clear();
|
||||
used_structs.clear();
|
||||
warnings.clear();
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
error_line = 0;
|
||||
tk_line = 1;
|
||||
char_idx = 0;
|
||||
error_set = false;
|
||||
error_str = "";
|
||||
last_const = false;
|
||||
while (nodes) {
|
||||
Node *n = nodes;
|
||||
nodes = nodes->next;
|
||||
|
@ -921,6 +933,35 @@ void ShaderLanguage::clear() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, IdentifierType p_type) {
|
||||
switch (p_type) {
|
||||
case IdentifierType::IDENTIFIER_CONSTANT:
|
||||
if (HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG) && used_constants.has(p_identifier)) {
|
||||
used_constants[p_identifier].used = true;
|
||||
}
|
||||
break;
|
||||
case IdentifierType::IDENTIFIER_VARYING:
|
||||
if (HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG) && used_varyings.has(p_identifier)) {
|
||||
used_varyings[p_identifier].used = true;
|
||||
}
|
||||
break;
|
||||
case IdentifierType::IDENTIFIER_UNIFORM:
|
||||
if (HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG) && used_uniforms.has(p_identifier)) {
|
||||
used_uniforms[p_identifier].used = true;
|
||||
}
|
||||
break;
|
||||
case IdentifierType::IDENTIFIER_FUNCTION:
|
||||
if (HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG) && used_functions.has(p_identifier)) {
|
||||
used_functions[p_identifier].used = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) {
|
||||
if (p_function_info.built_ins.has(p_identifier)) {
|
||||
if (r_data_type) {
|
||||
|
@ -3602,6 +3643,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
if (shader->structs.has(identifier)) {
|
||||
pstruct = shader->structs[identifier].shader_struct;
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG) && used_structs.has(identifier)) {
|
||||
used_structs[identifier].used = true;
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
struct_init = true;
|
||||
}
|
||||
|
||||
|
@ -3825,11 +3871,17 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
}
|
||||
}
|
||||
expr = func;
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings) {
|
||||
_parse_used_identifier(name, IdentifierType::IDENTIFIER_FUNCTION);
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
} else {
|
||||
//an identifier
|
||||
|
||||
last_const = false;
|
||||
last_name = identifier;
|
||||
last_type = IDENTIFIER_MAX;
|
||||
_set_tkpos(pos);
|
||||
|
||||
DataType data_type;
|
||||
|
@ -3874,12 +3926,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
}
|
||||
}
|
||||
}
|
||||
last_const = is_const;
|
||||
|
||||
if (ident_type == IDENTIFIER_FUNCTION) {
|
||||
_set_error("Can't use function as identifier: " + String(identifier));
|
||||
return nullptr;
|
||||
}
|
||||
if (is_const) {
|
||||
last_type = IDENTIFIER_CONSTANT;
|
||||
} else {
|
||||
last_type = ident_type;
|
||||
}
|
||||
}
|
||||
|
||||
Node *index_expression = nullptr;
|
||||
|
@ -3953,7 +4009,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
arrname->assign_expression = assign_expression;
|
||||
arrname->is_const = is_const;
|
||||
expr = arrname;
|
||||
|
||||
} else {
|
||||
VariableNode *varname = alloc_node<VariableNode>();
|
||||
varname->name = identifier;
|
||||
|
@ -3962,6 +4017,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
varname->struct_name = struct_name;
|
||||
expr = varname;
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings) {
|
||||
_parse_used_identifier(identifier, ident_type);
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
} else if (tk.type == TK_OP_ADD) {
|
||||
continue; //this one does nothing
|
||||
|
@ -4290,8 +4350,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
if (array_size > 0) {
|
||||
tk = _get_token();
|
||||
if (tk.type == TK_OP_ASSIGN) {
|
||||
if (last_const) {
|
||||
last_const = false;
|
||||
if (last_type == IDENTIFIER_CONSTANT) {
|
||||
_set_error("Constants cannot be modified.");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4648,9 +4707,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
|
||||
bool unary = false;
|
||||
bool ternary = false;
|
||||
Operator op = expression[i].op;
|
||||
|
||||
int priority;
|
||||
switch (expression[i].op) {
|
||||
switch (op) {
|
||||
case OP_EQUAL:
|
||||
priority = 8;
|
||||
break;
|
||||
|
@ -4771,6 +4831,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
|
|||
ERR_FAIL_V(nullptr); //unexpected operator
|
||||
}
|
||||
|
||||
#if DEBUG_ENABLED
|
||||
if (check_warnings && HAS_WARNING(ShaderWarning::FLOAT_COMPARISON_FLAG) && (op == OP_EQUAL || op == OP_NOT_EQUAL) && expression[i - 1].node->get_datatype() == TYPE_FLOAT && expression[i + 1].node->get_datatype() == TYPE_FLOAT) {
|
||||
_add_line_warning(ShaderWarning::FLOAT_COMPARISON);
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
if (priority < min_priority) {
|
||||
// < is used for left to right (default)
|
||||
// <= is used for right to left
|
||||
|
@ -5483,7 +5549,6 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
|||
p_block->statements.push_back(vardecl);
|
||||
|
||||
p_block->variables[name] = var;
|
||||
|
||||
if (tk.type == TK_COMMA) {
|
||||
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR) {
|
||||
_set_error("Multiple declarations in 'for' loop are not implemented yet.");
|
||||
|
@ -6313,7 +6378,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
}
|
||||
shader->structs[st.name] = st;
|
||||
shader->vstructs.push_back(st); // struct's order is important!
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG)) {
|
||||
used_structs.insert(st.name, Usage(tk_line));
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
} break;
|
||||
case TK_GLOBAL: {
|
||||
tk = _get_token();
|
||||
|
@ -6660,6 +6729,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
}
|
||||
|
||||
shader->uniforms[name] = uniform2;
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG)) {
|
||||
used_uniforms.insert(name, Usage(tk_line));
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
//reset scope for next uniform
|
||||
uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
|
||||
|
||||
|
@ -6703,6 +6778,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
}
|
||||
|
||||
shader->varyings[name] = varying;
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG)) {
|
||||
used_varyings.insert(name, Usage(tk_line));
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
} break;
|
||||
|
@ -7049,6 +7129,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
|
||||
shader->constants[name] = constant;
|
||||
shader->vconstants.push_back(constant);
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG)) {
|
||||
used_constants.insert(name, Usage(tk_line));
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
if (tk.type == TK_COMMA) {
|
||||
tk = _get_token();
|
||||
|
@ -7110,6 +7195,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
|||
|
||||
if (p_functions.has(name)) {
|
||||
func_node->can_discard = p_functions[name].can_discard;
|
||||
} else {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG)) {
|
||||
used_functions.insert(name, Usage(tk_line));
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
func_node->body = alloc_node<BlockNode>();
|
||||
|
@ -7443,6 +7534,33 @@ String ShaderLanguage::get_shader_type(const String &p_code) {
|
|||
return String();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void ShaderLanguage::_check_warning_accums() {
|
||||
for (Map<ShaderWarning::Code, Map<StringName, Usage> *>::Element *E = warnings_check_map.front(); E; E = E->next()) {
|
||||
for (const Map<StringName, Usage>::Element *U = (*E->get()).front(); U; U = U->next()) {
|
||||
if (!U->get().used) {
|
||||
_add_warning(E->key(), U->get().decl_line, U->key());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
List<ShaderWarning>::Element *ShaderLanguage::get_warnings_ptr() {
|
||||
return warnings.front();
|
||||
}
|
||||
void ShaderLanguage::enable_warning_checking(bool p_enabled) {
|
||||
check_warnings = p_enabled;
|
||||
}
|
||||
bool ShaderLanguage::is_warning_checking_enabled() const {
|
||||
return check_warnings;
|
||||
}
|
||||
void ShaderLanguage::set_warning_flags(uint32_t p_flags) {
|
||||
warning_flags = p_flags;
|
||||
}
|
||||
uint32_t ShaderLanguage::get_warning_flags() const {
|
||||
return warning_flags;
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) {
|
||||
clear();
|
||||
|
||||
|
@ -7455,6 +7573,12 @@ Error ShaderLanguage::compile(const String &p_code, const Map<StringName, Functi
|
|||
shader = alloc_node<ShaderNode>();
|
||||
Error err = _parse_shader(p_functions, p_render_modes, p_shader_types);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (check_warnings) {
|
||||
_check_warning_accums();
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
@ -7864,6 +7988,14 @@ ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() {
|
|||
ShaderLanguage::ShaderLanguage() {
|
||||
nodes = nullptr;
|
||||
completion_class = TAG_GLOBAL;
|
||||
|
||||
#if DEBUG_ENABLED
|
||||
warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants);
|
||||
warnings_check_map.insert(ShaderWarning::UNUSED_FUNCTION, &used_functions);
|
||||
warnings_check_map.insert(ShaderWarning::UNUSED_STRUCT, &used_structs);
|
||||
warnings_check_map.insert(ShaderWarning::UNUSED_UNIFORM, &used_uniforms);
|
||||
warnings_check_map.insert(ShaderWarning::UNUSED_VARYING, &used_varyings);
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
ShaderLanguage::~ShaderLanguage() {
|
||||
|
|
|
@ -39,6 +39,10 @@
|
|||
#include "core/typedefs.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#include "shader_warnings.h"
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
class ShaderLanguage {
|
||||
public:
|
||||
struct TkPos {
|
||||
|
@ -803,12 +807,42 @@ private:
|
|||
String error_str;
|
||||
int error_line;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
struct Usage {
|
||||
int decl_line;
|
||||
bool used = false;
|
||||
Usage(int p_decl_line = -1) {
|
||||
decl_line = p_decl_line;
|
||||
}
|
||||
};
|
||||
|
||||
Map<StringName, Usage> used_constants;
|
||||
Map<StringName, Usage> used_varyings;
|
||||
Map<StringName, Usage> used_uniforms;
|
||||
Map<StringName, Usage> used_functions;
|
||||
Map<StringName, Usage> used_structs;
|
||||
Map<ShaderWarning::Code, Map<StringName, Usage> *> warnings_check_map;
|
||||
|
||||
List<ShaderWarning> warnings;
|
||||
|
||||
bool check_warnings = false;
|
||||
uint32_t warning_flags;
|
||||
|
||||
void _add_line_warning(ShaderWarning::Code p_code, const StringName &p_subject = "") {
|
||||
warnings.push_back(ShaderWarning(p_code, tk_line, p_subject));
|
||||
}
|
||||
void _add_warning(ShaderWarning::Code p_code, int p_line, const StringName &p_subject = "") {
|
||||
warnings.push_back(ShaderWarning(p_code, p_line, p_subject));
|
||||
}
|
||||
void _check_warning_accums();
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
String code;
|
||||
int char_idx;
|
||||
int tk_line;
|
||||
|
||||
StringName current_function;
|
||||
bool last_const = false;
|
||||
StringName last_name;
|
||||
|
||||
VaryingFunctionNames varying_function_names;
|
||||
|
||||
|
@ -849,9 +883,15 @@ private:
|
|||
IDENTIFIER_LOCAL_VAR,
|
||||
IDENTIFIER_BUILTIN_VAR,
|
||||
IDENTIFIER_CONSTANT,
|
||||
IDENTIFIER_MAX,
|
||||
};
|
||||
|
||||
IdentifierType last_type = IDENTIFIER_MAX;
|
||||
|
||||
bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, ConstantNode::Value *r_constant_value = nullptr);
|
||||
#ifdef DEBUG_ENABLED
|
||||
void _parse_used_identifier(const StringName &p_identifier, IdentifierType p_type);
|
||||
#endif // DEBUG_ENABLED
|
||||
bool _is_operator_assign(Operator p_op) const;
|
||||
bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr);
|
||||
bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr);
|
||||
|
@ -910,6 +950,16 @@ private:
|
|||
Error _find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op);
|
||||
|
||||
public:
|
||||
#ifdef DEBUG_ENABLED
|
||||
List<ShaderWarning>::Element *get_warnings_ptr();
|
||||
|
||||
void enable_warning_checking(bool p_enabled);
|
||||
bool is_warning_checking_enabled() const;
|
||||
|
||||
void set_warning_flags(uint32_t p_flags);
|
||||
uint32_t get_warning_flags() const;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
//static void get_keyword_list(ShaderType p_type,List<String> *p_keywords);
|
||||
|
||||
void clear();
|
||||
|
|
131
servers/rendering/shader_warnings.cpp
Normal file
131
servers/rendering/shader_warnings.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*************************************************************************/
|
||||
/* shader_warnings.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "shader_warnings.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
ShaderWarning::Code ShaderWarning::get_code() const {
|
||||
return code;
|
||||
}
|
||||
|
||||
int ShaderWarning::get_line() const {
|
||||
return line;
|
||||
}
|
||||
|
||||
const StringName &ShaderWarning::get_subject() const {
|
||||
return subject;
|
||||
}
|
||||
|
||||
String ShaderWarning::get_message() const {
|
||||
switch (code) {
|
||||
case FLOAT_COMPARISON:
|
||||
return vformat("Direct floating-point comparison (this may not evaluate to `true` as you expect). Instead, use `abs(a - b) < 0.0001` for an approximate but predictable comparison.");
|
||||
case UNUSED_CONSTANT:
|
||||
return vformat("The const '%s' is declared but never used.", subject);
|
||||
case UNUSED_FUNCTION:
|
||||
return vformat("The function '%s' is declared but never used.", subject);
|
||||
case UNUSED_STRUCT:
|
||||
return vformat("The struct '%s' is declared but never used.", subject);
|
||||
case UNUSED_UNIFORM:
|
||||
return vformat("The uniform '%s' is declared but never used.", subject);
|
||||
case UNUSED_VARYING:
|
||||
return vformat("The varying '%s' is declared but never used.", subject);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
String ShaderWarning::get_name() const {
|
||||
return get_name_from_code(code);
|
||||
}
|
||||
|
||||
String ShaderWarning::get_name_from_code(Code p_code) {
|
||||
ERR_FAIL_INDEX_V(p_code, WARNING_MAX, String());
|
||||
|
||||
static const char *names[] = {
|
||||
"FLOAT_COMPARISON",
|
||||
"UNUSED_CONSTANT",
|
||||
"UNUSED_FUNCTION",
|
||||
"UNUSED_STRUCT",
|
||||
"UNUSED_UNIFORM",
|
||||
"UNUSED_VARYING",
|
||||
};
|
||||
|
||||
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
|
||||
|
||||
return names[(int)p_code];
|
||||
}
|
||||
|
||||
ShaderWarning::Code ShaderWarning::get_code_from_name(const String &p_name) {
|
||||
for (int i = 0; i < WARNING_MAX; i++) {
|
||||
if (get_name_from_code((Code)i) == p_name) {
|
||||
return (Code)i;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_V_MSG(WARNING_MAX, "Invalid shader warning name: " + p_name);
|
||||
}
|
||||
|
||||
static Map<int, uint32_t> *code_to_flags_map = nullptr;
|
||||
|
||||
static void init_code_to_flags_map() {
|
||||
code_to_flags_map = memnew((Map<int, uint32_t>));
|
||||
code_to_flags_map->insert(ShaderWarning::FLOAT_COMPARISON, ShaderWarning::FLOAT_COMPARISON_FLAG);
|
||||
code_to_flags_map->insert(ShaderWarning::UNUSED_CONSTANT, ShaderWarning::UNUSED_CONSTANT_FLAG);
|
||||
code_to_flags_map->insert(ShaderWarning::UNUSED_FUNCTION, ShaderWarning::UNUSED_FUNCTION_FLAG);
|
||||
code_to_flags_map->insert(ShaderWarning::UNUSED_STRUCT, ShaderWarning::UNUSED_STRUCT_FLAG);
|
||||
code_to_flags_map->insert(ShaderWarning::UNUSED_UNIFORM, ShaderWarning::UNUSED_UNIFORM_FLAG);
|
||||
code_to_flags_map->insert(ShaderWarning::UNUSED_VARYING, ShaderWarning::UNUSED_VARYING_FLAG);
|
||||
}
|
||||
|
||||
ShaderWarning::CodeFlags ShaderWarning::get_flags_from_codemap(const Map<Code, bool> &p_map) {
|
||||
uint32_t result = 0U;
|
||||
|
||||
if (code_to_flags_map == nullptr) {
|
||||
init_code_to_flags_map();
|
||||
}
|
||||
|
||||
for (Map<Code, bool>::Element *E = p_map.front(); E; E = E->next()) {
|
||||
if (E->get()) {
|
||||
ERR_FAIL_COND_V(!code_to_flags_map->has((int)E->key()), ShaderWarning::NONE_FLAG);
|
||||
result |= (*code_to_flags_map)[(int)E->key()];
|
||||
}
|
||||
}
|
||||
return (CodeFlags)result;
|
||||
}
|
||||
|
||||
ShaderWarning::ShaderWarning(Code p_code, int p_line, const StringName &p_subject) :
|
||||
code(p_code), line(p_line), subject(p_subject) {
|
||||
}
|
||||
|
||||
#endif // DEBUG_ENABLED
|
83
servers/rendering/shader_warnings.h
Normal file
83
servers/rendering/shader_warnings.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*************************************************************************/
|
||||
/* shader_warnings.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_WARNINGS
|
||||
#define SHADER_WARNINGS
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
#include "core/string/string_name.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/map.h"
|
||||
|
||||
class ShaderWarning {
|
||||
public:
|
||||
enum Code {
|
||||
FLOAT_COMPARISON,
|
||||
UNUSED_CONSTANT,
|
||||
UNUSED_FUNCTION,
|
||||
UNUSED_STRUCT,
|
||||
UNUSED_UNIFORM,
|
||||
UNUSED_VARYING,
|
||||
WARNING_MAX,
|
||||
};
|
||||
|
||||
enum CodeFlags : uint32_t {
|
||||
NONE_FLAG = 0U,
|
||||
FLOAT_COMPARISON_FLAG = 1U,
|
||||
UNUSED_CONSTANT_FLAG = 2U,
|
||||
UNUSED_FUNCTION_FLAG = 4U,
|
||||
UNUSED_STRUCT_FLAG = 8U,
|
||||
UNUSED_UNIFORM_FLAG = 16U,
|
||||
UNUSED_VARYING_FLAG = 32U,
|
||||
};
|
||||
|
||||
private:
|
||||
Code code;
|
||||
int line;
|
||||
StringName subject;
|
||||
|
||||
public:
|
||||
Code get_code() const;
|
||||
int get_line() const;
|
||||
const StringName &get_subject() const;
|
||||
String get_message() const;
|
||||
String get_name() const;
|
||||
|
||||
static String get_name_from_code(Code p_code);
|
||||
static Code get_code_from_name(const String &p_name);
|
||||
static CodeFlags get_flags_from_codemap(const Map<Code, bool> &p_map);
|
||||
|
||||
ShaderWarning(Code p_code = WARNING_MAX, int p_line = -1, const StringName &p_subject = "");
|
||||
};
|
||||
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#endif // SHADER_WARNINGS
|
Loading…
Reference in a new issue