Browse Source

Merge pull request #53296 from lawnjelly/project_settings_dirty_flag

Rémi Verschelde 3 years ago
parent
commit
43dfafdfc9

+ 19 - 0
core/project_settings.cpp

@@ -165,9 +165,26 @@ String ProjectSettings::globalize_path(const String &p_path) const {
 	return p_path;
 }
 
+void ProjectSettings::update() {
+	if (_dirty_this_frame) {
+		// A signal is sent a single time at the end of the frame when project settings
+		// are changed. This allows objects to respond.
+		// Alternatively objects outside the signal system can query ProjectSettings::has_changes()
+		if (_dirty_this_frame == 2) {
+			emit_signal("project_settings_changed");
+		}
+
+		_dirty_this_frame--;
+	}
+}
+
 bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
 	_THREAD_SAFE_METHOD_
 
+	// marking the project settings as dirty allows them only to be
+	// checked when dirty.
+	_dirty_this_frame = 2;
+
 	if (p_value.get_type() == Variant::NIL) {
 		props.erase(p_name);
 	} else {
@@ -1022,6 +1039,8 @@ void ProjectSettings::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &ProjectSettings::property_get_revert);
 
 	ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd);
+
+	ADD_SIGNAL(MethodInfo("project_settings_changed"));
 }
 
 ProjectSettings::ProjectSettings() {

+ 35 - 0
core/project_settings.h

@@ -35,10 +35,37 @@
 #include "core/os/thread_safe.h"
 #include "core/set.h"
 
+// Querying ProjectSettings is usually done at startup.
+// Additionally, in order to keep track of changes to ProjectSettings,
+// instead of Querying all the strings every frame just in case of changes,
+// there is a signal "project_settings_changed" which objects can subscribe to.
+
+// E.g. (from another Godot object)
+// // Call your user written object function to Query the project settings once at creation,
+// perhaps in an ENTER_TREE notification:
+// _project_settings_changed()
+// // Then connect your function to the signal so it is called every time something changes in future:
+// ProjectSettings::get_singleton()->connect("project_settings_changed", this, "_project_settings_changed");
+
+// Where for example your function may take the form:
+// void _project_settings_changed() {
+// _shadowmap_size = GLOBAL_GET("rendering/quality/shadow_atlas/size");
+// }
+
+// You may want to also disconnect from the signal in EXIT_TREE notification, if your object may be deleted
+// before ProjectSettings:
+// ProjectSettings::get_singleton()->disconnect("project_settings_changed", this, "_project_settings_changed");
+
+// Additionally, for objects that are not regular Godot objects capable of subscribing to signals (e.g. Rasterizers),
+// you can also query the function "has_changes()" each frame,
+// and update your local settings whenever this is set.
+
 class ProjectSettings : public Object {
 	GDCLASS(ProjectSettings, Object);
 	_THREAD_SAFE_CLASS_
 
+	int _dirty_this_frame = 2;
+
 public:
 	typedef Map<String, Variant> CustomMap;
 	static const String PROJECT_DATA_DIR_NAME_SUFFIX;
@@ -168,6 +195,14 @@ public:
 
 	bool has_custom_feature(const String &p_feature) const;
 
+	// Either use the signal `project_settings_changed` or query this function.
+	// N.B. _dirty_this_frame is set initially to 2.
+	// This is to cope with the situation where a project setting is changed in the iteration AFTER it is read.
+	// There is therefore the potential for a change to be missed. Persisting the counter
+	// for two frames avoids this, at the cost of a frame delay.
+	bool has_changes() const { return _dirty_this_frame == 1; }
+	void update();
+
 	ProjectSettings();
 	~ProjectSettings();
 };

+ 7 - 0
doc/classes/ProjectSettings.xml

@@ -1569,6 +1569,13 @@
 			Cell size used for the 2D hash grid that [VisibilityNotifier2D] uses (in pixels).
 		</member>
 	</members>
+	<signals>
+		<signal name="project_settings_changed">
+			<description>
+				Objects can use this signal to restrict reading of settings only to situations where a change has been made.
+			</description>
+		</signal>
+	</signals>
 	<constants>
 	</constants>
 </class>

+ 2 - 2
editor/import_defaults_editor.cpp

@@ -99,8 +99,8 @@ void ImportDefaultsEditor::_save() {
 		} else {
 			ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), Variant());
 		}
-
-		emit_signal("project_settings_changed");
+		// Calling ProjectSettings::set() causes the signal "project_settings_changed" to be sent to ProjectSettings.
+		// ProjectSettingsEditor subscribes to this and can reads the settings updated here.
 	}
 }
 

+ 55 - 33
editor/plugins/spatial_editor_plugin.cpp

@@ -2462,6 +2462,49 @@ void SpatialEditorPlugin::edited_scene_changed() {
 	}
 }
 
+void SpatialEditorViewport::_project_settings_changed() {
+	if (viewport) {
+		_project_settings_change_pending = false;
+
+		//update shadow atlas if changed
+		int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/size");
+		int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_0_subdiv");
+		int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_1_subdiv");
+		int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_2_subdiv");
+		int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_3_subdiv");
+
+		viewport->set_shadow_atlas_size(shadowmap_size);
+		viewport->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q0));
+		viewport->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q1));
+		viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2));
+		viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3));
+
+		// Update MSAA, FXAA, debanding and HDR if changed.
+		int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/filters/msaa");
+		viewport->set_msaa(Viewport::MSAA(msaa_mode));
+
+		bool use_fxaa = ProjectSettings::get_singleton()->get("rendering/quality/filters/use_fxaa");
+		viewport->set_use_fxaa(use_fxaa);
+
+		bool use_debanding = ProjectSettings::get_singleton()->get("rendering/quality/filters/use_debanding");
+		viewport->set_use_debanding(use_debanding);
+
+		float sharpen_intensity = ProjectSettings::get_singleton()->get("rendering/quality/filters/sharpen_intensity");
+		viewport->set_sharpen_intensity(sharpen_intensity);
+
+		bool hdr = ProjectSettings::get_singleton()->get("rendering/quality/depth/hdr");
+		viewport->set_hdr(hdr);
+
+		const bool use_32_bpc_depth = ProjectSettings::get_singleton()->get("rendering/quality/depth/use_32_bpc_depth");
+		viewport->set_use_32_bpc_depth(use_32_bpc_depth);
+
+	} else {
+		// Could not update immediately, set a pending update.
+		// This may never happen, but is included for safety
+		_project_settings_change_pending = true;
+	}
+}
+
 void SpatialEditorViewport::_notification(int p_what) {
 	if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
 		bool visible = is_visible_in_tree();
@@ -2583,19 +2626,9 @@ void SpatialEditorViewport::_notification(int p_what) {
 			}
 		}
 
-		//update shadow atlas if changed
-
-		int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/size");
-		int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_0_subdiv");
-		int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_1_subdiv");
-		int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_2_subdiv");
-		int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_3_subdiv");
-
-		viewport->set_shadow_atlas_size(shadowmap_size);
-		viewport->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q0));
-		viewport->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q1));
-		viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2));
-		viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3));
+		if (_project_settings_change_pending) {
+			_project_settings_changed();
+		}
 
 		bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION));
 
@@ -2603,26 +2636,6 @@ void SpatialEditorViewport::_notification(int p_what) {
 			viewport_container->set_stretch_shrink(shrink ? 2 : 1);
 		}
 
-		// Update MSAA, FXAA, debanding and HDR if changed.
-
-		int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/filters/msaa");
-		viewport->set_msaa(Viewport::MSAA(msaa_mode));
-
-		bool use_fxaa = ProjectSettings::get_singleton()->get("rendering/quality/filters/use_fxaa");
-		viewport->set_use_fxaa(use_fxaa);
-
-		bool use_debanding = ProjectSettings::get_singleton()->get("rendering/quality/filters/use_debanding");
-		viewport->set_use_debanding(use_debanding);
-
-		float sharpen_intensity = ProjectSettings::get_singleton()->get("rendering/quality/filters/sharpen_intensity");
-		viewport->set_sharpen_intensity(sharpen_intensity);
-
-		const bool hdr = ProjectSettings::get_singleton()->get("rendering/quality/depth/hdr");
-		viewport->set_hdr(hdr);
-
-		const bool use_32_bpc_depth = ProjectSettings::get_singleton()->get("rendering/quality/depth/use_32_bpc_depth");
-		viewport->set_use_32_bpc_depth(use_32_bpc_depth);
-
 		bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION));
 		info_label->set_visible(show_info);
 
@@ -2689,10 +2702,17 @@ void SpatialEditorViewport::_notification(int p_what) {
 		surface->connect("focus_entered", this, "_surface_focus_enter");
 		surface->connect("focus_exited", this, "_surface_focus_exit");
 
+		// Ensure we are up to date with project settings
+		_project_settings_changed();
+
+		// Any further changes to project settings get a signal
+		ProjectSettings::get_singleton()->connect("project_settings_changed", this, "_project_settings_changed");
+
 		_init_gizmo_instance(index);
 	}
 
 	if (p_what == NOTIFICATION_EXIT_TREE) {
+		ProjectSettings::get_singleton()->disconnect("project_settings_changed", this, "_project_settings_changed");
 		_finish_gizmo_instances();
 	}
 
@@ -3582,6 +3602,7 @@ void SpatialEditorViewport::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &SpatialEditorViewport::_selection_menu_hide);
 	ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpatialEditorViewport::can_drop_data_fw);
 	ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpatialEditorViewport::drop_data_fw);
+	ClassDB::bind_method(D_METHOD("_project_settings_changed"), &SpatialEditorViewport::_project_settings_changed);
 
 	ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")));
 	ADD_SIGNAL(MethodInfo("clicked", PropertyInfo(Variant::OBJECT, "viewport")));
@@ -4104,6 +4125,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 	gizmo_scale = 1.0;
 
 	preview_node = nullptr;
+	_project_settings_change_pending = false;
 
 	info_label = memnew(Label);
 	info_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);

+ 3 - 0
editor/plugins/spatial_editor_plugin.h

@@ -243,6 +243,7 @@ public:
 
 private:
 	int index;
+	bool _project_settings_change_pending;
 	ViewType view_type;
 	void _menu_option(int p_option);
 	void _set_auto_orthogonal();
@@ -458,6 +459,8 @@ private:
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
 	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
 
+	void _project_settings_changed();
+
 protected:
 	void _notification(int p_what);
 	static void _bind_methods();

+ 9 - 1
editor/project_settings_editor.cpp

@@ -143,7 +143,16 @@ void ProjectSettingsEditor::_notification(int p_what) {
 			restart_icon->set_texture(get_icon("StatusWarning", "EditorIcons"));
 			restart_label->add_color_override("font_color", get_color("warning_color", "Editor"));
 
+			// The ImportDefaultsEditor changes settings which must be read by this object when changed
+			ProjectSettings::get_singleton()->connect("project_settings_changed", this, "_settings_changed");
+
+		} break;
+		case NOTIFICATION_EXIT_TREE: {
+			if (ProjectSettings::get_singleton()) {
+				ProjectSettings::get_singleton()->disconnect("project_settings_changed", this, "_settings_changed");
+			}
 		} break;
+
 		case NOTIFICATION_POPUP_HIDE: {
 			EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "project_settings", get_rect());
 			set_process_unhandled_input(false);
@@ -2141,7 +2150,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
 	import_defaults_editor = memnew(ImportDefaultsEditor);
 	import_defaults_editor->set_name(TTR("Import Defaults"));
 	tab_container->add_child(import_defaults_editor);
-	import_defaults_editor->connect("project_settings_changed", this, "_settings_changed");
 
 	timer = memnew(Timer);
 	timer->set_wait_time(1.5);

+ 2 - 0
scene/main/scene_tree.cpp

@@ -589,6 +589,8 @@ bool SceneTree::idle(float p_time) {
 
 	_call_idle_callbacks();
 
+	ProjectSettings::get_singleton()->update();
+
 #ifdef TOOLS_ENABLED
 
 	if (Engine::get_singleton()->is_editor_hint()) {