diff options
123 files changed, 609 insertions, 189 deletions
| diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 9ba653e1a9..9f89f5d8c9 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -1813,3 +1813,24 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo  	return OK;  } + +Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count) { +	// We always allocate a new array, and we don't memcpy. +	// We also don't consider returning a pointer to the passed vectors when sizeof(real_t) == 4. +	// One reason is that we could decide to put a 4th component in Vector3 for SIMD/mobile performance, +	// which would cause trouble with these optimizations. +	Vector<float> floats; +	if (count == 0) { +		return floats; +	} +	floats.resize(count * 3); +	float *floats_w = floats.ptrw(); +	for (size_t i = 0; i < count; ++i) { +		const Vector3 v = vecs[i]; +		floats_w[0] = v.x; +		floats_w[1] = v.y; +		floats_w[2] = v.z; +		floats_w += 3; +	} +	return floats; +} diff --git a/core/io/marshalls.h b/core/io/marshalls.h index fef3a1c2c1..66e2571066 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -215,4 +215,6 @@ public:  Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false, int p_depth = 0);  Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0); +Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count); +  #endif // MARSHALLS_H diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 8dff8e6e7e..a998dece2a 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -453,7 +453,10 @@ public:  	}  	static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {  		double range = max - min; -		double result = is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range)); +		if (is_zero_approx(range)) { +			return min; +		} +		double result = value - (range * Math::floor((value - min) / range));  		if (is_equal_approx(result, max)) {  			return min;  		} @@ -461,7 +464,10 @@ public:  	}  	static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) {  		float range = max - min; -		float result = is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range)); +		if (is_zero_approx(range)) { +			return min; +		} +		float result = value - (range * Math::floor((value - min) / range));  		if (is_equal_approx(result, max)) {  			return min;  		} diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index ac569941bf..57d62dac91 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -65,11 +65,13 @@ static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...) const, Variant *b  template <class T, class... P>  static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { +	VariantInternal::clear(&r_ret);  	call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);  }  template <class T, class... P>  static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { +	VariantInternal::clear(&r_ret);  	call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);  } diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 67b692a7a0..d4c42e36eb 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -464,9 +464,6 @@  		<member name="debug/gdscript/warnings/unused_variable" type="int" setter="" getter="" default="1">  			When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused.  		</member> -		<member name="debug/gdscript/warnings/void_assignment" type="int" setter="" getter="" default="1"> -			When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when assigning the result of a function that returns [code]void[/code] to a variable. -		</member>  		<member name="debug/settings/crash_handler/message" type="String" setter="" getter="" default=""Please include this when reporting the bug to the project developer."">  			Message to be displayed before the backtrace when the engine crashes. By default, this message is only used in exported projects due to the editor-only override applied to this setting.  		</member> diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index 8b31781c5e..351af4508f 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -118,6 +118,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou  	item_menu->add_icon_item(get_theme_icon(SNAME("CreateNewSceneFrom"), SNAME("EditorIcons")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE);  	item_menu->add_icon_item(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH);  	item_menu->set_position(get_screen_position() + get_local_mouse_position()); +	item_menu->reset_size();  	item_menu->popup();  } @@ -323,6 +324,8 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {  				file_dialog->add_filter("*." + extensions[i], extensions[i].to_upper());  			} +			String filename = get_selected_path().get_file() + "." + extensions.front()->get().to_lower(); +			file_dialog->set_current_path(filename);  			file_dialog->popup_file_dialog();  		} break;  		case ITEM_MENU_COPY_NODE_PATH: { diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index affb31945d..1f350f1199 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -219,6 +219,7 @@ PluginConfigDialog::PluginConfigDialog() {  	GridContainer *grid = memnew(GridContainer);  	grid->set_columns(3); +	grid->set_v_size_flags(Control::SIZE_EXPAND_FILL);  	vbox->add_child(grid);  	// Plugin Name @@ -234,6 +235,7 @@ PluginConfigDialog::PluginConfigDialog() {  	name_edit = memnew(LineEdit);  	name_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));  	name_edit->set_placeholder("MyPlugin"); +	name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);  	grid->add_child(name_edit);  	// Subfolder @@ -248,6 +250,7 @@ PluginConfigDialog::PluginConfigDialog() {  	subfolder_edit = memnew(LineEdit);  	subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); +	subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);  	subfolder_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));  	grid->add_child(subfolder_edit); @@ -263,6 +266,8 @@ PluginConfigDialog::PluginConfigDialog() {  	desc_edit = memnew(TextEdit);  	desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);  	desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); +	desc_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); +	desc_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL);  	grid->add_child(desc_edit);  	// Author @@ -276,6 +281,7 @@ PluginConfigDialog::PluginConfigDialog() {  	author_edit = memnew(LineEdit);  	author_edit->set_placeholder("Godette"); +	author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);  	grid->add_child(author_edit);  	// Version @@ -289,6 +295,7 @@ PluginConfigDialog::PluginConfigDialog() {  	version_edit = memnew(LineEdit);  	version_edit->set_placeholder("1.0"); +	version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);  	grid->add_child(version_edit);  	// Language dropdown @@ -326,6 +333,7 @@ PluginConfigDialog::PluginConfigDialog() {  	script_edit = memnew(LineEdit);  	script_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));  	script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); +	script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);  	grid->add_child(script_edit);  	// Activate now checkbox diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 5f9446c3b1..cbe30f7e0f 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -436,6 +436,7 @@ EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {  }  EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(sphere);  	RS::get_singleton()->free(sphere_instance);  	RS::get_singleton()->free(viewport); @@ -767,6 +768,7 @@ EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {  }  EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	//RS::get_singleton()->free(sphere);  	RS::get_singleton()->free(mesh_instance);  	RS::get_singleton()->free(viewport); @@ -867,6 +869,7 @@ EditorFontPreviewPlugin::EditorFontPreviewPlugin() {  }  EditorFontPreviewPlugin::~EditorFontPreviewPlugin() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(canvas_item);  	RS::get_singleton()->free(canvas);  	RS::get_singleton()->free(viewport); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 91c7fbc06a..0df53bddd1 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -100,6 +100,7 @@ bool EditorNode3DGizmo::is_editable() const {  }  void EditorNode3DGizmo::clear() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	for (int i = 0; i < instances.size(); i++) {  		if (instances[i].instance.is_valid()) {  			RS::get_singleton()->free(instances[i].instance); @@ -809,6 +810,7 @@ void EditorNode3DGizmo::transform() {  }  void EditorNode3DGizmo::free() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	ERR_FAIL_COND(!spatial_node);  	ERR_FAIL_COND(!valid); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 4976c8c750..8e2b9b4bce 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -3557,6 +3557,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {  }  void Node3DEditorViewport::_finish_gizmo_instances() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	for (int i = 0; i < 3; i++) {  		RS::get_singleton()->free(move_gizmo_instance[i]);  		RS::get_singleton()->free(move_plane_gizmo_instance[i]); @@ -5536,6 +5537,7 @@ Node3DEditorViewportContainer::Node3DEditorViewportContainer() {  Node3DEditor *Node3DEditor::singleton = nullptr;  Node3DEditorSelectedItem::~Node3DEditorSelectedItem() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (sbox_instance.is_valid()) {  		RenderingServer::get_singleton()->free(sbox_instance);  	} diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index bb5491fcb5..562eddbc64 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3003,6 +3003,7 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) {  			_go_to_tab(script_list->get_item_metadata(next_tab));  			_update_script_names();  		} +		accept_event();  	}  	if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) {  		if (script_list->get_item_count() > 1) { @@ -3011,12 +3012,15 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) {  			_go_to_tab(script_list->get_item_metadata(next_tab));  			_update_script_names();  		} +		accept_event();  	}  	if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) {  		_menu_option(WINDOW_MOVE_UP); +		accept_event();  	}  	if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) {  		_menu_option(WINDOW_MOVE_DOWN); +		accept_event();  	}  } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index c93b0019dc..3d0c1acff8 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -5383,6 +5383,10 @@ VisualShaderEditor::VisualShaderEditor() {  	add_options.push_back(AddOption("Binormal", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal", "BINORMAL"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));  	add_options.push_back(AddOption("Color", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); +	add_options.push_back(AddOption("Custom0", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom0", "CUSTOM0"), { "custom0" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); +	add_options.push_back(AddOption("Custom1", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom1", "CUSTOM1"), { "custom1" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); +	add_options.push_back(AddOption("Custom2", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom2", "CUSTOM2"), { "custom2" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); +	add_options.push_back(AddOption("Custom3", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom3", "CUSTOM3"), { "custom3" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));  	add_options.push_back(AddOption("InstanceId", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id", "INSTANCE_ID"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));  	add_options.push_back(AddOption("InstanceCustom", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));  	add_options.push_back(AddOption("ModelViewMatrix", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview_matrix", "MODELVIEW_MATRIX"), { "modelview_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL)); diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 40683e2938..5243916bd2 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -338,7 +338,7 @@ void RenameDialog::_bind_methods() {  }  void RenameDialog::_update_substitute() { -	LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner()); +	LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner());  	bool is_main_field = _is_main_field(focus_owner_line_edit);  	but_insert_name->set_disabled(!is_main_field); @@ -636,7 +636,7 @@ bool RenameDialog::_is_main_field(LineEdit *line_edit) {  }  void RenameDialog::_insert_text(String text) { -	LineEdit *focus_owner = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner()); +	LineEdit *focus_owner = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner());  	if (_is_main_field(focus_owner)) {  		focus_owner->selection_delete(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index f91b571118..60d3925cd6 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3217,6 +3217,7 @@ List<Node *> SceneTreeDock::paste_nodes() {  		if (!paste_parent) {  			paste_parent = dup;  			owner = dup; +			dup->set_scene_file_path(String()); // Make sure the scene path is empty, to avoid accidental references.  			ur->add_do_method(EditorNode::get_singleton(), "set_edited_scene", dup);  		} else {  			ur->add_do_method(paste_parent, "add_child", dup, true); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 1fe1561559..40bc2beb23 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -629,6 +629,10 @@ void GDScript::_update_doc() {  		}  	} +	for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) { +		E.value->_update_doc(); +	} +  	_add_doc(doc);  }  #endif diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 9f8d4e4f7e..757e602ebe 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -937,6 +937,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,  				const GDScriptParser::EnumNode *prev_enum = current_enum;  				current_enum = member.m_enum; +				Dictionary dictionary;  				for (int j = 0; j < member.m_enum->values.size(); j++) {  					GDScriptParser::EnumNode::Value &element = member.m_enum->values.write[j]; @@ -960,11 +961,13 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,  					}  					enum_type.enum_values[element.identifier->name] = element.value; +					dictionary[String(element.identifier->name)] = element.value;  				}  				current_enum = prev_enum;  				member.m_enum->set_datatype(enum_type); +				member.m_enum->dictionary = dictionary;  				// Apply annotations.  				for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) { @@ -1748,12 +1751,6 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable  		type = p_variable->initializer->get_datatype(); -#ifdef DEBUG_ENABLED -		if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { -			parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name); -		} -#endif -  		if (p_variable->infer_datatype) {  			type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; @@ -1843,12 +1840,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant  		}  		type = p_constant->initializer->get_datatype(); - -#ifdef DEBUG_ENABLED -		if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) { -			parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name); -		} -#endif  	}  	if (p_constant->datatype_specifier != nullptr) { @@ -2044,6 +2035,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {  				update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));  			}  		} +		if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) { +			push_error("A void function cannot return a value.", p_return); +		}  		result = p_return->return_value->get_datatype();  	} else {  		// Return type is null by default. @@ -2259,30 +2253,26 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig  	}  	p_assignment->set_datatype(op_type); -	if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) { +	if (assignee_type.is_hard_type() && !assignee_type.is_variant() && op_type.is_hard_type()) {  		if (compatible) {  			compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);  			if (!compatible) { -				if (assignee_type.is_hard_type()) { -					// Try reverse test since it can be a masked subtype. -					if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) { -						push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value); -					} else { -						// TODO: Add warning. -						mark_node_unsafe(p_assignment); -						p_assignment->use_conversion_assign = true; -					} +				// Try reverse test since it can be a masked subtype. +				if (!is_type_compatible(op_type, assignee_type, true)) { +					push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);  				} else { -					// TODO: Warning in this case. +					// TODO: Add warning.  					mark_node_unsafe(p_assignment); +					p_assignment->use_conversion_assign = true;  				}  			}  		} else {  			push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);  		} -	} - -	if (assignee_type.has_no_type() || assigned_value_type.is_variant()) { +	} else if (assignee_type.is_hard_type() && !assignee_type.is_variant()) { +		mark_node_unsafe(p_assignment); +		p_assignment->use_conversion_assign = true; +	} else {  		mark_node_unsafe(p_assignment);  		if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {  			p_assignment->use_conversion_assign = true; @@ -2333,9 +2323,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig  	}  #ifdef DEBUG_ENABLED -	if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.is_hard_type() && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) { -		parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name); -	} else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) { +	if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {  		parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);  	}  #endif @@ -2650,6 +2638,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a  		} else if (GDScriptUtilityFunctions::function_exists(function_name)) {  			MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name); +			if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) { +				push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call); +			} +  			if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {  				// Can call on compilation.  				Vector<const Variant *> args; @@ -2693,6 +2685,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a  		} else if (Variant::has_utility_function(function_name)) {  			MethodInfo function_info = info_from_utility_func(function_name); +			if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) { +				push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call); +			} +  			if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {  				// Can call on compilation.  				Vector<const Variant *> args; @@ -2835,6 +2831,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a  			mark_lambda_use_self();  		} +		if (!p_is_root && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) { +			push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", p_call->function_name), p_call); +		} +  #ifdef DEBUG_ENABLED  		if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) {  			parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); @@ -3140,10 +3140,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod  					p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;  					break;  				case GDScriptParser::ClassNode::Member::ENUM: -					if (p_base != nullptr && p_base->is_constant) { -						p_identifier->is_constant = true; -						p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; -					} +					p_identifier->is_constant = true; +					p_identifier->reduced_value = member.m_enum->dictionary; +					p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;  					break;  				case GDScriptParser::ClassNode::Member::VARIABLE:  					p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; @@ -3197,7 +3196,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod  							return;  						case GDScriptParser::ClassNode::Member::ENUM:  							p_identifier->set_datatype(member.get_datatype()); -							p_identifier->is_constant = false; +							p_identifier->is_constant = true; +							p_identifier->reduced_value = member.m_enum->dictionary;  							return;  						case GDScriptParser::ClassNode::Member::CLASS:  							p_identifier->set_datatype(member.get_datatype()); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index cf9e734b05..3c9387a837 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -538,20 +538,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code  				// Construct a built-in type.  				Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name); -				gen->write_construct(result, vtype, arguments); +				gen->write_construct(return_addr, vtype, arguments);  			} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {  				// Variant utility function. -				gen->write_call_utility(result, call->function_name, arguments); +				gen->write_call_utility(return_addr, call->function_name, arguments);  			} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {  				// GDScript utility function. -				gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments); +				gen->write_call_gdscript_utility(return_addr, GDScriptUtilityFunctions::get_function(call->function_name), arguments);  			} else {  				// Regular function.  				const GDScriptParser::ExpressionNode *callee = call->callee;  				if (call->is_super) {  					// Super call. -					gen->write_super_call(result, call->function_name, arguments); +					gen->write_super_call(return_addr, call->function_name, arguments);  				} else {  					if (callee->type == GDScriptParser::Node::IDENTIFIER) {  						// Self function call. @@ -563,22 +563,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code  							if (_have_exact_arguments(method, arguments)) {  								// Exact arguments, use ptrcall. -								gen->write_call_ptrcall(result, self, method, arguments); +								gen->write_call_ptrcall(return_addr, self, method, arguments);  							} else {  								// Not exact arguments, but still can use method bind call. -								gen->write_call_method_bind(result, self, method, arguments); +								gen->write_call_method_bind(return_addr, self, method, arguments);  							}  						} else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {  							GDScriptCodeGenerator::Address self;  							self.mode = GDScriptCodeGenerator::Address::CLASS;  							if (within_await) { -								gen->write_call_async(result, self, call->function_name, arguments); +								gen->write_call_async(return_addr, self, call->function_name, arguments);  							} else {  								gen->write_call(return_addr, self, call->function_name, arguments);  							}  						} else {  							if (within_await) { -								gen->write_call_self_async(result, call->function_name, arguments); +								gen->write_call_self_async(return_addr, call->function_name, arguments);  							} else {  								gen->write_call_self(return_addr, call->function_name, arguments);  							} @@ -589,18 +589,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code  						if (subscript->is_attribute) {  							// May be static built-in method call.  							if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) { -								gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments); +								gen->write_call_builtin_type_static(return_addr, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);  							} else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") &&  									ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {  								// It's a static native method call. -								gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments); +								gen->write_call_native_static(return_addr, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);  							} else {  								GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);  								if (r_error) {  									return GDScriptCodeGenerator::Address();  								}  								if (within_await) { -									gen->write_call_async(result, base, call->function_name, arguments); +									gen->write_call_async(return_addr, base, call->function_name, arguments);  								} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {  									// Native method, use faster path.  									StringName class_name; @@ -613,16 +613,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code  										MethodBind *method = ClassDB::get_method(class_name, call->function_name);  										if (_have_exact_arguments(method, arguments)) {  											// Exact arguments, use ptrcall. -											gen->write_call_ptrcall(result, base, method, arguments); +											gen->write_call_ptrcall(return_addr, base, method, arguments);  										} else {  											// Not exact arguments, but still can use method bind call. -											gen->write_call_method_bind(result, base, method, arguments); +											gen->write_call_method_bind(return_addr, base, method, arguments);  										}  									} else {  										gen->write_call(return_addr, base, call->function_name, arguments);  									}  								} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) { -									gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments); +									gen->write_call_builtin_type(return_addr, base, base.type.builtin_type, call->function_name, arguments);  								} else {  									gen->write_call(return_addr, base, call->function_name, arguments);  								} @@ -2453,26 +2453,20 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri  			case GDScriptParser::ClassNode::Member::ENUM: {  				const GDScriptParser::EnumNode *enum_n = member.m_enum; +				StringName name = enum_n->identifier->name; -				// TODO: Make enums not be just a dictionary? -				Dictionary new_enum; -				for (int j = 0; j < enum_n->values.size(); j++) { -					// Needs to be string because Variant::get will convert to String. -					new_enum[String(enum_n->values[j].identifier->name)] = enum_n->values[j].value; -				} - -				p_script->constants.insert(enum_n->identifier->name, new_enum); +				p_script->constants.insert(name, enum_n->dictionary);  #ifdef TOOLS_ENABLED -				p_script->member_lines[enum_n->identifier->name] = enum_n->start_line; -				p_script->doc_enums[enum_n->identifier->name] = DocData::EnumDoc(); -				p_script->doc_enums[enum_n->identifier->name].name = enum_n->identifier->name; -				p_script->doc_enums[enum_n->identifier->name].description = enum_n->doc_description; +				p_script->member_lines[name] = enum_n->start_line; +				p_script->doc_enums[name] = DocData::EnumDoc(); +				p_script->doc_enums[name].name = name; +				p_script->doc_enums[name].description = enum_n->doc_description;  				for (int j = 0; j < enum_n->values.size(); j++) {  					DocData::ConstantDoc const_doc;  					const_doc.name = enum_n->values[j].identifier->name;  					const_doc.value = Variant(enum_n->values[j].value).operator String();  					const_doc.description = enum_n->values[j].doc_description; -					p_script->doc_enums[enum_n->identifier->name].values.push_back(const_doc); +					p_script->doc_enums[name].values.push_back(const_doc);  				}  #endif  			} break; @@ -2642,10 +2636,6 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:  		}  	} -#ifdef TOOLS_ENABLED -	p_script->_update_doc(); -#endif -  	p_script->_init_rpc_methods_properties();  	p_script->valid = true; @@ -2730,6 +2720,10 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri  		return err;  	} +#ifdef TOOLS_ENABLED +	p_script->_update_doc(); +#endif +  	return GDScriptCache::finish_compiling(main_script->get_path());  } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 540ef1c561..fd2f7b3288 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -483,6 +483,7 @@ public:  		IdentifierNode *identifier = nullptr;  		Vector<Value> values; +		Variant dictionary;  #ifdef TOOLS_ENABLED  		String doc_description;  #endif // TOOLS_ENABLED diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index fdcc0625d7..bc8e33b01a 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1533,8 +1533,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a  				Callable::CallError err;  				if (call_ret) {  					GET_INSTRUCTION_ARG(ret, argc + 1); +#ifdef DEBUG_ENABLED +					Variant::Type base_type = base->get_type(); +					Object *base_obj = base->get_validated_object(); +					StringName base_class = base_obj ? base_obj->get_class_name() : StringName(); +#endif  					base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);  #ifdef DEBUG_ENABLED +					if (ret->get_type() == Variant::NIL) { +						if (base_type == Variant::OBJECT) { +							if (base_obj) { +								MethodBind *method = ClassDB::get_method(base_class, *methodname); +								if (*methodname == CoreStringNames::get_singleton()->_free || (method && !method->has_return())) { +									err_text = R"(Trying to get a return value of a method that returns "void")"; +									OPCODE_BREAK; +								} +							} +						} else if (Variant::has_builtin_method(base_type, *methodname) && !Variant::has_builtin_method_return_value(base_type, *methodname)) { +							err_text = R"(Trying to get a return value of a method that returns "void")"; +							OPCODE_BREAK; +						} +					} +  					if (!call_async && ret->get_type() == Variant::OBJECT) {  						// Check if getting a function state without await.  						bool was_freed = false; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 36bc051643..9ae4bdae56 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -80,10 +80,6 @@ String GDScriptWarning::get_message() const {  		case STANDALONE_EXPRESSION: {  			return "Standalone expression (the line has no effect).";  		} break; -		case VOID_ASSIGNMENT: { -			CHECK_SYMBOLS(1); -			return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; -		} break;  		case NARROWING_CONVERSION: {  			return "Narrowing conversion (float is converted to int and loses precision).";  		} break; @@ -202,7 +198,6 @@ String GDScriptWarning::get_name_from_code(Code p_code) {  		"UNREACHABLE_CODE",  		"UNREACHABLE_PATTERN",  		"STANDALONE_EXPRESSION", -		"VOID_ASSIGNMENT",  		"NARROWING_CONVERSION",  		"INCOMPATIBLE_TERNARY",  		"UNUSED_SIGNAL", diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 7e4e975510..df6ee71425 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -57,7 +57,6 @@ public:  		UNREACHABLE_CODE, // Code after a return statement.  		UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).  		STANDALONE_EXPRESSION, // Expression not assigned to a variable. -		VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable.  		NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.  		INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible.  		UNUSED_SIGNAL, // Signal is defined but never emitted. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd new file mode 100644 index 0000000000..63587942f7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd @@ -0,0 +1,2 @@ +func test() -> void: +  return null diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out new file mode 100644 index 0000000000..3c09f44ba9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +A void function cannot return a value. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd new file mode 100644 index 0000000000..0ee4e7ea36 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd @@ -0,0 +1,4 @@ +func test() -> void: +  var a +  a = 1 +  return a diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out new file mode 100644 index 0000000000..3c09f44ba9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +A void function cannot return a value. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd new file mode 100644 index 0000000000..a3450966cc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd @@ -0,0 +1,3 @@ +func test(): +	var builtin := [] +	print(builtin.reverse()) # Built-in type method. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out new file mode 100644 index 0000000000..225c85e9c7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "reverse()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd new file mode 100644 index 0000000000..2162a181ac --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd @@ -0,0 +1,5 @@ +func foo() -> void: +	pass + +func test(): +	print(foo()) # Custom method. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out new file mode 100644 index 0000000000..2b1a607883 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "foo()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd new file mode 100644 index 0000000000..f3443d985e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd @@ -0,0 +1,2 @@ +func test(): +	print(print_debug()) # GDScript utility function. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out new file mode 100644 index 0000000000..502c18ab9d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "print_debug()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd new file mode 100644 index 0000000000..b8e81b160a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd @@ -0,0 +1,3 @@ +func test(): +	var obj := Node.new() +	print(obj.free()) # Native type method. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out new file mode 100644 index 0000000000..88be39345b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "free()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd new file mode 100644 index 0000000000..8eabed4271 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd @@ -0,0 +1,2 @@ +func test(): +	print(print()) # Built-in utility function. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out new file mode 100644 index 0000000000..ebf43186be --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot get return value of call to "print()" because it returns "void". diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd new file mode 100644 index 0000000000..2ce588373b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd @@ -0,0 +1,29 @@ +class Outer: +	enum OuterEnum { OuterValue = 3 } +	const OuterConst := OuterEnum + +	class Inner: +		enum InnerEnum { InnerValue = 7 } +		const InnerConst := InnerEnum + +		static func test() -> void: +			print(OuterEnum.size()); +			print(OuterEnum.OuterValue); +			print(OuterConst.size()); +			print(OuterConst.OuterValue); +			print(Outer.OuterEnum.size()); +			print(Outer.OuterEnum.OuterValue); +			print(Outer.OuterConst.size()); +			print(Outer.OuterConst.OuterValue); + +			print(InnerEnum.size()); +			print(InnerEnum.InnerValue); +			print(InnerConst.size()); +			print(InnerConst.InnerValue); +			print(Inner.InnerEnum.size()); +			print(Inner.InnerEnum.InnerValue); +			print(Inner.InnerConst.size()); +			print(Inner.InnerConst.InnerValue); + +func test(): +	Outer.Inner.test() diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out new file mode 100644 index 0000000000..e049f85b6e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out @@ -0,0 +1,17 @@ +GDTEST_OK +1 +3 +1 +3 +1 +3 +1 +3 +1 +7 +1 +7 +1 +7 +1 +7 diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd new file mode 100644 index 0000000000..c9caef7d7c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd @@ -0,0 +1,5 @@ +func variant() -> Variant: +  return 'variant' + +func test(): +  print(variant()) diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out new file mode 100644 index 0000000000..57fe608cc5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out @@ -0,0 +1,2 @@ +GDTEST_OK +variant diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd deleted file mode 100644 index b4a42b3e3d..0000000000 --- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd +++ /dev/null @@ -1,6 +0,0 @@ -func i_return_void() -> void: -	return - - -func test(): -	var __ = i_return_void() diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out deleted file mode 100644 index 84c9598f9a..0000000000 --- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out +++ /dev/null @@ -1,5 +0,0 @@ -GDTEST_OK ->> WARNING ->> Line: 6 ->> VOID_ASSIGNMENT ->> Assignment operation, but the function 'i_return_void()' returns void. diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd new file mode 100644 index 0000000000..a3daf70627 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd @@ -0,0 +1,4 @@ +func test(): +	var obj +	obj = Node.new() +	print(obj.free()) diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out new file mode 100644 index 0000000000..5edaf19442 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/use_return_value_of_free_call.gd +>> 4 +>> Trying to get a return value of a method that returns "void" diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd new file mode 100644 index 0000000000..49fb76ad1f --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd @@ -0,0 +1,4 @@ +func test(): +	var value +	value = [] +	print(value.reverse()) diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out new file mode 100644 index 0000000000..128356ff8a --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/use_return_value_of_void_builtin_method_call.gd +>> 4 +>> Trying to get a return value of a method that returns "void" diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd new file mode 100644 index 0000000000..44f9aa467a --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd @@ -0,0 +1,4 @@ +func test(): +	var obj +	obj = RefCounted.new() +	print(obj.notify_property_list_changed()) diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out new file mode 100644 index 0000000000..e02c206778 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/use_return_value_of_void_native_method_call.gd +>> 4 +>> Trying to get a return value of a method that returns "void" diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd index 5303fb04e2..9b64084fa6 100644 --- a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd +++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd @@ -15,10 +15,10 @@ func test():  	var string_array: Array[String] = []  	var stringname_array: Array[StringName] = [] -	assert(!string_array.push_back(&"abc")) +	string_array.push_back(&"abc")  	print("Array[String] insert converted: ", typeof(string_array[0]) == TYPE_STRING) -	assert(!stringname_array.push_back("abc")) +	stringname_array.push_back("abc")  	print("Array[StringName] insert converted: ", typeof(stringname_array[0]) == TYPE_STRING_NAME)  	print("StringName in Array[String]: ", &"abc" in string_array) @@ -28,8 +28,8 @@ func test():  	assert(!packed_string_array.push_back("abc"))  	print("StringName in PackedStringArray: ", &"abc" in packed_string_array) -	assert(!string_array.push_back("abc")) +	string_array.push_back("abc")  	print("StringName finds String in Array: ", string_array.find(&"abc")) -	assert(!stringname_array.push_back(&"abc")) +	stringname_array.push_back(&"abc")  	print("String finds StringName in Array: ", stringname_array.find("abc")) diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd new file mode 100644 index 0000000000..22e54cf91c --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd @@ -0,0 +1,9 @@ +func test(): +	var x: int = 2 +	var y = 3.14 +	var z := 2.72 +	print(typeof(x)) +	x = y +	print(typeof(x)) +	x = z +	print(typeof(x)) diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out new file mode 100644 index 0000000000..4a268dd8e0 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out @@ -0,0 +1,12 @@ +GDTEST_OK +>> WARNING +>> Line: 6 +>> NARROWING_CONVERSION +>> Narrowing conversion (float is converted to int and loses precision). +>> WARNING +>> Line: 8 +>> NARROWING_CONVERSION +>> Narrowing conversion (float is converted to int and loses precision). +2 +2 +2 diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 89ef9ddd90..186e6a504f 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1405,6 +1405,7 @@ GridMapEditor::GridMapEditor() {  }  GridMapEditor::~GridMapEditor() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	_clear_clipboard_data();  	for (int i = 0; i < 3; i++) { diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index e396d77d86..d23d68ffba 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -796,6 +796,10 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {  }  void GridMap::_octant_exit_world(const OctantKey &p_key) { +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +	ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); +	ERR_FAIL_NULL(NavigationServer3D::get_singleton()); +  	ERR_FAIL_COND(!octant_map.has(p_key));  	Octant &g = *octant_map[p_key];  	PhysicsServer3D::get_singleton()->body_set_state(g.static_body, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform()); @@ -834,6 +838,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {  }  void GridMap::_octant_clean_up(const OctantKey &p_key) { +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +	ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); +	ERR_FAIL_NULL(NavigationServer3D::get_singleton()); +  	ERR_FAIL_COND(!octant_map.has(p_key));  	Octant &g = *octant_map[p_key]; @@ -1186,6 +1194,7 @@ Vector3 GridMap::_get_offset() const {  }  void GridMap::clear_baked_meshes() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	for (int i = 0; i < baked_meshes.size(); i++) {  		RS::get_singleton()->free(baked_meshes[i].instance);  	} diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 8c785d7591..1b6f1d7897 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -40,6 +40,7 @@ NoiseTexture2D::NoiseTexture2D() {  }  NoiseTexture2D::~NoiseTexture2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (texture.is_valid()) {  		RS::get_singleton()->free(texture);  	} diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 844b15e9fb..747dcbd76c 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -339,9 +339,6 @@ def configure(env: "Environment"):      env.Prepend(CPPPATH=["#platform/linuxbsd"])      if env["x11"]: -        if not env["vulkan"]: -            print("Error: X11 support requires vulkan=yes") -            env.Exit(255)          env.Append(CPPDEFINES=["X11_ENABLED"])      env.Append(CPPDEFINES=["UNIX_ENABLED"]) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e7864ebac0..f462893112 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -3709,7 +3709,6 @@ WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;  // UXTheme API.  bool DisplayServerWindows::dark_title_available = false;  bool DisplayServerWindows::ux_theme_available = false; -IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr;  ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;  GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;  GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr; @@ -3727,7 +3726,7 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {  } SHC_PROCESS_DPI_AWARENESS;  bool DisplayServerWindows::is_dark_mode_supported() const { -	return ux_theme_available && IsDarkModeAllowedForApp(); +	return ux_theme_available;  }  bool DisplayServerWindows::is_dark_mode() const { @@ -3817,13 +3816,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win  	// Load UXTheme.  	HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");  	if (ux_theme_lib) { -		IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));  		ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));  		GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));  		GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));  		GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98)); -		ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference; +		ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;  		if (os_ver.dwBuildNumber >= 22000) {  			dark_title_available = true;  		} diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 4702bb7765..82894d300f 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -152,7 +152,6 @@ typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output);  typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets);  typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable); -typedef bool(WINAPI *IsDarkModeAllowedForAppPtr)();  typedef bool(WINAPI *ShouldAppsUseDarkModePtr)();  typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode);  typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name); @@ -288,7 +287,6 @@ class DisplayServerWindows : public DisplayServer {  	// UXTheme API  	static bool dark_title_available;  	static bool ux_theme_available; -	static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp;  	static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode;  	static GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx;  	static GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName; diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index df75d34e0f..fccaf63319 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -661,5 +661,6 @@ CollisionObject2D::CollisionObject2D() {  }  CollisionObject2D::~CollisionObject2D() { +	ERR_FAIL_NULL(PhysicsServer2D::get_singleton());  	PhysicsServer2D::get_singleton()->free(rid);  } diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index c6296f4732..68dc85486d 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -1507,6 +1507,7 @@ CPUParticles2D::CPUParticles2D() {  }  CPUParticles2D::~CPUParticles2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(multimesh);  	RS::get_singleton()->free(mesh);  } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index ccbc080768..95e24028a6 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -688,6 +688,7 @@ GPUParticles2D::GPUParticles2D() {  }  GPUParticles2D::~GPUParticles2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(particles);  	RS::get_singleton()->free(mesh);  } diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp index 3ec744ff8e..9e12c7aa3b 100644 --- a/scene/2d/joint_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -246,6 +246,7 @@ Joint2D::Joint2D() {  }  Joint2D::~Joint2D() { +	ERR_FAIL_NULL(PhysicsServer2D::get_singleton());  	PhysicsServer2D::get_singleton()->free(joint);  } diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 78b5199358..e07393a9c7 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -321,6 +321,7 @@ Light2D::Light2D() {  }  Light2D::~Light2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(canvas_light);  } diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 67e82140e4..e7997c6eff 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -148,6 +148,7 @@ OccluderPolygon2D::OccluderPolygon2D() {  }  OccluderPolygon2D::~OccluderPolygon2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(occ_polygon);  } @@ -291,5 +292,7 @@ LightOccluder2D::LightOccluder2D() {  }  LightOccluder2D::~LightOccluder2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +  	RS::get_singleton()->free(occluder);  } diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 62afe0d89b..4455142a6c 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -197,6 +197,7 @@ NavigationAgent2D::NavigationAgent2D() {  }  NavigationAgent2D::~NavigationAgent2D() { +	ERR_FAIL_NULL(NavigationServer2D::get_singleton());  	NavigationServer2D::get_singleton()->free(agent);  	agent = RID(); // Pointless  } diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp index d639e1cc89..34005dbd9e 100644 --- a/scene/2d/navigation_link_2d.cpp +++ b/scene/2d/navigation_link_2d.cpp @@ -285,6 +285,7 @@ NavigationLink2D::NavigationLink2D() {  }  NavigationLink2D::~NavigationLink2D() { +	ERR_FAIL_NULL(NavigationServer2D::get_singleton());  	NavigationServer2D::get_singleton()->free(link);  	link = RID();  } diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index cbec4db4c5..4d9d46fb06 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -116,6 +116,7 @@ NavigationObstacle2D::NavigationObstacle2D() {  }  NavigationObstacle2D::~NavigationObstacle2D() { +	ERR_FAIL_NULL(NavigationServer2D::get_singleton());  	NavigationServer2D::get_singleton()->free(agent);  	agent = RID(); // Pointless  } diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 675ef7c780..78c65256e8 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -343,6 +343,7 @@ NavigationRegion2D::NavigationRegion2D() {  }  NavigationRegion2D::~NavigationRegion2D() { +	ERR_FAIL_NULL(NavigationServer2D::get_singleton());  	NavigationServer2D::get_singleton()->free(region);  #ifdef DEBUG_ENABLED diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index e41664b006..5b00a3a2dc 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -664,6 +664,7 @@ Polygon2D::Polygon2D() {  Polygon2D::~Polygon2D() {  	// This will free the internally-allocated mesh instance, if allocated. +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());  	RS::get_singleton()->free(mesh);  } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 62787d4488..fa0358fabc 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -529,6 +529,7 @@ Bone2D::Bone2D() {  Bone2D::~Bone2D() {  #ifdef TOOLS_ENABLED  	if (!editor_gizmo_rid.is_null()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RenderingServer::get_singleton()->free(editor_gizmo_rid);  	}  #endif // TOOLS_ENABLED @@ -803,5 +804,6 @@ Skeleton2D::Skeleton2D() {  }  Skeleton2D::~Skeleton2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(skeleton);  } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 3136752a2e..f52dbd1c53 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1108,6 +1108,7 @@ void TileMap::_rendering_update_layer(int p_layer) {  void TileMap::_rendering_cleanup_layer(int p_layer) {  	ERR_FAIL_INDEX(p_layer, (int)layers.size()); +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer *rs = RenderingServer::get_singleton();  	if (layers[p_layer].canvas_item.is_valid()) {  		rs->free(layers[p_layer].canvas_item); @@ -1280,6 +1281,7 @@ void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) {  }  void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	// Free the canvas items.  	for (const RID &ci : p_quadrant->canvas_items) {  		RenderingServer::get_singleton()->free(ci); @@ -1596,6 +1598,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r  void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) {  	// Remove a quadrant. +	ERR_FAIL_NULL(PhysicsServer2D::get_singleton());  	for (RID body : p_quadrant->bodies) {  		bodies_coords.erase(body);  		PhysicsServer2D::get_singleton()->free(body); @@ -1754,6 +1757,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List  void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) {  	// Clear navigation shapes in the quadrant. +	ERR_FAIL_NULL(NavigationServer2D::get_singleton());  	for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) {  		for (int i = 0; i < E.value.size(); i++) {  			RID region = E.value[i]; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 2a50575749..03c24e63ef 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -744,8 +744,10 @@ Camera3D::Camera3D() {  }  Camera3D::~Camera3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(camera);  	if (pyramid_shape.is_valid()) { +		ERR_FAIL_NULL(PhysicsServer3D::get_singleton());  		PhysicsServer3D::get_singleton()->free(pyramid_shape);  	}  } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 66546092f2..1b94ff822c 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -352,6 +352,8 @@ void CollisionObject3D::_shape_changed(const Ref<Shape3D> &p_shape) {  }  void CollisionObject3D::_update_debug_shapes() { +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +  	if (!is_inside_tree()) {  		debug_shapes_to_update.clear();  		return; @@ -393,6 +395,8 @@ void CollisionObject3D::_update_debug_shapes() {  }  void CollisionObject3D::_clear_debug_shapes() { +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +  	for (KeyValue<uint32_t, ShapeData> &E : shapes) {  		ShapeData &shapedata = E.value;  		ShapeData::ShapeBase *shape_bases = shapedata.shapes.ptrw(); @@ -627,6 +631,7 @@ int CollisionObject3D::shape_owner_get_shape_index(uint32_t p_owner, int p_shape  }  void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	ERR_FAIL_COND(!shapes.has(p_owner));  	ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size()); @@ -722,5 +727,6 @@ CollisionObject3D::CollisionObject3D() {  }  CollisionObject3D::~CollisionObject3D() { +	ERR_FAIL_NULL(PhysicsServer3D::get_singleton());  	PhysicsServer3D::get_singleton()->free(rid);  } diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 5ac8535bb6..6d47375589 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -1701,5 +1701,6 @@ CPUParticles3D::CPUParticles3D() {  }  CPUParticles3D::~CPUParticles3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(multimesh);  } diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index fc442986a8..ee1086fb88 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -254,5 +254,6 @@ Decal::Decal() {  }  Decal::~Decal() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(decal);  } diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 4606e70310..d9912c47b1 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -118,5 +118,6 @@ FogVolume::FogVolume() {  }  FogVolume::~FogVolume() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(volume);  } diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 17dfe2610e..6302a9e1b8 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -634,5 +634,6 @@ GPUParticles3D::GPUParticles3D() {  }  GPUParticles3D::~GPUParticles3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(particles);  } diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 476820b1c4..f6546b2d1f 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -58,6 +58,7 @@ GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_ty  }  GPUParticlesCollision3D::~GPUParticlesCollision3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(collision);  } @@ -819,6 +820,7 @@ GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_ty  	set_base(collision);  }  GPUParticlesAttractor3D::~GPUParticlesAttractor3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(collision);  } diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp index 1a18f43e7b..d86c156d99 100644 --- a/scene/3d/joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -234,6 +234,7 @@ Joint3D::Joint3D() {  }  Joint3D::~Joint3D() { +	ERR_FAIL_NULL(PhysicsServer3D::get_singleton());  	PhysicsServer3D::get_singleton()->free(joint);  } diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 262dc0db37..d3020fc5b7 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -966,6 +966,7 @@ Label3D::~Label3D() {  	TS->free_rid(text_rid); +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(mesh);  	for (KeyValue<SurfaceKey, SurfaceData> E : surfaces) {  		RenderingServer::get_singleton()->free(E.value.material); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 3f43e718b8..23a08f0292 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -476,6 +476,7 @@ Light3D::Light3D() {  }  Light3D::~Light3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->instance_set_base(get_instance(), RID());  	if (light.is_valid()) { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 1b5427c80d..108bbe072e 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -313,6 +313,7 @@ LightmapGIData::LightmapGIData() {  }  LightmapGIData::~LightmapGIData() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(lightmap);  } diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 741f7397ad..3acb33f42a 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -204,6 +204,7 @@ NavigationAgent3D::NavigationAgent3D() {  }  NavigationAgent3D::~NavigationAgent3D() { +	ERR_FAIL_NULL(NavigationServer3D::get_singleton());  	NavigationServer3D::get_singleton()->free(agent);  	agent = RID(); // Pointless  } diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp index bee7c7f39b..009abfc8fd 100644 --- a/scene/3d/navigation_link_3d.cpp +++ b/scene/3d/navigation_link_3d.cpp @@ -227,10 +227,12 @@ NavigationLink3D::NavigationLink3D() {  }  NavigationLink3D::~NavigationLink3D() { +	ERR_FAIL_NULL(NavigationServer3D::get_singleton());  	NavigationServer3D::get_singleton()->free(link);  	link = RID();  #ifdef DEBUG_ENABLED +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (debug_instance.is_valid()) {  		RenderingServer::get_singleton()->free(debug_instance);  	} diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index f241d65649..6431bd2b77 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -122,6 +122,7 @@ NavigationObstacle3D::NavigationObstacle3D() {  }  NavigationObstacle3D::~NavigationObstacle3D() { +	ERR_FAIL_NULL(NavigationServer3D::get_singleton());  	NavigationServer3D::get_singleton()->free(agent);  	agent = RID(); // Pointless  } diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 86b78f847e..0e0233c0cd 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -375,12 +375,15 @@ NavigationRegion3D::~NavigationRegion3D() {  	if (navigation_mesh.is_valid()) {  		navigation_mesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed));  	} +	ERR_FAIL_NULL(NavigationServer3D::get_singleton());  	NavigationServer3D::get_singleton()->free(region);  #ifdef DEBUG_ENABLED  	NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));  	NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));  	NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh)); + +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (debug_instance.is_valid()) {  		RenderingServer::get_singleton()->free(debug_instance);  	} diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 4e1ed5654a..7166d99b92 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -32,6 +32,7 @@  #include "core/config/project_settings.h"  #include "core/core_string_names.h" +#include "core/io/marshalls.h"  #include "core/math/geometry_2d.h"  #include "core/math/triangulate.h"  #include "scene/3d/importer_mesh_instance_3d.h" @@ -138,6 +139,7 @@ Occluder3D::Occluder3D() {  Occluder3D::~Occluder3D() {  	if (occluder.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(occluder);  	}  } @@ -533,11 +535,20 @@ void OccluderInstance3D::_bake_surface(const Transform3D &p_transform, Array p_s  	}  	if (!Math::is_zero_approx(p_simplification_dist) && SurfaceTool::simplify_func) { -		float error_scale = SurfaceTool::simplify_scale_func((float *)vertices.ptr(), vertices.size(), sizeof(Vector3)); +		Vector<float> vertices_f32 = vector3_to_float32_array(vertices.ptr(), vertices.size()); + +		float error_scale = SurfaceTool::simplify_scale_func(vertices_f32.ptr(), vertices.size(), sizeof(float) * 3);  		float target_error = p_simplification_dist / error_scale;  		float error = -1.0f;  		int target_index_count = MIN(indices.size(), 36); -		uint32_t index_count = SurfaceTool::simplify_func((unsigned int *)indices.ptrw(), (unsigned int *)indices.ptr(), indices.size(), (float *)vertices.ptr(), vertices.size(), sizeof(Vector3), target_index_count, target_error, &error); + +		uint32_t index_count = SurfaceTool::simplify_func( +				(unsigned int *)indices.ptrw(), +				(unsigned int *)indices.ptr(), +				indices.size(), +				vertices_f32.ptr(), vertices.size(), sizeof(float) * 3, +				target_index_count, target_error, &error); +  		indices.resize(index_count);  	} diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 02ab297d8e..ea0cfce987 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -40,6 +40,7 @@ Path3D::Path3D() {  }  Path3D::~Path3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (debug_instance.is_valid()) {  		RS::get_singleton()->free(debug_instance);  	} diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index d29f337fc8..93d3039494 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -3358,6 +3358,7 @@ PhysicalBone3D::~PhysicalBone3D() {  	if (joint_data) {  		memdelete(joint_data);  	} +	ERR_FAIL_NULL(PhysicsServer3D::get_singleton());  	PhysicsServer3D::get_singleton()->free(joint);  } diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index bc3cc31963..f8164ee1aa 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -257,5 +257,6 @@ ReflectionProbe::ReflectionProbe() {  }  ReflectionProbe::~ReflectionProbe() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(probe);  } diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index b205c2cde0..27310e7cee 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -58,10 +58,10 @@ Ref<Skin> SkinReference::get_skin() const {  }  SkinReference::~SkinReference() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (skeleton_node) {  		skeleton_node->skin_bindings.erase(this);  	} -  	RS::get_singleton()->free(skeleton);  } diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 1814baa9e9..31d3f1aeb7 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -704,6 +704,7 @@ SoftBody3D::SoftBody3D() :  SoftBody3D::~SoftBody3D() {  	memdelete(rendering_server_handler); +	ERR_FAIL_NULL(PhysicsServer3D::get_singleton());  	PhysicsServer3D::get_singleton()->free(physics_rid);  } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index e488ff3059..3629464cd1 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -611,6 +611,7 @@ SpriteBase3D::SpriteBase3D() {  }  SpriteBase3D::~SpriteBase3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(mesh);  	RenderingServer::get_singleton()->free(material);  } diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp index 6d4c18a69e..70c9321338 100644 --- a/scene/3d/visible_on_screen_notifier_3d.cpp +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -99,6 +99,7 @@ VisibleOnScreenNotifier3D::VisibleOnScreenNotifier3D() {  VisibleOnScreenNotifier3D::~VisibleOnScreenNotifier3D() {  	RID base_old = get_base();  	set_base(RID()); +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(base_old);  } diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 3b21f5c03a..07ca63dcd6 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -157,6 +157,7 @@ VisualInstance3D::VisualInstance3D() {  }  VisualInstance3D::~VisualInstance3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(instance);  } diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 7caf2f4874..2cb15715c6 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -242,6 +242,7 @@ VoxelGIData::VoxelGIData() {  }  VoxelGIData::~VoxelGIData() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(probe);  } @@ -516,5 +517,6 @@ VoxelGI::VoxelGI() {  }  VoxelGI::~VoxelGI() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(voxel_gi);  } diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index f87829cf71..beaffc14b0 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -154,7 +154,9 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {  			queue_redraw();  		} -		_update_hover(); +		if (!tabs.is_empty()) { +			_update_hover(); +		}  		return;  	} @@ -362,12 +364,19 @@ void TabBar::_notification(int p_what) {  		} break;  		case NOTIFICATION_DRAW: { +			bool rtl = is_layout_rtl(); +			Vector2 size = get_size(); +  			if (tabs.is_empty()) { +				// Draw the drop indicator where the first tab would be if there are no tabs. +				if (dragging_valid_tab) { +					int x = rtl ? size.x : 0; +					theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - (theme_cache.drop_mark_icon->get_width() / 2), (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color); +				} +  				return;  			} -			bool rtl = is_layout_rtl(); -			Vector2 size = get_size();  			int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();  			int ofs = tabs[offset].ofs_cache; @@ -1092,7 +1101,8 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {  					hover_now += 1;  				}  			} else { -				hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1; +				int x = tabs.is_empty() ? 0 : get_tab_rect(0).position.x; +				hover_now = is_layout_rtl() ^ (p_point.x < x) ? 0 : get_tab_count() - 1;  			}  			move_tab(tab_from_id, hover_now); @@ -1118,7 +1128,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {  						hover_now += 1;  					}  				} else { -					hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count(); +					hover_now = tabs.is_empty() || (is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x)) ? 0 : get_tab_count();  				}  				Tab moving_tab = from_tabs->tabs[tab_from_id]; @@ -1154,10 +1164,13 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {  int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {  	int hover_now = -1; -	for (int i = offset; i <= max_drawn_tab; i++) { -		Rect2 rect = get_tab_rect(i); -		if (rect.has_point(p_point)) { -			hover_now = i; + +	if (!tabs.is_empty()) { +		for (int i = offset; i <= max_drawn_tab; i++) { +			Rect2 rect = get_tab_rect(i); +			if (rect.has_point(p_point)) { +				hover_now = i; +			}  		}  	} diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index f3812eb497..404c289a6a 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -1305,6 +1305,7 @@ CanvasItem::CanvasItem() :  }  CanvasItem::~CanvasItem() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(canvas_item);  } @@ -1459,5 +1460,6 @@ CanvasTexture::CanvasTexture() {  	canvas_texture = RS::get_singleton()->canvas_texture_create();  }  CanvasTexture::~CanvasTexture() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(canvas_texture);  } diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 97b784e9d0..2aa8df10ab 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -360,5 +360,6 @@ CanvasLayer::CanvasLayer() {  }  CanvasLayer::~CanvasLayer() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(canvas);  } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index afc31ae480..c0b81c9b9a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -80,6 +80,7 @@ void ViewportTexture::setup_local_to_scene() {  	vp->viewport_textures.insert(this); +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (proxy_ph.is_valid()) {  		RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid);  		RS::get_singleton()->free(proxy_ph); @@ -153,6 +154,8 @@ ViewportTexture::~ViewportTexture() {  		vp->viewport_textures.erase(this);  	} +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +  	if (proxy_ph.is_valid()) {  		RS::get_singleton()->free(proxy_ph);  	} @@ -301,6 +304,8 @@ void Viewport::_sub_window_remove(Window *p_window) {  	int index = _sub_window_find(p_window);  	ERR_FAIL_COND(index == -1); +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +  	RS::get_singleton()->free(gui.sub_windows[index].canvas_item);  	gui.sub_windows.remove_at(index); @@ -4117,6 +4122,7 @@ Viewport::~Viewport() {  	for (ViewportTexture *E : viewport_textures) {  		E->vp = nullptr;  	} +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(viewport);  } diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 8e4876e01f..292accddc1 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -135,6 +135,7 @@ CameraAttributes::CameraAttributes() {  }  CameraAttributes::~CameraAttributes() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(camera_attributes);  } diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index b16059c218..0cc5faffb1 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -294,6 +294,8 @@ CanvasItemMaterial::CanvasItemMaterial() :  CanvasItemMaterial::~CanvasItemMaterial() {  	MutexLock lock(material_mutex); +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +  	if (shader_map.has(current_key)) {  		shader_map[current_key].users--;  		if (shader_map[current_key].users == 0) { diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 23bd8a4be4..f97cffc3fc 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1550,5 +1550,6 @@ Environment::Environment() {  }  Environment::~Environment() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(environment);  } diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp index 46b44d681f..2aca552716 100644 --- a/scene/resources/fog_material.cpp +++ b/scene/resources/fog_material.cpp @@ -132,6 +132,7 @@ void FogMaterial::_bind_methods() {  void FogMaterial::cleanup_shader() {  	if (shader.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(shader);  	}  } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 584a7e7eac..78b4edfcf7 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -270,24 +270,18 @@ void Font::set_cache_capacity(int p_single_line, int p_multi_line) {  }  Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { -	uint64_t hash = p_text.hash64(); -	hash = hash_djb2_one_64(p_font_size, hash); -	if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { -		hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); -		hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); -	} -	hash = hash_djb2_one_64(p_direction, hash); -	hash = hash_djb2_one_64(p_orientation, hash); +	bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL); +	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);  	Ref<TextLine> buffer; -	if (cache.has(hash)) { -		buffer = cache.get(hash); +	if (cache.has(key)) { +		buffer = cache.get(key);  	} else {  		buffer.instantiate();  		buffer->set_direction(p_direction);  		buffer->set_orientation(p_orientation);  		buffer->add_string(p_text, Ref<Font>(this), p_font_size); -		cache.insert(hash, buffer); +		cache.insert(key, buffer);  	}  	buffer->set_width(p_width); @@ -300,17 +294,11 @@ Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignmen  }  Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { -	uint64_t hash = p_text.hash64(); -	hash = hash_djb2_one_64(p_font_size, hash); -	hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); -	hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); -	hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); -	hash = hash_djb2_one_64(p_direction, hash); -	hash = hash_djb2_one_64(p_orientation, hash); +	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);  	Ref<TextParagraph> lines_buffer; -	if (cache_wrap.has(hash)) { -		lines_buffer = cache_wrap.get(hash); +	if (cache_wrap.has(key)) { +		lines_buffer = cache_wrap.get(key);  	} else {  		lines_buffer.instantiate();  		lines_buffer->set_direction(p_direction); @@ -319,7 +307,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment  		lines_buffer->set_width(p_width);  		lines_buffer->set_break_flags(p_brk_flags);  		lines_buffer->set_justification_flags(p_jst_flags); -		cache_wrap.insert(hash, lines_buffer); +		cache_wrap.insert(key, lines_buffer);  	}  	lines_buffer->set_alignment(p_alignment); @@ -329,24 +317,18 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment  }  void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { -	uint64_t hash = p_text.hash64(); -	hash = hash_djb2_one_64(p_font_size, hash); -	if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { -		hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); -		hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); -	} -	hash = hash_djb2_one_64(p_direction, hash); -	hash = hash_djb2_one_64(p_orientation, hash); +	bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL); +	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);  	Ref<TextLine> buffer; -	if (cache.has(hash)) { -		buffer = cache.get(hash); +	if (cache.has(key)) { +		buffer = cache.get(key);  	} else {  		buffer.instantiate();  		buffer->set_direction(p_direction);  		buffer->set_orientation(p_orientation);  		buffer->add_string(p_text, Ref<Font>(this), p_font_size); -		cache.insert(hash, buffer); +		cache.insert(key, buffer);  	}  	Vector2 ofs = p_pos; @@ -366,17 +348,11 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t  }  void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { -	uint64_t hash = p_text.hash64(); -	hash = hash_djb2_one_64(p_font_size, hash); -	hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); -	hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); -	hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); -	hash = hash_djb2_one_64(p_direction, hash); -	hash = hash_djb2_one_64(p_orientation, hash); +	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);  	Ref<TextParagraph> lines_buffer; -	if (cache_wrap.has(hash)) { -		lines_buffer = cache_wrap.get(hash); +	if (cache_wrap.has(key)) { +		lines_buffer = cache_wrap.get(key);  	} else {  		lines_buffer.instantiate();  		lines_buffer->set_direction(p_direction); @@ -385,7 +361,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S  		lines_buffer->set_width(p_width);  		lines_buffer->set_break_flags(p_brk_flags);  		lines_buffer->set_justification_flags(p_jst_flags); -		cache_wrap.insert(hash, lines_buffer); +		cache_wrap.insert(key, lines_buffer);  	}  	Vector2 ofs = p_pos; @@ -402,24 +378,18 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S  }  void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { -	uint64_t hash = p_text.hash64(); -	hash = hash_djb2_one_64(p_font_size, hash); -	if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { -		hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); -		hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); -	} -	hash = hash_djb2_one_64(p_direction, hash); -	hash = hash_djb2_one_64(p_orientation, hash); +	bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL); +	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);  	Ref<TextLine> buffer; -	if (cache.has(hash)) { -		buffer = cache.get(hash); +	if (cache.has(key)) { +		buffer = cache.get(key);  	} else {  		buffer.instantiate();  		buffer->set_direction(p_direction);  		buffer->set_orientation(p_orientation);  		buffer->add_string(p_text, Ref<Font>(this), p_font_size); -		cache.insert(hash, buffer); +		cache.insert(key, buffer);  	}  	Vector2 ofs = p_pos; @@ -439,17 +409,11 @@ void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const Str  }  void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { -	uint64_t hash = p_text.hash64(); -	hash = hash_djb2_one_64(p_font_size, hash); -	hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); -	hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash); -	hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash); -	hash = hash_djb2_one_64(p_direction, hash); -	hash = hash_djb2_one_64(p_orientation, hash); +	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);  	Ref<TextParagraph> lines_buffer; -	if (cache_wrap.has(hash)) { -		lines_buffer = cache_wrap.get(hash); +	if (cache_wrap.has(key)) { +		lines_buffer = cache_wrap.get(key);  	} else {  		lines_buffer.instantiate();  		lines_buffer->set_direction(p_direction); @@ -458,7 +422,7 @@ void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos,  		lines_buffer->set_width(p_width);  		lines_buffer->set_break_flags(p_brk_flags);  		lines_buffer->set_justification_flags(p_jst_flags); -		cache_wrap.insert(hash, lines_buffer); +		cache_wrap.insert(key, lines_buffer);  	}  	Vector2 ofs = p_pos; diff --git a/scene/resources/font.h b/scene/resources/font.h index e9f7507652..44198a3111 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -47,9 +47,44 @@ class TextParagraph;  class Font : public Resource {  	GDCLASS(Font, Resource); +	struct ShapedTextKey { +		String text; +		int font_size = 14; +		float width = 0.f; +		BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_NONE; +		BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY; +		TextServer::Direction direction = TextServer::DIRECTION_AUTO; +		TextServer::Orientation orientation = TextServer::ORIENTATION_HORIZONTAL; + +		bool operator==(const ShapedTextKey &p_b) const { +			return (font_size == p_b.font_size) && (width == p_b.width) && (jst_flags == p_b.jst_flags) && (brk_flags == p_b.brk_flags) && (direction == p_b.direction) && (orientation == p_b.orientation) && (text == p_b.text); +		} + +		ShapedTextKey() {} +		ShapedTextKey(const String &p_text, int p_font_size, float p_width, BitField<TextServer::JustificationFlag> p_jst_flags, BitField<TextServer::LineBreakFlag> p_brk_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { +			text = p_text; +			font_size = p_font_size; +			width = p_width; +			jst_flags = p_jst_flags; +			brk_flags = p_brk_flags; +			direction = p_direction; +			orientation = p_orientation; +		} +	}; + +	struct ShapedTextKeyHasher { +		_FORCE_INLINE_ static uint32_t hash(const ShapedTextKey &p_a) { +			uint32_t hash = p_a.text.hash(); +			hash = hash_murmur3_one_32(p_a.font_size, hash); +			hash = hash_murmur3_one_float(p_a.width, hash); +			hash = hash_murmur3_one_32(p_a.brk_flags | (p_a.jst_flags << 6) | (p_a.direction << 12) | (p_a.orientation << 15), hash); +			return hash_fmix32(hash); +		} +	}; +  	// Shaped string cache. -	mutable LRUCache<uint64_t, Ref<TextLine>> cache; -	mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; +	mutable LRUCache<ShapedTextKey, Ref<TextLine>, ShapedTextKeyHasher> cache; +	mutable LRUCache<ShapedTextKey, Ref<TextParagraph>, ShapedTextKeyHasher> cache_wrap;  protected:  	// Output. diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 90cc3ea5f4..2defe729cc 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -410,5 +410,6 @@ ImmediateMesh::ImmediateMesh() {  	mesh = RS::get_singleton()->mesh_create();  }  ImmediateMesh::~ImmediateMesh() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(mesh);  } diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index d1278f9340..b5e02b2f76 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -30,6 +30,7 @@  #include "importer_mesh.h" +#include "core/io/marshalls.h"  #include "core/math/random_pcg.h"  #include "core/math/static_raycaster.h"  #include "scene/resources/surface_tool.h" @@ -424,9 +425,8 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli  			normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting  		} -		const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target -		float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3)); -		float mesh_error = 0.0f; +		Vector<float> merged_vertices_f32 = vector3_to_float32_array(merged_vertices_ptr, merged_vertex_count); +		float scale = SurfaceTool::simplify_scale_func(merged_vertices_f32.ptr(), merged_vertex_count, sizeof(float) * 3);  		unsigned int index_target = 12; // Start with the smallest target, 4 triangles  		unsigned int last_index_count = 0; @@ -446,11 +446,25 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli  			raycaster->commit();  		} +		const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target +		float mesh_error = 0.0f; +  		while (index_target < index_count) {  			PackedInt32Array new_indices;  			new_indices.resize(index_count); -			size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3); +			Vector<float> merged_normals_f32 = vector3_to_float32_array(merged_normals.ptr(), merged_normals.size()); + +			size_t new_index_count = SurfaceTool::simplify_with_attrib_func( +					(unsigned int *)new_indices.ptrw(), +					(const uint32_t *)merged_indices_ptr, index_count, +					merged_vertices_f32.ptr(), merged_vertex_count, +					sizeof(float) * 3, // Vertex stride +					index_target, +					max_mesh_error, +					&mesh_error, +					merged_normals_f32.ptr(), +					normal_weights.ptr(), 3);  			if (new_index_count < last_index_count * 1.5f) {  				index_target = index_target * 1.5f; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index a16d2c2072..44ce90cc4a 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -141,6 +141,7 @@ Material::Material() {  }  Material::~Material() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(material);  } @@ -3005,6 +3006,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :  }  BaseMaterial3D::~BaseMaterial3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	MutexLock lock(material_mutex);  	if (shader_map.has(current_key)) { diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index af770ddede..a610290a11 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -2117,6 +2117,7 @@ ArrayMesh::ArrayMesh() {  ArrayMesh::~ArrayMesh() {  	if (mesh.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RenderingServer::get_singleton()->free(mesh);  	}  } @@ -2132,5 +2133,6 @@ PlaceholderMesh::PlaceholderMesh() {  }  PlaceholderMesh::~PlaceholderMesh() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(rid);  } diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index 8afb0563b2..2ea357d814 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -365,5 +365,6 @@ MultiMesh::MultiMesh() {  }  MultiMesh::~MultiMesh() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(multimesh);  } diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index b77430c154..a07a8ac7cd 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -1894,6 +1894,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() :  }  ParticleProcessMaterial::~ParticleProcessMaterial() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	MutexLock lock(material_mutex);  	if (shader_map.has(current_key)) { diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 54d3676c15..aeb62d6709 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -340,6 +340,7 @@ PrimitiveMesh::PrimitiveMesh() {  }  PrimitiveMesh::~PrimitiveMesh() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(mesh);  } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 48ec084b02..c6621ce482 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -208,6 +208,7 @@ Shader::Shader() {  }  Shader::~Shader() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(shader);  } diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index 735134e27b..32ce0abf7a 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -106,5 +106,6 @@ Sky::Sky() {  }  Sky::~Sky() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(sky);  } diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 94967352c8..185cbdc6bf 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -46,7 +46,7 @@ void SurfaceTool::strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32A  	Vector<uint32_t> remap;  	remap.resize(r_vertices.size()); -	uint32_t new_vertex_count = generate_remap_func(remap.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), (float *)r_vertices.ptr(), r_vertices.size(), sizeof(Vector3)); +	uint32_t new_vertex_count = generate_remap_func(remap.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), r_vertices.ptr(), r_vertices.size(), sizeof(Vector3));  	remap_vertex_func(r_vertices.ptrw(), r_vertices.ptr(), r_vertices.size(), sizeof(Vector3), remap.ptr());  	r_vertices.resize(new_vertex_count);  	remap_index_func((unsigned int *)r_indices.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), remap.ptr()); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 18915e294e..0685ff0992 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -324,6 +324,7 @@ ImageTexture::ImageTexture() {}  ImageTexture::~ImageTexture() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RenderingServer::get_singleton()->free(texture);  	}  } @@ -630,6 +631,7 @@ PortableCompressedTexture2D::PortableCompressedTexture2D() {}  PortableCompressedTexture2D::~PortableCompressedTexture2D() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RenderingServer::get_singleton()->free(texture);  	}  } @@ -1041,6 +1043,7 @@ CompressedTexture2D::CompressedTexture2D() {}  CompressedTexture2D::~CompressedTexture2D() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(texture);  	}  } @@ -1225,6 +1228,7 @@ ImageTexture3D::ImageTexture3D() {  ImageTexture3D::~ImageTexture3D() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(texture);  	}  } @@ -1386,6 +1390,7 @@ CompressedTexture3D::CompressedTexture3D() {}  CompressedTexture3D::~CompressedTexture3D() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(texture);  	}  } @@ -1911,6 +1916,7 @@ CurveTexture::CurveTexture() {}  CurveTexture::~CurveTexture() {  	if (_texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(_texture);  	}  } @@ -2109,6 +2115,7 @@ CurveXYZTexture::CurveXYZTexture() {}  CurveXYZTexture::~CurveXYZTexture() {  	if (_texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(_texture);  	}  } @@ -2121,6 +2128,7 @@ GradientTexture1D::GradientTexture1D() {  GradientTexture1D::~GradientTexture1D() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(texture);  	}  } @@ -2263,6 +2271,7 @@ GradientTexture2D::GradientTexture2D() {  GradientTexture2D::~GradientTexture2D() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(texture);  	}  } @@ -2521,6 +2530,7 @@ void ProxyTexture::set_base(const Ref<Texture2D> &p_texture) {  	base = p_texture;  	if (base.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		if (proxy_ph.is_valid()) {  			RS::get_singleton()->texture_proxy_update(proxy, base->get_rid());  			RS::get_singleton()->free(proxy_ph); @@ -2571,6 +2581,7 @@ ProxyTexture::ProxyTexture() {  }  ProxyTexture::~ProxyTexture() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	if (proxy_ph.is_valid()) {  		RS::get_singleton()->free(proxy_ph);  	} @@ -2828,6 +2839,7 @@ AnimatedTexture::AnimatedTexture() {  }  AnimatedTexture::~AnimatedTexture() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(proxy);  	RS::get_singleton()->free(proxy_ph);  } @@ -3031,6 +3043,7 @@ ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) {  ImageTextureLayered::~ImageTextureLayered() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(texture);  	}  } @@ -3198,6 +3211,7 @@ CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {  CompressedTextureLayered::~CompressedTextureLayered() {  	if (texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(texture);  	}  } @@ -3353,6 +3367,7 @@ CameraTexture::CameraTexture() {}  CameraTexture::~CameraTexture() {  	if (_texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RenderingServer::get_singleton()->free(_texture);  	}  } @@ -3386,7 +3401,7 @@ RID PlaceholderTexture2D::get_rid() const {  void PlaceholderTexture2D::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture2D::set_size); -	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size"); +	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");  }  PlaceholderTexture2D::PlaceholderTexture2D() { @@ -3394,6 +3409,7 @@ PlaceholderTexture2D::PlaceholderTexture2D() {  }  PlaceholderTexture2D::~PlaceholderTexture2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(rid);  } @@ -3441,6 +3457,7 @@ PlaceholderTexture3D::PlaceholderTexture3D() {  	rid = RS::get_singleton()->texture_3d_placeholder_create();  }  PlaceholderTexture3D::~PlaceholderTexture3D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(rid);  } @@ -3499,5 +3516,6 @@ PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) {  	rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));  }  PlaceholderTextureLayered::~PlaceholderTextureLayered() { +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(rid);  } diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 6b8f8097a8..fc2366a78c 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -2650,6 +2650,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {  	{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" },  	{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" },  	{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, +	{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom0", "CUSTOM0" }, +	{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom1", "CUSTOM1" }, +	{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom2", "CUSTOM2" }, +	{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom3", "CUSTOM3" },  	// Node3D, Fragment  	{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 75deb1e60b..85ce47f06c 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -89,6 +89,9 @@ World2D::World2D() {  }  World2D::~World2D() { +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +	ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); +	ERR_FAIL_NULL(NavigationServer2D::get_singleton());  	RenderingServer::get_singleton()->free(canvas);  	PhysicsServer2D::get_singleton()->free(space);  	NavigationServer2D::get_singleton()->free(navigation_map); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index ae8c9a182f..8808da95d7 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -157,6 +157,9 @@ World3D::World3D() {  }  World3D::~World3D() { +	ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); +	ERR_FAIL_NULL(RenderingServer::get_singleton()); +	ERR_FAIL_NULL(NavigationServer3D::get_singleton());  	PhysicsServer3D::get_singleton()->free(space);  	RenderingServer::get_singleton()->free(scenario);  	NavigationServer3D::get_singleton()->free(navigation_map); diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp index 624c06ecf1..e5b160ae27 100644 --- a/servers/camera/camera_feed.cpp +++ b/servers/camera/camera_feed.cpp @@ -165,6 +165,7 @@ CameraFeed::CameraFeed(String p_name, FeedPosition p_position) {  CameraFeed::~CameraFeed() {  	// Free our textures +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RenderingServer::get_singleton()->free(texture[CameraServer::FEED_Y_IMAGE]);  	RenderingServer::get_singleton()->free(texture[CameraServer::FEED_CBCR_IMAGE]);  } diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp index ca76a819ec..47f767425b 100644 --- a/servers/physics_3d/godot_collision_solver_3d.cpp +++ b/servers/physics_3d/godot_collision_solver_3d.cpp @@ -513,10 +513,6 @@ bool GodotCollisionSolver3D::solve_distance_world_boundary(const GodotShape3D *p  }  bool GodotCollisionSolver3D::solve_distance(const GodotShape3D *p_shape_A, const Transform3D &p_transform_A, const GodotShape3D *p_shape_B, const Transform3D &p_transform_B, Vector3 &r_point_A, Vector3 &r_point_B, const AABB &p_concave_hint, Vector3 *r_sep_axis) { -	if (p_shape_A->is_concave()) { -		return false; -	} -  	if (p_shape_B->get_type() == PhysicsServer3D::SHAPE_WORLD_BOUNDARY) {  		Vector3 a, b;  		bool col = solve_distance_world_boundary(p_shape_B, p_transform_B, p_shape_A, p_transform_A, a, b); diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp index e1ca5a7103..627e6941cf 100644 --- a/servers/rendering/renderer_scene_occlusion_cull.cpp +++ b/servers/rendering/renderer_scene_occlusion_cull.cpp @@ -60,6 +60,8 @@ void RendererSceneOcclusionCull::HZBuffer::clear() {  	if (debug_image.is_valid()) {  		debug_image.unref();  	} + +	ERR_FAIL_NULL(RenderingServer::get_singleton());  	RS::get_singleton()->free(debug_texture);  } diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index ec4ae98397..b437ed1512 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -123,6 +123,7 @@ XRInterface::XRInterface() {}  XRInterface::~XRInterface() {  	if (vrs.vrs_texture.is_valid()) { +		ERR_FAIL_NULL(RenderingServer::get_singleton());  		RS::get_singleton()->free(vrs.vrs_texture);  		vrs.vrs_texture = RID();  	} diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h index f5c5de7fdf..5feb3a4054 100644 --- a/tests/core/object/test_object.h +++ b/tests/core/object/test_object.h @@ -281,6 +281,84 @@ TEST_CASE("[Object] Absent name getter") {  			actual_value == Variant(),  			"The returned value should equal nil variant.");  } + +TEST_CASE("[Object] Signals") { +	Object object; + +	CHECK_FALSE(object.has_signal("my_custom_signal")); + +	List<MethodInfo> signals_before; +	object.get_signal_list(&signals_before); + +	object.add_user_signal(MethodInfo("my_custom_signal")); + +	CHECK(object.has_signal("my_custom_signal")); + +	List<MethodInfo> signals_after; +	object.get_signal_list(&signals_after); + +	// Should be one more signal. +	CHECK_EQ(signals_before.size() + 1, signals_after.size()); + +	SUBCASE("Adding the same user signal again should not have any effect") { +		CHECK(object.has_signal("my_custom_signal")); +		ERR_PRINT_OFF; +		object.add_user_signal(MethodInfo("my_custom_signal")); +		ERR_PRINT_ON; +		CHECK(object.has_signal("my_custom_signal")); + +		List<MethodInfo> signals_after_existing_added; +		object.get_signal_list(&signals_after_existing_added); + +		CHECK_EQ(signals_after.size(), signals_after_existing_added.size()); +	} + +	SUBCASE("Trying to add a preexisting signal should not have any effect") { +		CHECK(object.has_signal("script_changed")); +		ERR_PRINT_OFF; +		object.add_user_signal(MethodInfo("script_changed")); +		ERR_PRINT_ON; +		CHECK(object.has_signal("script_changed")); + +		List<MethodInfo> signals_after_existing_added; +		object.get_signal_list(&signals_after_existing_added); + +		CHECK_EQ(signals_after.size(), signals_after_existing_added.size()); +	} + +	SUBCASE("Adding an empty signal should not have any effect") { +		CHECK_FALSE(object.has_signal("")); +		ERR_PRINT_OFF; +		object.add_user_signal(MethodInfo("")); +		ERR_PRINT_ON; +		CHECK_FALSE(object.has_signal("")); + +		List<MethodInfo> signals_after_empty_added; +		object.get_signal_list(&signals_after_empty_added); + +		CHECK_EQ(signals_after.size(), signals_after_empty_added.size()); +	} + +	SUBCASE("Emitting a non existing signal will return an error") { +		Error err = object.emit_signal("some_signal"); +		CHECK(err == ERR_UNAVAILABLE); +	} + +	SUBCASE("Emitting an existing signal should call the connected method") { +		Array empty_signal_args; +		empty_signal_args.push_back(Array()); + +		SIGNAL_WATCH(&object, "my_custom_signal"); +		SIGNAL_CHECK_FALSE("my_custom_signal"); + +		Error err = object.emit_signal("my_custom_signal"); +		CHECK(err == OK); + +		SIGNAL_CHECK("my_custom_signal", empty_signal_args); +		SIGNAL_UNWATCH(&object, "my_custom_signal"); +	} +} +  } // namespace TestObject  #endif // TEST_OBJECT_H |