ProjectSettings add dirty flag and project_settings_changed signal

Most frames there will be no change in project settings, and it makes no sense to read settings every frame in case of changes, as a large number of string compares are involved.

This PR adds a signal to ProjectSettings that can be subscribed to in order to keep local settings up to date with ProjectSettings.

In addition a function `ProjectSettings::has_changes()` is provided for objects outside the signal system (e.g. Rasterizers).
This commit is contained in:
lawnjelly 2021-10-01 11:47:28 +01:00
parent 4ed2c8ee08
commit f0af29346b
8 changed files with 132 additions and 36 deletions

View file

@ -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() {

View file

@ -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();
};

View file

@ -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>

View file

@ -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.
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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);

View file

@ -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()) {