From f44455c3778e0b7e451360e68c7e47fc3b5c689b Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Tue, 7 Jan 2025 11:14:15 +0000 Subject: [PATCH 01/16] Physics Interpolation - Auto-reset on `set_physics_interpolation_mode()` Fixes historical bug where auto-reset wasn't working correctly. Also fixes process modes on Cameras when mode is changed. --- scene/2d/camera_2d.cpp | 1 + scene/3d/camera.cpp | 1 + scene/main/node.cpp | 9 ++++----- scene/main/scene_tree.cpp | 5 +++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 146f87d4a..c1564ae19 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -297,6 +297,7 @@ void Camera2D::_notification(int p_what) { // Force the limits etc to update. _interpolation_data.xform_curr = get_camera_transform(); _interpolation_data.xform_prev = _interpolation_data.xform_curr; + _update_process_mode(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { if (!smoothing_active && !is_physics_interpolated_and_enabled()) { diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index fa1ebebd5..54bfe2108 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -229,6 +229,7 @@ void Camera::_notification(int p_what) { if (is_inside_tree()) { _interpolation_data.xform_curr = get_global_transform(); _interpolation_data.xform_prev = _interpolation_data.xform_curr; + _update_process_mode(); } } break; case NOTIFICATION_EXIT_WORLD: { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index bae4f7755..0358690ed 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1200,13 +1200,12 @@ void Node::set_physics_interpolation_mode(PhysicsInterpolationMode p_mode) { } break; } - // if swapping from interpolated to non-interpolated, use this as - // an extra means to cause a reset - if (is_physics_interpolated() && !interpolate && is_inside_tree()) { + _propagate_physics_interpolated(interpolate); + + // Auto-reset on changing interpolation mode. + if (is_physics_interpolated() && is_inside_tree()) { propagate_notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); } - - _propagate_physics_interpolated(interpolate); } void Node::reset_physics_interpolation() { diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 96029dc60..3e90a5f12 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -591,6 +591,11 @@ void SceneTree::set_physics_interpolation_enabled(bool p_enabled) { _physics_interpolation_enabled = p_enabled; RenderingServer::get_singleton()->set_physics_interpolation_enabled(p_enabled); + + // Perform an auto reset on the root node for convenience for the user. + if (root) { + root->reset_physics_interpolation(); + } } bool SceneTree::is_physics_interpolation_enabled() const { From 0269f45fe554c2143f934abc59784f57d031407f Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Thu, 30 Jan 2025 11:44:16 +0000 Subject: [PATCH 02/16] Physics Interpolation - fix client interpolation pump Client interpolation pump is moved AFTER the physics tick, after physics objects have been moved. This is necessary because the `current` transform is also updated during the pump. --- scene/main/scene_tree.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 3e90a5f12..d959b5fff 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -618,11 +618,6 @@ void SceneTree::iteration_prepare() { // are flushed before pumping the interpolation prev and currents. flush_transform_notifications(); RenderingServer::get_singleton()->tick(); - - // Any objects performing client physics interpolation - // should be given an opportunity to keep their previous transforms - // up to date before each new physics tick. - _client_physics_interpolation.physics_process(); } } @@ -631,6 +626,11 @@ void SceneTree::iteration_end() { // to be flushed to the RenderingServer before finishing a physics tick. if (_physics_interpolation_enabled) { flush_transform_notifications(); + + // Any objects performing client physics interpolation + // should be given an opportunity to keep their previous transforms + // up to date. + _client_physics_interpolation.physics_process(); } } From d8f68fa38079e9389998f7671b7767d0ccb63b65 Mon Sep 17 00:00:00 2001 From: axewizardbgg Date: Mon, 10 Feb 2025 19:07:16 -0500 Subject: [PATCH 03/16] Docs: BitMap: Added description for opaque_to_polygons method --- doc/classes/BitMap.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/classes/BitMap.xml b/doc/classes/BitMap.xml index 11de04d35..f8eec9354 100644 --- a/doc/classes/BitMap.xml +++ b/doc/classes/BitMap.xml @@ -62,6 +62,9 @@ + Generates polygon outlines from the opaque (non-transparent) areas of the [BitMap] using a Marching Squares algorithm. + Returns an [Array] of [PoolVector2Array], where each [PoolVector2Array] represents a polygon outline. These outlines can be directly assigned to the [code]polygon[/code] property of nodes like [CollisionPolygon2D] or [OccluderPolygon2D]. + The [code]epsilon[/code] parameter controls polygon simplification. A lower value produces more accurate polygons, but at the cost of increased polygon size and potential performance impact. A higher value simplifies the polygons, reducing their size and improving performance, but with less accuracy. From 3b688f61624ffbe94bf9a02d474ad235ede7f40f Mon Sep 17 00:00:00 2001 From: Yufeng Ying Date: Wed, 5 Mar 2025 17:36:59 +0800 Subject: [PATCH 04/16] Prevent inlining error print functions. --- core/error/error_macros.h | 18 +++++++++--------- core/typedefs.h | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/core/error/error_macros.h b/core/error/error_macros.h index c09c4178e..a53203cf1 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -78,15 +78,15 @@ struct ErrorHandlerList { void add_error_handler(ErrorHandlerList *p_handler); void remove_error_handler(ErrorHandlerList *p_handler); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false); -void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false); -void _err_flush_stdout(); +_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +_NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR); +_NO_INLINE_ void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false); +_NO_INLINE_ void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false); +_NO_INLINE_ void _err_flush_stdout(); void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string); diff --git a/core/typedefs.h b/core/typedefs.h index 56a2bf2a6..fd9926470 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -70,6 +70,20 @@ #endif +// Should never inline. +#ifndef _NO_INLINE_ +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define _NO_INLINE_ __attribute__((noinline)) +#elif defined(__llvm__) +#define _NO_INLINE_ __attribute__((noinline)) +#elif defined(_MSC_VER) +#define _NO_INLINE_ __declspec(noinline) +#else +#define _NO_INLINE_ +#endif + +#endif + // No discard allows the compiler to flag warnings if we don't use the return value of functions / classes #ifndef _NO_DISCARD_ // c++ 17 onwards From d149a595139d9932f4bf975beaf6795ea1225da4 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Fri, 7 Mar 2025 12:43:51 +0000 Subject: [PATCH 05/16] Add GLOBAL_GET cached macros. GLOBAL_GET is an expensive operation which should not be used each frame / tick. This PR adds macros which do a cheaper revision check, and only call the expensive GLOBAL_GET when project settings have changed. Co-authored-by: Lukas Tenbrink --- core/config/project_settings.cpp | 2 ++ core/config/project_settings.h | 29 ++++++++++++++++++- core/error/error_macros.cpp | 2 +- drivers/gles2/rasterizer_gles2.cpp | 2 +- drivers/gles2/rasterizer_scene_gles2.cpp | 4 +-- drivers/gles2/rasterizer_storage_gles2.cpp | 2 +- drivers/gles2/shader_compiler_gles2.cpp | 4 +-- drivers/gles2/shader_gles2.cpp | 2 +- drivers/gles3/rasterizer_gles3.cpp | 2 +- drivers/gles3/rasterizer_scene_gles3.cpp | 16 +++++----- drivers/gles3/rasterizer_storage_gles3.cpp | 2 +- drivers/gles3/shader_compiler_gles3.cpp | 4 +-- editor/import/resource_importer_texture.cpp | 4 +-- .../animation_player_editor_plugin.cpp | 2 +- editor/plugins/canvas_item_editor_plugin.cpp | 2 +- editor/plugins/editor_preview_plugins.cpp | 2 +- editor/plugins/theme_editor_preview.cpp | 2 +- .../editor_script_text_editor.cpp | 2 +- main/main.cpp | 2 +- modules/gdscript/gdscript_editor.cpp | 2 +- modules/gdscript/gdscript_parser.cpp | 6 ++-- .../android/export/gradle_export_util.cpp | 2 +- scene/3d/mesh_instance.cpp | 4 +-- scene/main/scene_tree.cpp | 2 +- scene/main/viewport.cpp | 6 ++-- scene/resources/material/spatial_material.cpp | 2 +- servers/audio/audio_stream.cpp | 2 +- .../portals/portal_occlusion_culler.cpp | 2 +- servers/rendering_server.cpp | 8 ++--- 29 files changed, 76 insertions(+), 47 deletions(-) diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 9db9935de..3e7646f8d 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -194,6 +194,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < custom_feature_array.size(); i++) { custom_features.insert(custom_feature_array[i]); } + _version++; return true; } @@ -207,6 +208,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { } } + _version++; return true; } bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 0e0f913b6..3f0b5b524 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -67,6 +67,10 @@ class ProjectSettings : public Object { int _dirty_this_frame = 2; + // Starting version from 1 ensures that all callers can reset their tested version to 0, + // and will always detect the initial project settings as a "change". + uint32_t _version = 1; + public: typedef RBMap CustomMap; static const String PROJECT_DATA_DIR_NAME_SUFFIX; @@ -198,6 +202,10 @@ public: // 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; } + + // Testing a version allows fast cached GET_GLOBAL macros. + uint32_t get_version() const { return _version; } + void update(); ProjectSettings(); @@ -215,4 +223,23 @@ Variant _GLOBAL_DEF_ALIAS(const String &p_var, const String &p_old_name, const V #define GLOBAL_DEF_ALIAS_RST(m_var, m_old_name, m_value) _GLOBAL_DEF(m_var, m_old_name, m_value, true) #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) -#endif +///////////////////////////////////////////////////////////////////////////////////////// +// Cached versions of GLOBAL_GET. +// Cached but uses a typed variable for storage, this can be more efficient. +#define GLOBAL_GET_CACHED(m_type, m_setting_name) ([](const char *p_name) -> m_type {\ +static_assert(std::is_trivially_destructible::value, "GLOBAL_GET_CACHED must use a trivial type that allows static lifetime.");\ +static m_type local_var;\ +static uint32_t local_version = 0;\ +static Mutex local_mutex;\ +uint32_t new_version = ProjectSettings::get_singleton()->get_version();\ +if (local_version != new_version) {\ + MutexLock lock(local_mutex);\ + local_version = new_version;\ + local_var = ProjectSettings::get_singleton()->get(p_name);\ + return local_var;\ +}\ +MutexLock lock(local_mutex);\ +return local_var; })(m_setting_name) + +#endif // PROJECT_SETTINGS_H + diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index a32e11ea5..aaa799fa6 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -147,7 +147,7 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, warn_count = warn_max; warn_timeout = time_now + warn_timeout_seconds; - if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { + if (GLOBAL_GET_CACHED(bool, "debug/settings/physics_interpolation/enable_warnings")) { // UINT64_MAX means unused. if (p_id == UINT64_MAX) { _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", ERR_HANDLER_WARNING); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 997532067..96f74752f 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -276,7 +276,7 @@ void RasterizerGLES2::begin_frame(double frame_step) { frame_step = 0.001; } - double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); + double time_roll_over = GLOBAL_GET_CACHED(double, "rendering/limits/time/time_rollover_secs"); time_total = Math::fmod(time_total, time_roll_over); storage->frame.time[0] = time_total; diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 509c2f390..0d8c50fbe 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -4059,9 +4059,9 @@ void RasterizerSceneGLES2::initialize() { } void RasterizerSceneGLES2::iteration() { - shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET("rendering/quality/shadows/filter_mode"))); + shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET_CACHED(int32_t, "rendering/quality/shadows/filter_mode"))); - const int directional_shadow_size_new = next_power_of_2(int(GLOBAL_GET("rendering/quality/directional_shadow/size"))); + const int directional_shadow_size_new = next_power_of_2(GLOBAL_GET_CACHED(int32_t, "rendering/quality/directional_shadow/size")); if (directional_shadow_size != directional_shadow_size_new) { directional_shadow_size = directional_shadow_size_new; directional_shadow_create(); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 663fd9e54..9a3bcac47 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -2296,7 +2296,7 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, RS: } //bool has_morph = p_blend_shapes.size(); - bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); Surface::Attrib attribs[RS::ARRAY_MAX]; diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 6b30ca682..4268e0831 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -1226,7 +1226,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { //actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; //actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; - bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley"); + bool force_lambert = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_lambert_over_burley"); if (!force_lambert) { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; @@ -1236,7 +1236,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; - bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx"); + bool force_blinn = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_blinn_over_ggx"); if (!force_blinn) { actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp index eabbeafea..e40375c42 100644 --- a/drivers/gles2/shader_gles2.cpp +++ b/drivers/gles2/shader_gles2.cpp @@ -178,7 +178,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { strings.push_back("#define USE_HIGHP_PRECISION\n"); #endif - if (GLOBAL_GET("rendering/gles2/compatibility/enable_high_float.Android")) { + if (GLOBAL_GET_CACHED(bool, "rendering/gles2/compatibility/enable_high_float.Android")) { // enable USE_HIGHP_PRECISION but safeguarded by an availability check as highp support is optional in GLES2 // see Section 4.5.4 of the GLSL_ES_Specification_1.00 strings.push_back("#ifdef GL_FRAGMENT_PRECISION_HIGH\n #define USE_HIGHP_PRECISION\n#endif\n"); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 22239493b..7c147c3d1 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -200,7 +200,7 @@ void RasterizerGLES3::begin_frame(double frame_step) { frame_step = 0.001; } - double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); + double time_roll_over = GLOBAL_GET_CACHED(double, "rendering/limits/time/time_rollover_secs"); time_total = Math::fmod(time_total, time_roll_over); storage->frame.time[0] = time_total; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index d76aebaea..c7b1f326e 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -5315,22 +5315,22 @@ void RasterizerSceneGLES3::initialize() { } void RasterizerSceneGLES3::iteration() { - shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET("rendering/quality/shadows/filter_mode"))); + shadow_filter_mode = ShadowFilterMode(GLOBAL_GET_CACHED(int32_t, "rendering/quality/shadows/filter_mode")); - const int directional_shadow_size_new = next_power_of_2(int(GLOBAL_GET("rendering/quality/directional_shadow/size"))); + const int directional_shadow_size_new = next_power_of_2(GLOBAL_GET_CACHED(int32_t, "rendering/quality/directional_shadow/size")); if (directional_shadow_size != directional_shadow_size_new) { directional_shadow_size = directional_shadow_size_new; directional_shadow_create(); } - subsurface_scatter_follow_surface = GLOBAL_GET("rendering/quality/subsurface_scattering/follow_surface"); - subsurface_scatter_weight_samples = GLOBAL_GET("rendering/quality/subsurface_scattering/weight_samples"); - subsurface_scatter_quality = SubSurfaceScatterQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/quality"))); - subsurface_scatter_size = GLOBAL_GET("rendering/quality/subsurface_scattering/scale"); + subsurface_scatter_follow_surface = GLOBAL_GET_CACHED(bool, "rendering/quality/subsurface_scattering/follow_surface"); + subsurface_scatter_weight_samples = GLOBAL_GET_CACHED(bool, "rendering/quality/subsurface_scattering/weight_samples"); + subsurface_scatter_quality = SubSurfaceScatterQuality(int(GLOBAL_GET_CACHED(int32_t, "rendering/quality/subsurface_scattering/quality"))); + subsurface_scatter_size = GLOBAL_GET_CACHED(float, "rendering/quality/subsurface_scattering/scale"); - storage->config.use_lightmap_filter_bicubic = GLOBAL_GET("rendering/quality/lightmapping/use_bicubic_sampling"); + storage->config.use_lightmap_filter_bicubic = GLOBAL_GET_CACHED(bool, "rendering/quality/lightmapping/use_bicubic_sampling"); state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_FILTER_BICUBIC, storage->config.use_lightmap_filter_bicubic); - state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, GLOBAL_GET("rendering/quality/voxel_cone_tracing/high_quality")); + state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, GLOBAL_GET_CACHED(bool, "rendering/quality/voxel_cone_tracing/high_quality")); } void RasterizerSceneGLES3::finalize() { diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 3d623b021..9011b54d6 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -3390,7 +3390,7 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, RS: } //bool has_morph = p_blend_shapes.size(); - bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); Surface::Attrib attribs[RS::ARRAY_MAX]; diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index 66d5bce64..a31686262 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -1268,7 +1268,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; - bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley"); + bool force_lambert = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_lambert_over_burley"); if (!force_lambert) { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; @@ -1278,7 +1278,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; - bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx"); + bool force_blinn = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_blinn_over_ggx"); if (!force_blinn) { actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index b3e9c678e..5af645861 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -419,12 +419,12 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String tex_flags |= Texture::FLAG_REPEAT; const bool min_gles3 = GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3" && - !GLOBAL_GET("rendering/quality/driver/fallback_to_gles2"); + !GLOBAL_GET_CACHED(bool, "rendering/quality/driver/fallback_to_gles2"); if (!min_gles3 && !image->is_size_po2()) { // The project can be run using GLES2. GLES2 does not guarantee that // repeating textures with a non-power-of-two size will be displayed // without artifacts (due to upscaling to the nearest power of 2). - if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2")) { + if (GLOBAL_GET_CACHED(bool, "rendering/quality/driver/fallback_to_gles2")) { WARN_PRINT(vformat("%s: Imported a repeating texture with a size of %dx%d, but the project is configured to allow falling back to GLES2.\nNon-power-of-2 repeating textures may not display correctly on some platforms such as HTML5. This is because GLES2 does not mandate support for non-power-of-2 repeating textures.", p_source_file, image->get_width(), image->get_height())); } else { diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 23bfe6833..1386d7538 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1475,7 +1475,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { // Render every past/future step with the capture shader. RS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, onion.capture.material->get_rid()); - onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET("rendering/environment/default_clear_color")); + onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET_CACHED(Color, "rendering/environment/default_clear_color")); onion.capture.material->set_shader_param("differences_only", onion.differences_only); onion.capture.material->set_shader_param("present", onion.differences_only ? RS::get_singleton()->viewport_get_texture(present_rid) : RID()); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 6c5dbcc1f..f95e82fe6 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -3677,7 +3677,7 @@ void CanvasItemEditor::set_current_tool(Tool p_tool) { void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_PHYSICS_PROCESS) { - EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels")); + EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET_CACHED(bool, "gui/common/snap_controls_to_pixels")); bool has_container_parents = false; int nb_control = 0; diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 729a603bb..bc5f82d1f 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -894,7 +894,7 @@ Ref EditorFontPreviewPlugin::generate_from_path(const String &p_path, c Ref font = sampled_font; - const Color c = GLOBAL_GET("rendering/environment/default_clear_color"); + const Color c = GLOBAL_GET_CACHED(Color, "rendering/environment/default_clear_color"); const float fg = c.get_luminance() < 0.5 ? 1.0 : 0.0; font->draw(canvas_item, pos, sampled_text, Color(fg, fg, fg)); diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index 9e469bebd..a0eb1d7bb 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -96,7 +96,7 @@ void ThemeEditorPreview::_propagate_redraw(Control *p_at) { void ThemeEditorPreview::_refresh_interval() { // In case the project settings have changed. - preview_bg->set_frame_color(GLOBAL_GET("rendering/environment/default_clear_color")); + preview_bg->set_frame_color(GLOBAL_GET_CACHED(Color, "rendering/environment/default_clear_color")); _propagate_redraw(preview_bg); _propagate_redraw(preview_content); diff --git a/editor_modules/editor_code_editor/editor_script_text_editor.cpp b/editor_modules/editor_code_editor/editor_script_text_editor.cpp index e44386bc2..ef2e45956 100644 --- a/editor_modules/editor_code_editor/editor_script_text_editor.cpp +++ b/editor_modules/editor_code_editor/editor_script_text_editor.cpp @@ -446,7 +446,7 @@ void EditorScriptTextEditor::_validate_script() { warnings_panel->clear(); // Add missing connections. - if (GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) { + if (GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/enable")) { Node *base = get_tree()->get_edited_scene_root(); if (base && missing_connections.size() > 0) { warnings_panel->push_table(1); diff --git a/main/main.cpp b/main/main.cpp index 822bd3145..56c72fac0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2544,7 +2544,7 @@ bool Main::iteration() { if (print_fps) { print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2))); } - } else if (print_fps || GLOBAL_GET("debug/settings/stdout/print_fps")) { + } else if (print_fps || GLOBAL_GET_CACHED(bool, "debug/settings/stdout/print_fps")) { print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2))); } } else { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 8d5a9e799..f78a60bcb 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2091,7 +2091,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (!_static) { List methods; - bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize(); + bool is_autocompleting_getters = GLOBAL_GET_CACHED(bool, "debug/gdscript/completion/autocomplete_setters_and_getters"); ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters); for (List::Element *E = methods.front(); E; E = E->next()) { if (E->get().name.begins_with("_")) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index b0d5798c1..765c2dc22 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -8703,10 +8703,10 @@ void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol } void GDScriptParser::_add_warning(int p_code, int p_line, const Vector &p_symbols) { - if (GLOBAL_GET("debug/gdscript/warnings/exclude_addons").booleanize() && base_path.begins_with("res://addons/")) { + if (GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/exclude_addons") && base_path.begins_with("res://addons/")) { return; } - if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) { + if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/enable")) { return; } String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower(); @@ -8820,7 +8820,7 @@ Error GDScriptParser::_parse(const String &p_base_path) { // Resolve warning ignores Vector> warning_skips = tokenizer->get_warning_skips(); - bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize(); + bool warning_is_error = GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/treat_warnings_as_errors"); for (List::Element *E = warnings.front(); E;) { GDScriptWarning &w = E->get(); int skip_index = -1; diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index a7db010f3..f1845853a 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -223,7 +223,7 @@ String _get_activity_tag(const Ref &p_preset) { "android:resizeableActivity=\"%s\">\n", bool_to_string(p_preset->get("package/exclude_from_recents")), orientation, - bool_to_string(bool(GLOBAL_GET("display/window/size/resizable")))); + bool_to_string(GLOBAL_GET_CACHED(bool, "display/window/size/resizable"))); manifest_activity_text += " \n"; manifest_activity_text += " \n"; return manifest_activity_text; diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index 2b870133d..26a4f95e4 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -202,12 +202,12 @@ void MeshInstance::_resolve_skeleton_path() { bool MeshInstance::_is_global_software_skinning_enabled() { // Check if forced in project settings. - if (GLOBAL_GET("rendering/quality/skinning/force_software_skinning")) { + if (GLOBAL_GET_CACHED(bool, "rendering/quality/skinning/force_software_skinning")) { return true; } // Check if enabled in project settings. - if (!GLOBAL_GET("rendering/quality/skinning/software_skinning_fallback")) { + if (!GLOBAL_GET_CACHED(bool, "rendering/quality/skinning/software_skinning_fallback")) { return false; } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index d959b5fff..f443ca0fe 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -652,7 +652,7 @@ bool SceneTree::iteration(float p_time) { call_group_flags(GROUP_CALL_REALTIME, "_pg_process", "trigger_physics_process"); _notify_group_pause("physics_process_internal", Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); - if (GLOBAL_GET("physics/common/enable_pause_aware_picking")) { + if (GLOBAL_GET_CACHED(bool, "physics/common/enable_pause_aware_picking")) { call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_process_picking", true); } _notify_group_pause("physics_process", Node::NOTIFICATION_PHYSICS_PROCESS); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index b8e4cfb44..8fcb8822d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -174,14 +174,14 @@ class TooltipPanel : public PanelContainer { GDCLASS(TooltipPanel, PanelContainer); public: - TooltipPanel() {}; + TooltipPanel() {} }; class TooltipLabel : public Label { GDCLASS(TooltipLabel, Label); public: - TooltipLabel() {}; + TooltipLabel() {} }; ///////////////////////////////////// @@ -387,7 +387,7 @@ void Viewport::_notification(int p_what) { } } - if (!GLOBAL_GET("physics/common/enable_pause_aware_picking")) { + if (!GLOBAL_GET_CACHED(bool, "physics/common/enable_pause_aware_picking")) { _process_picking(false); } } break; diff --git a/scene/resources/material/spatial_material.cpp b/scene/resources/material/spatial_material.cpp index cf2523370..a91def7f0 100644 --- a/scene/resources/material/spatial_material.cpp +++ b/scene/resources/material/spatial_material.cpp @@ -2194,7 +2194,7 @@ SpatialMaterial::SpatialMaterial(bool p_orm) : flags[i] = false; } - force_vertex_shading = GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); + force_vertex_shading = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_vertex_shading"); diffuse_mode = DIFFUSE_BURLEY; specular_mode = SPECULAR_SCHLICK_GGX; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 4c07312e9..d08427ee6 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -185,7 +185,7 @@ void AudioStreamPlaybackMicrophone::start(float p_from_pos) { return; } - if (!GLOBAL_GET("audio/enable_audio_input")) { + if (!GLOBAL_GET_CACHED(bool, "audio/enable_audio_input")) { WARN_PRINT("Need to enable Project settings > Audio > Enable Audio Input option to use capturing."); return; } diff --git a/servers/rendering/portals/portal_occlusion_culler.cpp b/servers/rendering/portals/portal_occlusion_culler.cpp index 0db6f0928..e26ce8370 100644 --- a/servers/rendering/portals/portal_occlusion_culler.cpp +++ b/servers/rendering/portals/portal_occlusion_culler.cpp @@ -249,7 +249,7 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c // Bodge to keep settings up to date, until the project settings PR is merged #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && ((Engine::get_singleton()->get_frames_drawn() % 16) == 0)) { - _max_polys = GLOBAL_GET("rendering/misc/occlusion_culling/max_active_polygons"); + _max_polys = GLOBAL_GET_CACHED(int32_t, "rendering/misc/occlusion_culling/max_active_polygons"); } #endif _num_spheres = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index eee3ee95a..9f6cdca54 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -870,7 +870,7 @@ uint32_t RenderingServer::mesh_surface_get_format_stride(uint32_t p_format, int } void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t *r_strides) const { - bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); int attributes_base_offset = 0; int attributes_stride = 0; @@ -1248,7 +1248,7 @@ bool RenderingServer::_mesh_find_format(RS::PrimitiveType p_primitive, const Arr } uint32_t RenderingServer::mesh_find_format_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) { - bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_compress_format & rS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[RS::ARRAY_MAX]; @@ -1268,7 +1268,7 @@ uint32_t RenderingServer::mesh_find_format_from_arrays(PrimitiveType p_primitive } void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) { - bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[RS::ARRAY_MAX]; @@ -1338,7 +1338,7 @@ void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_p } Array RenderingServer::_get_array_from_surface(uint32_t p_format, PoolVector p_vertex_data, int p_vertex_len, PoolVector p_index_data, int p_index_len) const { - bool use_split_stream = GLOBAL_GET("rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & rS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[ARRAY_MAX]; uint32_t strides[RS::ARRAY_MAX]; From 9f85d89056a4260db0b5d07120f1b3be0bbdd099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Sun, 13 Mar 2022 12:43:27 +0100 Subject: [PATCH 06/16] Drop mouse focus and over when gui input is globally disabled Since some porjects may be relying on the former behavior, this is opt-in via a new project setting, disabled by default, but enabled for new projects, since it's the new standard behavior (and the only one in 4.0). --- doc/classes/ProjectSettings.xml | 6 ++++++ editor/project_manager.cpp | 1 + main/main.cpp | 1 + scene/main/viewport.cpp | 23 +++++++++++++++++------ scene/main/viewport.h | 1 + 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index ef3138756..cb4ba36f7 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -743,6 +743,12 @@ Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden. + + If enabled, the moment [member Viewport.gui_disable_input] is set to [code]false[/code] to disable GUI input in a viewport, current mouse over and mouse focus will be dropped. + That behavior helps to keep a robust GUI state, with no surprises when input is resumed regardless what has happened in the meantime. + If disabled, the legacy behavior is used, which consists in just not doing anything besides the GUI input disable itself. + [b]Note:[/b] This is set to [code]true[/code] by default for new projects and is the recommended setting. + If [code]true[/code], swaps OK and Cancel buttons in dialogs on Windows and UWP to follow interface conventions. diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 7e522de37..f4f7df737 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -512,6 +512,7 @@ private: initial_settings["application/config/icon"] = "res://icon.png"; initial_settings["rendering/environment/default_environment"] = "res://default_env.tres"; initial_settings["physics/common/enable_pause_aware_picking"] = true; + initial_settings["gui/common/drop_mouse_on_gui_input_disabled"] = true; if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("project.pandemonium"), initial_settings, Vector(), false) != OK) { set_message(TTR("Couldn't create project.pandemonium in project path."), MESSAGE_ERROR); diff --git a/main/main.cpp b/main/main.cpp index 56c72fac0..32f8528d6 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1359,6 +1359,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0)); ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", PROPERTY_HINT_RANGE, "0,1000,1")); GLOBAL_DEF("physics/common/enable_pause_aware_picking", false); + GLOBAL_DEF("gui/common/drop_mouse_on_gui_input_disabled", false); GLOBAL_DEF("debug/settings/stdout/print_fps", false); GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8fcb8822d..b37787ff9 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2005,9 +2005,7 @@ void Viewport::_gui_input_event(Ref p_event) { } if (gui.mouse_focus_mask == 0 && over != gui.mouse_over) { - if (gui.mouse_over) { - _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT); - } + _drop_mouse_over(); _gui_cancel_tooltip(); @@ -2126,9 +2124,7 @@ void Viewport::_gui_input_event(Ref p_event) { } if (over != gui.mouse_over) { - if (gui.mouse_over) { - _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT); - } + _drop_mouse_over(); _gui_cancel_tooltip(); @@ -2689,6 +2685,13 @@ void Viewport::_drop_mouse_focus() { } } +void Viewport::_drop_mouse_over() { + if (gui.mouse_over) { + _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT); + gui.mouse_over = nullptr; + } +} + void Viewport::_drop_physics_mouseover(bool p_paused_only) { physics_has_last_mousepos = false; @@ -2908,6 +2911,14 @@ bool Viewport::gui_has_modal_stack() const { } void Viewport::set_disable_input(bool p_disable) { + if (p_disable == disable_input) { + return; + } + if (p_disable && GLOBAL_GET("gui/common/drop_mouse_on_gui_input_disabled")) { + _drop_mouse_focus(); + _drop_mouse_over(); + _gui_cancel_tooltip(); + } disable_input = p_disable; } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index a8149bb31..ff2134f14 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -575,6 +575,7 @@ private: void _canvas_layer_remove(CanvasLayer *p_canvas_layer); void _drop_mouse_focus(); + void _drop_mouse_over(); void _drop_physics_mouseover(bool p_paused_only = false); void _update_canvas_items(Node *p_node); From ae19a49a1eef2a17449d6560e5fc2258db57e233 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 08:01:29 +0200 Subject: [PATCH 07/16] Make drop_mouse_on_gui_input_disabled GLOBAL_GET cached. --- scene/main/viewport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index b37787ff9..29724ff0c 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2914,7 +2914,7 @@ void Viewport::set_disable_input(bool p_disable) { if (p_disable == disable_input) { return; } - if (p_disable && GLOBAL_GET("gui/common/drop_mouse_on_gui_input_disabled")) { + if (p_disable && GLOBAL_GET_CACHED(bool, "gui/common/drop_mouse_on_gui_input_disabled")) { _drop_mouse_focus(); _drop_mouse_over(); _gui_cancel_tooltip(); From dedeec94921f2685c94bc5e523206ed216edc618 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Thu, 19 Dec 2024 12:44:33 +0000 Subject: [PATCH 08/16] Simplified `ObjectDB::get_instance()` casting Reduces boiler plate by templating `get_instance()` for the cast type, while remaining backward compatible to the existing functionality. --- core/object/object.cpp | 1 + core/object/object.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/core/object/object.cpp b/core/object/object.cpp index bd4ff3d7b..77c0ad41a 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -2043,6 +2043,7 @@ void ObjectDB::remove_instance(Object *p_object) { rw_lock.write_unlock(); } + Object *ObjectDB::get_instance(ObjectID p_instance_id) { rw_lock.read_lock(); Object **obj = instances.getptr(p_instance_id); diff --git a/core/object/object.h b/core/object/object.h index 56213bc98..c7001aff9 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -856,6 +856,12 @@ public: typedef void (*DebugFunc)(Object *p_obj); static Object *get_instance(ObjectID p_instance_id); + + template + static T *get_instance(ObjectID p_instance_id) { + return Object::cast_to(get_instance(p_instance_id)); + } + static void debug_objects(DebugFunc p_func); static int get_object_count(); From 8a735a3c02e8134730a68df6214841e73cdb8903 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Tue, 4 Mar 2025 10:32:07 +0000 Subject: [PATCH 09/16] Physics Interpolation - Move 3D FTI to `SceneTree` Moves 3D interpolation from `VisualServer` to the client code (`SceneTree`). Complete rework of 3D physics interpolation, but using the same user API. --- core/containers/local_vector.h | 9 + doc/classes/RenderingServer.xml | 16 - doc/classes/VehicleWheel.xml | 1 + main/tests/test_expression.h | 1 + .../doc_classes/BoneAttachment.xml | 1 + modules/skeleton_3d/nodes/bone_attachment.cpp | 1 + modules/skeleton_3d/nodes/skeleton_ik.cpp | 2 +- scene/3d/camera.cpp | 127 +++----- scene/3d/camera.h | 25 +- scene/3d/vehicle_body.cpp | 4 +- scene/3d/visual_instance.cpp | 45 ++- scene/3d/visual_instance.h | 2 +- scene/main/scene_tree.cpp | 19 ++ scene/main/scene_tree.h | 4 + scene/main/scene_tree_fti.cpp | 288 ++++++++++++++++++ scene/main/scene_tree_fti.h | 112 +++++++ scene/main/spatial.cpp | 64 +++- scene/main/spatial.h | 39 ++- servers/rendering/rasterizer.h | 24 -- servers/rendering/rendering_server_raster.h | 2 - servers/rendering/rendering_server_scene.cpp | 199 +----------- servers/rendering/rendering_server_scene.h | 8 - servers/rendering/rendering_server_wrap_mt.h | 2 - servers/rendering_server.cpp | 4 +- servers/rendering_server.h | 2 - 25 files changed, 601 insertions(+), 400 deletions(-) create mode 100644 scene/main/scene_tree_fti.cpp create mode 100644 scene/main/scene_tree_fti.h diff --git a/core/containers/local_vector.h b/core/containers/local_vector.h index 16f891b9f..63efc9487 100644 --- a/core/containers/local_vector.h +++ b/core/containers/local_vector.h @@ -105,6 +105,15 @@ public: return false; } + bool erase_unordered(const T &p_val) { + int64_t idx = find(p_val); + if (idx >= 0) { + remove_unordered(idx); + return true; + } + return false; + } + U erase_multiple_unordered(const T &p_val) { U from = 0; U count = 0; diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index b105dda8f..90ea8076f 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1476,14 +1476,6 @@ Sets a material that will override the material for all surfaces on the mesh associated with this instance. Equivalent to [member GeometryInstance.material_override]. - - - - - Prevents physics interpolation for the current physics tick. - This is useful when moving an instance to a new location, to give an instantaneous change rather than interpolation from the previous location. - - @@ -1525,14 +1517,6 @@ Sets a margin to increase the size of the AABB when culling objects from the view frustum. This allows you to avoid culling objects that fall outside the view frustum. Equivalent to [member GeometryInstance.extra_cull_margin]. - - - - - - Turns on and off physics interpolation for the instance. - - diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml index cc3b01551..2a3e8c4ce 100644 --- a/doc/classes/VehicleWheel.xml +++ b/doc/classes/VehicleWheel.xml @@ -52,6 +52,7 @@ [b]Note:[/b] The simulation does not take the effect of gears into account, you will need to add logic for this if you wish to simulate gears. A negative value will result in the wheel reversing. + The steering angle for the wheel. Setting this to a non-zero value will result in the vehicle turning when it's moving. diff --git a/main/tests/test_expression.h b/main/tests/test_expression.h index a240dfe67..431231014 100644 --- a/main/tests/test_expression.h +++ b/main/tests/test_expression.h @@ -39,3 +39,4 @@ MainLoop *test(); } #endif // TEST_EXPRESSION_H + diff --git a/modules/skeleton_3d/doc_classes/BoneAttachment.xml b/modules/skeleton_3d/doc_classes/BoneAttachment.xml index 54abd81cb..d79a6258c 100644 --- a/modules/skeleton_3d/doc_classes/BoneAttachment.xml +++ b/modules/skeleton_3d/doc_classes/BoneAttachment.xml @@ -77,6 +77,7 @@ The name of the attached bone. + diff --git a/modules/skeleton_3d/nodes/bone_attachment.cpp b/modules/skeleton_3d/nodes/bone_attachment.cpp index 33e75fa40..73b56b475 100644 --- a/modules/skeleton_3d/nodes/bone_attachment.cpp +++ b/modules/skeleton_3d/nodes/bone_attachment.cpp @@ -187,6 +187,7 @@ String BoneAttachment::get_configuration_warning() const { } BoneAttachment::BoneAttachment() { + set_physics_interpolation_mode(PHYSICS_INTERPOLATION_MODE_OFF); bound = false; bone_idx = -1; override_pose = false; diff --git a/modules/skeleton_3d/nodes/skeleton_ik.cpp b/modules/skeleton_3d/nodes/skeleton_ik.cpp index b5b0c27d1..fae4cce92 100644 --- a/modules/skeleton_3d/nodes/skeleton_ik.cpp +++ b/modules/skeleton_3d/nodes/skeleton_ik.cpp @@ -279,7 +279,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, false); Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin; - make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta); + make_goal(p_task, p_task->skeleton->get_global_transform_interpolated().affine_inverse(), blending_delta); if (p_use_magnet && p_task->chain.middle_chain_item) { p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 54bfe2108..80155a6d3 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -50,6 +50,13 @@ void Camera::_request_camera_update() { _update_camera(); } +void Camera::fti_update_servers() { + if (camera.is_valid()) { + Transform tr = _get_adjusted_camera_transform(_get_cached_global_transform_interpolated()); + VisualServer::get_singleton()->camera_set_transform(camera, tr); + } +} + void Camera::_update_camera_mode() { force_change = true; switch (mode) { @@ -90,12 +97,8 @@ void Camera::_update_camera() { if (!is_physics_interpolated_and_enabled()) { RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); } else { - // Ideally we shouldn't be moving a physics interpolated camera within a frame, - // because it will break smooth interpolation, but it may occur on e.g. level load. - if (!Engine::get_singleton()->is_in_physics_frame() && camera.is_valid()) { - _physics_interpolation_ensure_transform_calculated(true); - RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); - } + // Force a refresh next frame. + fti_notify_node_changed(); } // here goes listener stuff @@ -119,40 +122,6 @@ void Camera::_physics_interpolated_changed() { _update_process_mode(); } -void Camera::_physics_interpolation_ensure_data_flipped() { - // The curr -> previous update can either occur - // on the INTERNAL_PHYSICS_PROCESS OR - // on NOTIFICATION_TRANSFORM_CHANGED, - // if NOTIFICATION_TRANSFORM_CHANGED takes place - // earlier than INTERNAL_PHYSICS_PROCESS on a tick. - // This is to ensure that the data keeps flowing, but the new data - // doesn't overwrite before prev has been set. - - // Keep the data flowing. - uint64_t tick = Engine::get_singleton()->get_physics_frames(); - if (_interpolation_data.last_update_physics_tick != tick) { - _interpolation_data.xform_prev = _interpolation_data.xform_curr; - _interpolation_data.last_update_physics_tick = tick; - physics_interpolation_flip_data(); - } -} - -void Camera::_physics_interpolation_ensure_transform_calculated(bool p_force) const { - DEV_CHECK_ONCE(!Engine::get_singleton()->is_in_physics_frame()); - - InterpolationData &id = _interpolation_data; - uint64_t frame = Engine::get_singleton()->get_frames_drawn(); - - if (id.last_update_frame != frame || p_force) { - id.last_update_frame = frame; - - TransformInterpolator::interpolate_transform(id.xform_prev, id.xform_curr, id.xform_interpolated, Engine::get_singleton()->get_physics_interpolation_fraction()); - - Transform &tr = id.camera_xform_interpolated; - tr = _get_adjusted_camera_transform(id.xform_interpolated); - } -} - void Camera::set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal) { _desired_process_internal = p_process_internal; _desired_physics_process_internal = p_physics_process_internal; @@ -160,17 +129,8 @@ void Camera::set_desired_process_modes(bool p_process_internal, bool p_physics_p } void Camera::_update_process_mode() { - bool process = _desired_process_internal; - bool physics_process = _desired_physics_process_internal; - - if (is_physics_interpolated_and_enabled()) { - if (is_current()) { - process = true; - physics_process = true; - } - } - set_process_internal(process); - set_physics_process_internal(physics_process); + set_process_internal(_desired_process_internal); + set_physics_process_internal(_desired_physics_process_internal); } void Camera::_notification(int p_what) { @@ -187,50 +147,21 @@ void Camera::_notification(int p_what) { world->_camera_set(this); } } break; - case NOTIFICATION_INTERNAL_PROCESS: { - if (is_physics_interpolated_and_enabled() && camera.is_valid()) { - _physics_interpolation_ensure_transform_calculated(); - -#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION - print_line("\t\tinterpolated Camera: " + rtos(_interpolation_data.xform_interpolated.origin.x) + "\t( prev " + rtos(_interpolation_data.xform_prev.origin.x) + ", curr " + rtos(_interpolation_data.xform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); -#endif - - RenderingServer::get_singleton()->camera_set_transform(camera, _interpolation_data.camera_xform_interpolated); - } - } break; - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - if (is_physics_interpolated_and_enabled()) { - _physics_interpolation_ensure_data_flipped(); - _interpolation_data.xform_curr = get_global_transform(); - } - } break; case NOTIFICATION_TRANSFORM_CHANGED: { - if (is_physics_interpolated_and_enabled()) { - _physics_interpolation_ensure_data_flipped(); - _interpolation_data.xform_curr = get_global_transform(); #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (is_physics_interpolated_and_enabled()) { if (!Engine::get_singleton()->is_in_physics_frame()) { PHYSICS_INTERPOLATION_NODE_WARNING(get_instance_id(), "Interpolated Camera triggered from outside physics process"); } -#endif } +#endif _request_camera_update(); if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { velocity_tracker->update_position(get_global_transform().origin); } - // Allow auto-reset when first adding to the tree, as a convenience. - if (_is_physics_interpolation_reset_requested() && is_inside_tree()) { - _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); - _set_physics_interpolation_reset_requested(false); - } - } break; case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { - if (is_inside_tree()) { - _interpolation_data.xform_curr = get_global_transform(); - _interpolation_data.xform_prev = _interpolation_data.xform_curr; - _update_process_mode(); - } + _update_process_mode(); } break; case NOTIFICATION_EXIT_WORLD: { if (!get_tree()->is_node_being_edited(this)) { @@ -273,8 +204,7 @@ Transform Camera::_get_adjusted_camera_transform(const Transform &p_xform) const Transform Camera::get_camera_transform() const { if (is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame()) { - _physics_interpolation_ensure_transform_calculated(); - return _interpolation_data.camera_xform_interpolated; + return _get_adjusted_camera_transform(_get_cached_global_transform_interpolated()); } return _get_adjusted_camera_transform(get_global_transform()); @@ -864,9 +794,16 @@ float ClippedCamera::get_margin() const { return margin; } void ClippedCamera::set_process_mode(ProcessMode p_mode) { - if (is_physics_interpolated_and_enabled() && p_mode == CLIP_PROCESS_IDLE) { - p_mode = CLIP_PROCESS_PHYSICS; - WARN_PRINT_ONCE("[Physics interpolation] Forcing ClippedCamera to PROCESS_PHYSICS mode."); + if (is_physics_interpolated_and_enabled()) { + if (p_mode == CLIP_PROCESS_IDLE) { + p_mode = CLIP_PROCESS_PHYSICS; + WARN_PRINT_ONCE("[Physics interpolation] Forcing ClippedCamera to PROCESS_PHYSICS mode."); + } + + process_mode = p_mode; + + set_desired_process_modes(false, true); + return; } if (process_mode == p_mode) { @@ -880,8 +817,11 @@ ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const { return process_mode; } -void ClippedCamera::physics_interpolation_flip_data() { +void ClippedCamera::fti_pump() { _interpolation_data.clip_offset_prev = _interpolation_data.clip_offset_curr; + + // Must call the base class. + Spatial::fti_pump(); } void ClippedCamera::_physics_interpolated_changed() { @@ -898,6 +838,11 @@ Transform ClippedCamera::_get_adjusted_camera_transform(const Transform &p_xform return t; } +void ClippedCamera::fti_update_servers() { + clip_offset = ((_interpolation_data.clip_offset_curr - _interpolation_data.clip_offset_prev) * Engine::get_singleton()->get_physics_interpolation_fraction()) + _interpolation_data.clip_offset_prev; + Camera::fti_update_servers(); +} + void ClippedCamera::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { // Switch process mode to physics if we are turning on interpolation. @@ -965,10 +910,6 @@ void ClippedCamera::_notification(int p_what) { _update_camera(); } - if (is_physics_interpolated_and_enabled() && (p_what == NOTIFICATION_INTERNAL_PROCESS)) { - clip_offset = ((_interpolation_data.clip_offset_curr - _interpolation_data.clip_offset_prev) * Engine::get_singleton()->get_physics_interpolation_fraction()) + _interpolation_data.clip_offset_prev; - } - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { update_gizmos(); } diff --git a/scene/3d/camera.h b/scene/3d/camera.h index e08a8bc8c..896edc7a3 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -96,26 +96,10 @@ private: Ref velocity_tracker; bool affect_lod = true; - /////////////////////////////////////////////////////// - // INTERPOLATION FUNCTIONS - void _physics_interpolation_ensure_transform_calculated(bool p_force = false) const; - void _physics_interpolation_ensure_data_flipped(); - - // These can be set by derived Cameras, - // if they wish to do processing (while still - // allowing physics interpolation to function). + // These can be set by derived Cameras. bool _desired_process_internal = false; bool _desired_physics_process_internal = false; - mutable struct InterpolationData { - Transform xform_curr; - Transform xform_prev; - Transform xform_interpolated; - Transform camera_xform_interpolated; // After modification according to camera type. - uint32_t last_update_physics_tick = 0; - uint32_t last_update_frame = UINT32_MAX; - } _interpolation_data; - void _update_process_mode(); protected: @@ -123,9 +107,6 @@ protected: // This is because physics interpolation may need to request process modes additionally. void set_desired_process_modes(bool p_process_internal, bool p_physics_process_internal); - // Opportunity for derived classes to interpolate extra attributes. - virtual void physics_interpolation_flip_data() {} - virtual void _physics_interpolated_changed(); virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const; /////////////////////////////////////////////////////// @@ -133,6 +114,7 @@ protected: void _update_camera(); virtual void _request_camera_update(); void _update_camera_mode(); + virtual void fti_update_servers(); void _notification(int p_what); virtual void _validate_property(PropertyInfo &p_property) const; @@ -253,8 +235,9 @@ private: protected: virtual Transform _get_adjusted_camera_transform(const Transform &p_xform) const; - virtual void physics_interpolation_flip_data(); + virtual void fti_pump(); virtual void _physics_interpolated_changed(); + virtual void fti_update_servers(); /////////////////////////////////////////////////////// void _notification(int p_what); diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index c762d8215..d0c7f2dc3 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -47,7 +47,7 @@ public: real_t getDiagonal() const { return m_Adiag; } - btVehicleJacobianEntry() {}; + btVehicleJacobianEntry() {} //constraint between two different rigidbodies btVehicleJacobianEntry( const Basis &world2A, @@ -369,6 +369,8 @@ VehicleWheel::VehicleWheel() { m_raycastInfo.m_suspensionLength = 0.0; body = nullptr; + + set_physics_interpolation_mode(PHYSICS_INTERPOLATION_MODE_OFF); } void VehicleBody::_update_wheel_transform(VehicleWheel &wheel, PhysicsDirectBodyState *s) { diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 8e77ff4f3..937b8874c 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -85,6 +85,12 @@ void VisualInstance::set_instance_use_identity_transform(bool p_enable) { } } +void VisualInstance::fti_update_servers() { + if (!_is_using_identity_transform()) { + VisualServer::get_singleton()->instance_set_transform(get_instance(), _get_cached_global_transform_interpolated()); + } +} + void VisualInstance::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { @@ -100,33 +106,26 @@ void VisualInstance::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - if (_is_vi_visible() || is_physics_interpolated_and_enabled()) { + if (_is_vi_visible()) { if (!_is_using_identity_transform()) { - RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); - - // For instance when first adding to the tree, when the previous transform is - // unset, to prevent streaking from the origin. - if (_is_physics_interpolation_reset_requested() && is_physics_interpolated_and_enabled() && is_inside_tree()) { - if (_is_vi_visible()) { - _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + // Physics interpolated VIs don't need to send their transform immediately after setting, + // indeed it is counterproductive, because the interpolated transform will be sent + // to the VisualServer immediately prior to rendering. + if (!is_physics_interpolated_and_enabled()) { + RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); + } else { + // For instance when first adding to the tree, when the previous transform is + // unset, to prevent streaking from the origin. + if (_is_physics_interpolation_reset_requested() && is_inside_tree()) { + if (_is_vi_visible()) { + _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + } + _set_physics_interpolation_reset_requested(false); } - _set_physics_interpolation_reset_requested(false); } } } } break; - case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: { - if (_is_vi_visible() && is_physics_interpolated() && is_inside_tree()) { - // We must ensure the VisualServer transform is up to date before resetting. - // This is because NOTIFICATION_TRANSFORM_CHANGED is deferred, - // and cannot be relied to be called in order before NOTIFICATION_RESET_PHYSICS_INTERPOLATION. - if (!_is_using_identity_transform()) { - RenderingServer::get_singleton()->instance_set_transform(instance, get_global_transform()); - } - - RenderingServer::get_singleton()->instance_reset_physics_interpolation(instance); - } - } break; case NOTIFICATION_EXIT_WORLD: { RenderingServer::get_singleton()->instance_set_scenario(instance, RID()); RenderingServer::get_singleton()->instance_attach_skeleton(instance, RID()); @@ -142,10 +141,6 @@ void VisualInstance::_notification(int p_what) { } } -void VisualInstance::_physics_interpolated_changed() { - RenderingServer::get_singleton()->instance_set_interpolated(instance, is_physics_interpolated()); -} - RID VisualInstance::get_instance() const { return instance; } diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 0c14edc10..6a1199263 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -55,8 +55,8 @@ class VisualInstance : public CullInstance { protected: void _update_visibility(); virtual void _refresh_portal_mode(); - virtual void _physics_interpolated_changed(); void set_instance_use_identity_transform(bool p_enable); + virtual void fti_update_servers(); void _notification(int p_what); static void _bind_methods(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index f443ca0fe..6c35a9f06 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -592,6 +592,8 @@ void SceneTree::set_physics_interpolation_enabled(bool p_enabled) { RenderingServer::get_singleton()->set_physics_interpolation_enabled(p_enabled); + get_scene_tree_fti().set_enabled(get_root(), p_enabled); + // Perform an auto reset on the root node for convenience for the user. if (root) { root->reset_physics_interpolation(); @@ -617,6 +619,7 @@ void SceneTree::iteration_prepare() { // Make sure any pending transforms from the last tick / frame // are flushed before pumping the interpolation prev and currents. flush_transform_notifications(); + get_scene_tree_fti().tick_update(); RenderingServer::get_singleton()->tick(); } } @@ -689,6 +692,17 @@ bool SceneTree::idle(float p_time) { //print_line("node count: "+itos(get_node_count())); //print_line("TEXTURE RAM: "+itos(RS::get_singleton()->get_render_info(RS::INFO_TEXTURE_MEM_USED))); + // First pass of scene tree fixed timestep interpolation. + if (get_scene_tree_fti().is_enabled()) { + // Special, we need to ensure RenderingServer is up to date + // with *all* the pending xforms *before* updating it during + // the FTI update. + // If this is not done, we can end up with a deferred `set_transform()` + // overwriting the interpolated xform in the server. + flush_transform_notifications(); + get_scene_tree_fti().frame_update(get_root(), true); + } + root_lock++; if (MainLoop::idle(p_time)) { @@ -805,6 +819,11 @@ bool SceneTree::idle(float p_time) { #endif + // Second pass of scene tree fixed timestep interpolation. + // ToDo: Possibly needs another flush_transform_notifications here + // depending on whether there are side effects to _call_idle_callbacks(). + get_scene_tree_fti().frame_update(get_root(), false); + RenderingServer::get_singleton()->pre_draw(true); return _quit; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 70d34d591..0dfc764ac 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -36,6 +36,7 @@ #include "core/io/multiplayer_api.h" #include "core/os/main_loop.h" #include "core/os/thread_safe.h" +#include "scene/main/scene_tree_fti.h" class PackedScene; class Node; @@ -160,6 +161,7 @@ private: StretchAspect stretch_aspect; Size2i stretch_min; real_t stretch_scale; + SceneTreeFTI scene_tree_fti; void _update_font_oversampling(float p_ratio); void _update_root_rect(); @@ -468,6 +470,8 @@ public: void client_physics_interpolation_add_spatial(SelfList *p_elem); void client_physics_interpolation_remove_spatial(SelfList *p_elem); + SceneTreeFTI &get_scene_tree_fti() { return scene_tree_fti; } + static void add_idle_callback(IdleCallback p_callback); SceneTree(); ~SceneTree(); diff --git a/scene/main/scene_tree_fti.cpp b/scene/main/scene_tree_fti.cpp new file mode 100644 index 000000000..2500c4da5 --- /dev/null +++ b/scene/main/scene_tree_fti.cpp @@ -0,0 +1,288 @@ +/**************************************************************************/ +/* scene_tree_fti.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef _3D_DISABLED + +#include "scene_tree_fti.h" +#include "core/config/engine.h" +#include "core/error/error_macros.h" +#include "core/math/transform_interpolator.h" +#include "core/os/os.h" +#include "scene/main/spatial.h" +#include "scene/3d/visual_instance.h" + +void SceneTreeFTI::_reset_flags(Node *p_node) { + Spatial *s = Object::cast_to(p_node); + + if (s) { + s->data.fti_on_frame_list = false; + s->data.fti_on_tick_list = false; + + // In most cases the later NOTIFICATION_RESET_PHYSICS_INTERPOLATION + // will reset this, but this should help cover hidden nodes. + s->data.local_transform_prev = s->data.local_transform; + } + + for (int n = 0; n < p_node->get_child_count(); n++) { + _reset_flags(p_node->get_child(n)); + } +} + +void SceneTreeFTI::set_enabled(Node *p_root, bool p_enabled) { + if (data.enabled == p_enabled) { + return; + } + + data.spatial_tick_list[0].clear(); + data.spatial_tick_list[1].clear(); + + // Spatial flags must be reset. + if (p_root) { + _reset_flags(p_root); + } + + data.enabled = p_enabled; +} + +void SceneTreeFTI::tick_update() { + if (!data.enabled) { + return; + } + + uint32_t curr_mirror = data.mirror; + uint32_t prev_mirror = curr_mirror ? 0 : 1; + + LocalVector &curr = data.spatial_tick_list[curr_mirror]; + LocalVector &prev = data.spatial_tick_list[prev_mirror]; + + // First detect on the previous list but not on this tick list. + for (uint32_t n = 0; n < prev.size(); n++) { + Spatial *s = prev[n]; + if (!s->data.fti_on_tick_list) { + // Needs a reset so jittering will stop. + s->fti_pump(); + + // This may not get updated so set it to the same as global xform. + // TODO: double check this is the best value. + s->data.global_transform_interpolated = s->get_global_transform(); + + // Remove from interpolation list. + if (s->data.fti_on_frame_list) { + s->data.fti_on_frame_list = false; + } + } + } + + // Now pump all on the current list. + for (uint32_t n = 0; n < curr.size(); n++) { + Spatial *s = curr[n]; + + // Reset, needs to be marked each tick. + s->data.fti_on_tick_list = false; + + // Pump. + s->fti_pump(); + } + + // Clear previous list and flip. + prev.clear(); + data.mirror = prev_mirror; +} + +void SceneTreeFTI::_spatial_notify_set_transform(Spatial &r_spatial) { + // This may be checked by the calling routine already, + // but needs to be double checked for custom SceneTrees. + if (!data.enabled || !r_spatial.is_physics_interpolated()) { + return; + } + + if (!r_spatial.data.fti_on_tick_list) { + r_spatial.data.fti_on_tick_list = true; + data.spatial_tick_list[data.mirror].push_back(&r_spatial); + } + + if (!r_spatial.data.fti_on_frame_list) { + r_spatial.data.fti_on_frame_list = true; + } +} + +void SceneTreeFTI::spatial_notify_delete(Spatial *p_spatial) { + if (!data.enabled) { + return; + } + + if (p_spatial->data.fti_on_frame_list) { + p_spatial->data.fti_on_frame_list = false; + } + + // This can potentially be optimized for large scenes with large churn, + // as it will be doing a linear search through the lists. + data.spatial_tick_list[0].erase_unordered(p_spatial); + data.spatial_tick_list[1].erase_unordered(p_spatial); +} + +void SceneTreeFTI::_update_dirty_spatials(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform *p_parent_global_xform, int p_depth) { + Spatial *s = Object::cast_to(p_node); + + // Don't recurse into hidden branches. + if (s && !s->is_visible()) { + // NOTE : If we change from recursing entire tree, we should do an is_visible_in_tree() + // check for the first of the branch. + return; + } + + // Not a Spatial. + // Could be e.g. a viewport or something + // so we should still recurse to children. + if (!s) { + for (int n = 0; n < p_node->get_child_count(); n++) { + _update_dirty_spatials(p_node->get_child(n), p_current_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1); + } + return; + } + + // Start the active interpolation chain from here onwards + // as we recurse further into the SceneTree. + // Once we hit an active (interpolated) node, we have to fully + // process all ancestors because their xform will also change. + // Anything not moving (inactive) higher in the tree need not be processed. + if (!p_active) { + if (data.frame_start) { + // On the frame start, activate whenever we hit something that requests interpolation. + if (s->data.fti_on_frame_list) { + p_active = true; + } + } else { + // On the frame end, we want to re-interpolate *anything* that has moved + // since the frame start. + if (s->data.dirty & Spatial::DIRTY_GLOBAL_INTERPOLATED) { + p_active = true; + } + } + } + + if (data.frame_start) { + // Mark on the Spatial whether we have set global_transform_interp. + // This can later be used when calling `get_global_transform_interpolated()` + // to know which xform to return. + s->data.fti_global_xform_interp_set = p_active; + } + + if (p_active) { +#if 0 + bool dirty = s->data.dirty & Spatial::DIRTY_GLOBAL_INTERP; + + if (data.debug) { + String sz; + for (int n = 0; n < p_depth; n++) { + sz += "\t"; + } + print_line(sz + p_node->get_name() + (dirty ? " DIRTY" : "")); + } +#endif + + // First calculate our local xform. + // This will either use interpolation, or just use the current local if not interpolated. + Transform local_interp; + if (s->is_physics_interpolated()) { + TransformInterpolator::interpolate_transform(s->data.local_transform_prev, s->data.local_transform, local_interp, p_interpolation_fraction); + } else { + local_interp = s->data.local_transform; + } + + // Concatenate parent xform. + if (!s->is_set_as_toplevel()) { + if (p_parent_global_xform) { + s->data.global_transform_interpolated = (*p_parent_global_xform) * local_interp; + } else { + const Spatial *parent = s->get_parent_spatial(); + + if (parent) { + const Transform &parent_glob = parent->data.fti_global_xform_interp_set ? parent->data.global_transform_interpolated : parent->data.global_transform; + s->data.global_transform_interpolated = parent_glob * local_interp; + } else { + s->data.global_transform_interpolated = local_interp; + } + } + } else { + s->data.global_transform_interpolated = local_interp; + } + + // Upload to VisualServer the interpolated global xform. + s->fti_update_servers(); + + } // if active. + + // Remove the dirty interp flag from EVERYTHING as we go. + s->data.dirty &= ~Spatial::DIRTY_GLOBAL_INTERPOLATED; + + // Recurse to children. + for (int n = 0; n < p_node->get_child_count(); n++) { + _update_dirty_spatials(p_node->get_child(n), p_current_frame, p_interpolation_fraction, p_active, s->data.fti_global_xform_interp_set ? &s->data.global_transform_interpolated : &s->data.global_transform, p_depth + 1); + } +} + +void SceneTreeFTI::frame_update(Node *p_root, bool p_frame_start) { + if (!data.enabled || !p_root) { + return; + } + + data.frame_start = p_frame_start; + + float f = Engine::get_singleton()->get_physics_interpolation_fraction(); + uint32_t frame = Engine::get_singleton()->get_frames_drawn(); + +// #define SCENE_TREE_FTI_TAKE_TIMINGS +#ifdef SCENE_TREE_FTI_TAKE_TIMINGS + uint64_t before = OS::get_singleton()->get_ticks_usec(); +#endif + + if (data.debug) { + print_line(String("\nScene: ") + (data.frame_start ? "start" : "end") + "\n"); + } + + // Probably not the most optimal approach as we traverse the entire SceneTree + // but simple and foolproof. + // Can be optimized later. + _update_dirty_spatials(p_root, frame, f, false); + + if (!p_frame_start && data.debug) { + data.debug = false; + } + +#ifdef SCENE_TREE_FTI_TAKE_TIMINGS + uint64_t after = OS::get_singleton()->get_ticks_usec(); + if ((Engine::get_singleton()->get_frames_drawn() % 60) == 0) { + print_line("Took " + itos(after - before) + " usec " + (data.frame_start ? "start" : "end")); + } +#endif +} + +#endif // ndef _3D_DISABLED diff --git a/scene/main/scene_tree_fti.h b/scene/main/scene_tree_fti.h new file mode 100644 index 000000000..c283d8ce8 --- /dev/null +++ b/scene/main/scene_tree_fti.h @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* scene_tree_fti.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef SCENE_TREE_FTI_H +#define SCENE_TREE_FTI_H + +#include "core/containers/local_vector.h" +#include "core/os/mutex.h" + +class Spatial; +class Node; +class Transform; + +#ifdef _3D_DISABLED +// Stubs +class SceneTreeFTI { +public: + void frame_update(Node *p_root, bool p_frame_start) {} + void tick_update() {} + void set_enabled(Node *p_root, bool p_enabled) {} + bool is_enabled() const { return false; } + + void spatial_notify_set_transform(Spatial &r_spatial) {} + void spatial_notify_delete(Spatial *p_spatial) {} +}; +#else + +// Important. +// This class uses raw pointers, so it is essential that on deletion, this class is notified +// so that any references can be cleared up to prevent dangling pointer access. + +// This class can be used from a custom SceneTree. + +// Note we could potentially make SceneTreeFTI static / global to avoid the lookup through scene tree, +// but this covers the custom case of multiple scene trees. + +// This class is not thread safe, but can be made thread safe easily with a mutex as in the 4.x version. + +class SceneTreeFTI { + struct Data { + // Prev / Curr lists of spatials having local xforms pumped. + LocalVector spatial_tick_list[2]; + uint32_t mirror = 0; + + bool enabled = false; + + // Whether we are in physics ticks, or in a frame. + bool in_frame = false; + + // Updating at the start of the frame, or the end on second pass. + bool frame_start = true; + + bool debug = false; + } data; + + void _update_dirty_spatials(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform *p_parent_global_xform = nullptr, int p_depth = 0); + void _reset_flags(Node *p_node); + void _spatial_notify_set_transform(Spatial &r_spatial); + +public: + // Hottest function, allow inlining the data.enabled check. + void spatial_notify_set_transform(Spatial &r_spatial) { + if (!data.enabled) { + return; + } + _spatial_notify_set_transform(r_spatial); + } + + void spatial_notify_delete(Spatial *p_spatial); + + // Calculate interpolated xforms, send to visual server. + void frame_update(Node *p_root, bool p_frame_start); + + // Update local xform pumps. + void tick_update(); + + void set_enabled(Node *p_root, bool p_enabled); + bool is_enabled() const { return data.enabled; } + + void set_debug_next_frame() { data.debug = true; } +}; + +#endif // ndef _3D_DISABLED + +#endif // SCENE_TREE_FTI_H diff --git a/scene/main/spatial.cpp b/scene/main/spatial.cpp index de84516e2..496f13164 100644 --- a/scene/main/spatial.cpp +++ b/scene/main/spatial.cpp @@ -118,7 +118,7 @@ void Spatial::_propagate_transform_changed(Spatial *p_origin) { #endif get_tree()->xform_change_list.add(&xform_change); } - data.dirty |= DIRTY_GLOBAL; + data.dirty |= DIRTY_GLOBAL | DIRTY_GLOBAL_INTERPOLATED; data.children_lock--; } @@ -174,13 +174,27 @@ void Spatial::_notification(int p_what) { _propagate_merging_allowed(merging_allowed); } - data.dirty |= DIRTY_GLOBAL; //global is always dirty upon entering a scene + data.dirty |= DIRTY_GLOBAL | DIRTY_GLOBAL_INTERPOLATED; //global is always dirty upon entering a scene _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); + if (is_physics_interpolated_and_enabled()) { + // Always reset FTI when entering tree. + fti_pump(); + + // No need to interpolate as we are doing a reset. + data.global_transform_interpolated = get_global_transform(); + + // Make sure servers are up to date. + fti_update_servers(); + } } break; case NOTIFICATION_EXIT_TREE: { + if (is_inside_tree()) { + get_tree()->get_scene_tree_fti().spatial_notify_delete(this); + } + notification(NOTIFICATION_EXIT_WORLD, true); if (xform_change.in_list()) { get_tree()->xform_change_list.remove(&xform_change); @@ -241,6 +255,13 @@ void Spatial::_notification(int p_what) { if (data.client_physics_interpolation_data) { data.client_physics_interpolation_data->global_xform_prev = data.client_physics_interpolation_data->global_xform_curr; } + data.local_transform_prev = data.local_transform; + } break; + + case NOTIFICATION_PAUSED: { + if (is_physics_interpolated_and_enabled()) { + data.local_transform_prev = data.local_transform; + } } break; default: { @@ -270,7 +291,22 @@ void Spatial::set_global_rotation(const Vector3 &p_euler_rad) { set_global_transform(transform); } +void Spatial::fti_pump() { + if (data.dirty & DIRTY_LOCAL) { + _update_local_transform(); + } + + data.local_transform_prev = data.local_transform; +} + +void Spatial::fti_notify_node_changed() { + if (is_inside_tree()) { + get_tree()->get_scene_tree_fti().spatial_notify_set_transform(*this); + } +} + void Spatial::set_transform(const Transform &p_transform) { + fti_notify_node_changed(); data.local_transform = p_transform; data.dirty |= DIRTY_VECTORS; data.dirty &= ~DIRTY_LOCAL; @@ -393,14 +429,19 @@ Transform Spatial::_get_global_transform_interpolated(real_t p_interpolation_fra } Transform Spatial::get_global_transform_interpolated() { +#if 1 // Pass through if physics interpolation is switched off. // This is a convenience, as it allows you to easy turn off interpolation // without changing any code. - if (!is_physics_interpolated_and_enabled()) { - return get_global_transform(); + if (data.fti_global_xform_interp_set && is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame() && is_visible_in_tree()) { + return data.global_transform_interpolated; } - // If we are in the physics frame, the interpolated global transform is meaningless. + return get_global_transform(); +#else + // OLD METHOD - deprecated since moving to SceneTreeFTI, + // but leaving for reference and comparison for debugging. + // However, there is an exception, we may want to use this as a means of starting off the client // interpolation pump if not already started (when _is_physics_interpolated_client_side() is false). if (Engine::get_singleton()->is_in_physics_frame() && _is_physics_interpolated_client_side()) { @@ -408,6 +449,7 @@ Transform Spatial::get_global_transform_interpolated() { } return _get_global_transform_interpolated(Engine::get_singleton()->get_physics_interpolation_fraction()); +#endif } Transform Spatial::get_global_transform() const { @@ -473,6 +515,7 @@ Transform Spatial::get_relative_transform(const Node *p_parent) const { } void Spatial::set_translation(const Vector3 &p_translation) { + fti_notify_node_changed(); data.local_transform.origin = p_translation; _change_notify("transform"); _propagate_transform_changed(this); @@ -482,6 +525,7 @@ void Spatial::set_translation(const Vector3 &p_translation) { } void Spatial::set_rotation(const Vector3 &p_euler_rad) { + fti_notify_node_changed(); if (data.dirty & DIRTY_VECTORS) { data.scale = data.local_transform.basis.get_scale(); data.dirty &= ~DIRTY_VECTORS; @@ -501,6 +545,7 @@ void Spatial::set_rotation_degrees(const Vector3 &p_euler_deg) { } void Spatial::set_scale(const Vector3 &p_scale) { + fti_notify_node_changed(); if (data.dirty & DIRTY_VECTORS) { data.rotation = data.local_transform.basis.get_rotation(); data.dirty &= ~DIRTY_VECTORS; @@ -1106,6 +1151,11 @@ Spatial::Spatial() : data.disable_scale = false; data.vi_visible = true; data.merging_allowed = true; + + data.fti_on_frame_list = false; + data.fti_on_tick_list = false; + data.fti_global_xform_interp_set = false; + data.merging_mode = MERGING_MODE_INHERIT; data.client_physics_interpolation_data = nullptr; @@ -1123,4 +1173,8 @@ Spatial::Spatial() : Spatial::~Spatial() { _disable_client_physics_interpolation(); + + if (is_inside_tree()) { + get_tree()->get_scene_tree_fti().spatial_notify_delete(this); + } } diff --git a/scene/main/spatial.h b/scene/main/spatial.h index 84f79cbac..66565d13c 100644 --- a/scene/main/spatial.h +++ b/scene/main/spatial.h @@ -55,6 +55,8 @@ class Spatial : public Node { GDCLASS(Spatial, Node); OBJ_CATEGORY("3D"); + friend class SceneTreeFTI; + public: enum MergingMode : unsigned int { MERGING_MODE_INHERIT, @@ -77,15 +79,27 @@ private: DIRTY_NONE = 0, DIRTY_VECTORS = 1, DIRTY_LOCAL = 2, - DIRTY_GLOBAL = 4 + DIRTY_GLOBAL = 4, + DIRTY_GLOBAL_INTERPOLATED = 8, }; mutable SelfList xform_change; SelfList _client_physics_interpolation_spatials_list; struct Data { + // Interpolated global transform - correct on the frame only. + // Only used with FTI. + Transform global_transform_interpolated; + + // Current xforms are either + // * Used for everything (when not using FTI) + // * Correct on the physics tick (when using FTI) mutable Transform global_transform; mutable Transform local_transform; + + // Only used with FTI. + Transform local_transform_prev; + mutable Vector3 rotation; mutable Vector3 scale; @@ -109,6 +123,11 @@ private: bool visible : 1; bool disable_scale : 1; + // Scene tree interpolation + bool fti_on_frame_list : 1; + bool fti_on_tick_list : 1; + bool fti_global_xform_interp_set : 1; + bool merging_allowed : 1; int children_lock; @@ -145,9 +164,27 @@ protected: bool _is_vi_visible() const { return data.vi_visible; } + Transform _get_global_transform_interpolated(real_t p_interpolation_fraction); + const Transform &_get_cached_global_transform_interpolated() const { return data.global_transform_interpolated; } void _disable_client_physics_interpolation(); + // Calling this announces to the FTI system that a node has been moved, + // or requires an update in terms of interpolation + // (e.g. changing Camera zoom even if position hasn't changed). + void fti_notify_node_changed(); + + // Opportunity after FTI to update the servers + // with global_transform_interpolated, + // and any custom interpolated data in derived classes. + // Make sure to call the parent class fti_update_servers(), + // so all data is updated to the servers. + virtual void fti_update_servers() {} + + // Pump the FTI data, also gives a chance for inherited classes + // to pump custom data, but they *must* call the base class here too. + virtual void fti_pump(); + void _notification(int p_what); static void _bind_methods(); diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 71e61ce70..7f88d71b0 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -92,16 +92,8 @@ public: RID material_override; RID material_overlay; - // This is the main transform to be drawn with .. - // This will either be the interpolated transform (when using fixed timestep interpolation) - // or the ONLY transform (when not using FTI). Transform transform; - // for interpolation we store the current transform (this physics tick) - // and the transform in the previous tick - Transform transform_curr; - Transform transform_prev; - int depth_layer; uint32_t layer_mask; @@ -123,16 +115,6 @@ public: bool baked_light : 1; //this flag is only to know if it actually did use baked light bool redraw_if_visible : 1; - bool on_interpolate_list : 1; - bool on_interpolate_transform_list : 1; - bool interpolated : 1; - TransformInterpolator::Method interpolation_method : 3; - - // For fixed timestep interpolation. - // Note 32 bits is plenty for checksum, no need for real_t - float transform_checksum_curr; - float transform_checksum_prev; - float depth; //used for sorting SelfList dependency_item; @@ -158,12 +140,6 @@ public: lightmap_capture = nullptr; lightmap_slice = -1; lightmap_uv_rect = Rect2(0, 0, 1, 1); - on_interpolate_list = false; - on_interpolate_transform_list = false; - interpolated = true; - interpolation_method = TransformInterpolator::INTERP_LERP; - transform_checksum_curr = 0.0; - transform_checksum_prev = 0.0; } }; diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 88c1a7216..6e86bd092 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -569,8 +569,6 @@ public: BIND2(instance_set_layer_mask, RID, uint32_t) BIND3(instance_set_pivot_data, RID, float, bool) BIND2(instance_set_transform, RID, const Transform &) - BIND2(instance_set_interpolated, RID, bool) - BIND1(instance_reset_physics_interpolation, RID) BIND2(instance_attach_object_instance_id, RID, ObjectID) BIND3(instance_set_blend_shape_weight, RID, int, float) BIND3(instance_set_surface_material, RID, int, RID) diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp index 853cf2aba..7685f6943 100644 --- a/servers/rendering/rendering_server_scene.cpp +++ b/servers/rendering/rendering_server_scene.cpp @@ -675,9 +675,6 @@ void RenderingServerScene::instance_set_scenario(RID p_instance, RID p_scenario) _instance_destroy_occlusion_rep(instance); } - // remove any interpolation data associated with the instance in this scenario - _interpolation_data.notify_free_instance(p_instance, *instance); - switch (instance->base_type) { case RS::INSTANCE_LIGHT: { InstanceLightData *light = static_cast(instance->base_data); @@ -767,27 +764,6 @@ void RenderingServerScene::instance_set_pivot_data(RID p_instance, float p_sorti instance->use_aabb_center = p_use_aabb_center; } -void RenderingServerScene::instance_reset_physics_interpolation(RID p_instance) { - Instance *instance = instance_owner.get(p_instance); - ERR_FAIL_COND(!instance); - - if (_interpolation_data.interpolation_enabled && instance->interpolated) { - instance->transform_prev = instance->transform_curr; - instance->transform_checksum_prev = instance->transform_checksum_curr; - -#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION - print_line("instance_reset_physics_interpolation .. tick " + itos(Engine::get_singleton()->get_physics_frames())); - print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x)); -#endif - } -} - -void RenderingServerScene::instance_set_interpolated(RID p_instance, bool p_interpolated) { - Instance *instance = instance_owner.get(p_instance); - ERR_FAIL_COND(!instance); - instance->interpolated = p_interpolated; -} - void RenderingServerScene::instance_set_transform(RID p_instance, const Transform &p_transform) { Instance *instance = instance_owner.get(p_instance); ERR_FAIL_COND(!instance); @@ -796,40 +772,8 @@ void RenderingServerScene::instance_set_transform(RID p_instance, const Transfor print_line("instance_set_transform " + rtos(p_transform.origin.x) + " .. tick " + itos(Engine::get_singleton()->get_physics_frames())); #endif - if (!(_interpolation_data.interpolation_enabled && instance->interpolated) || !instance->scenario) { - if (instance->transform == p_transform) { - return; //must be checked to avoid worst evil - } - -#ifdef DEBUG_ENABLED - - for (int i = 0; i < 4; i++) { - const Vector3 &v = i < 3 ? p_transform.basis.rows[i] : p_transform.origin; - ERR_FAIL_COND(Math::is_inf(v.x)); - ERR_FAIL_COND(Math::is_nan(v.x)); - ERR_FAIL_COND(Math::is_inf(v.y)); - ERR_FAIL_COND(Math::is_nan(v.y)); - ERR_FAIL_COND(Math::is_inf(v.z)); - ERR_FAIL_COND(Math::is_nan(v.z)); - } - -#endif - instance->transform = p_transform; - _instance_queue_update(instance, true); - return; - } - - float new_checksum = TransformInterpolator::checksum_transform(p_transform); - bool checksums_match = (instance->transform_checksum_curr == new_checksum) && (instance->transform_checksum_prev == new_checksum); - - // we can't entirely reject no changes because we need the interpolation - // system to keep on stewing - - // Optimized check. First checks the checksums. If they pass it does the slow check at the end. - // Alternatively we can do this non-optimized and ignore the checksum... - // if no change - if (checksums_match && (instance->transform_curr == p_transform) && (instance->transform_prev == p_transform)) { - return; + if (instance->transform == p_transform) { + return; //must be checked to avoid worst evil } #ifdef DEBUG_ENABLED @@ -846,53 +790,10 @@ void RenderingServerScene::instance_set_transform(RID p_instance, const Transfor #endif - instance->transform_curr = p_transform; - - // keep checksums up to date - instance->transform_checksum_curr = new_checksum; - - if (!instance->on_interpolate_transform_list) { - _interpolation_data.instance_transform_update_list_curr->push_back(p_instance); - instance->on_interpolate_transform_list = true; - } else { - DEV_ASSERT(_interpolation_data.instance_transform_update_list_curr->size()); - } - - // If the instance is invisible, then we are simply updating the data flow, there is no need to calculate the interpolated - // transform or anything else. - // Ideally we would not even call the RenderingServer::set_transform() when invisible but that would entail having logic - // to keep track of the previous transform on the SceneTree side. The "early out" below is less efficient but a lot cleaner codewise. - if (!instance->visible) { - return; - } - - // decide on the interpolation method .. slerp if possible - instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis); - - if (!instance->on_interpolate_list) { - _interpolation_data.instance_interpolate_update_list.push_back(p_instance); - instance->on_interpolate_list = true; - } else { - DEV_ASSERT(_interpolation_data.instance_interpolate_update_list.size()); - } - + instance->transform = p_transform; _instance_queue_update(instance, true); } -void RenderingServerScene::InterpolationData::notify_free_instance(RID p_rid, Instance &r_instance) { - r_instance.on_interpolate_list = false; - r_instance.on_interpolate_transform_list = false; - - if (!interpolation_enabled) { - return; - } - - // if the instance was on any of the lists, remove - instance_interpolate_update_list.erase_multiple_unordered(p_rid); - instance_transform_update_list_curr->erase_multiple_unordered(p_rid); - instance_transform_update_list_prev->erase_multiple_unordered(p_rid); -} - void RenderingServerScene::update_interpolation_tick(bool p_process) { #ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION print_line("update_interpolation_tick " + itos(Engine::get_singleton()->get_physics_frames())); @@ -900,84 +801,11 @@ void RenderingServerScene::update_interpolation_tick(bool p_process) { // update interpolation in storage RSG::storage->update_interpolation_tick(p_process); - - // detect any that were on the previous transform list that are no longer active, - // we should remove them from the interpolate list - - for (unsigned int n = 0; n < _interpolation_data.instance_transform_update_list_prev->size(); n++) { - const RID &rid = (*_interpolation_data.instance_transform_update_list_prev)[n]; - Instance *instance = instance_owner.getornull(rid); - - bool active = true; - - // no longer active? (either the instance deleted or no longer being transformed) - if (instance && !instance->on_interpolate_transform_list) { - active = false; - instance->on_interpolate_list = false; - - // make sure the most recent transform is set - instance->transform = instance->transform_curr; - - // and that both prev and current are the same, just in case of any interpolations - instance->transform_prev = instance->transform_curr; - - // make sure are updated one more time to ensure the AABBs are correct - _instance_queue_update(instance, true); - } - - if (!instance) { - active = false; - } - - if (!active) { - _interpolation_data.instance_interpolate_update_list.erase(rid); - } - } - - // and now for any in the transform list (being actively interpolated), keep the previous transform - // value up to date ready for the next tick - if (p_process) { - for (unsigned int n = 0; n < _interpolation_data.instance_transform_update_list_curr->size(); n++) { - const RID &rid = (*_interpolation_data.instance_transform_update_list_curr)[n]; - Instance *instance = instance_owner.getornull(rid); - if (instance) { - instance->transform_prev = instance->transform_curr; - instance->transform_checksum_prev = instance->transform_checksum_curr; - instance->on_interpolate_transform_list = false; - } - } - } - - // we maintain a mirror list for the transform updates, so we can detect when an instance - // is no longer being transformed, and remove it from the interpolate list - SWAP(_interpolation_data.instance_transform_update_list_curr, _interpolation_data.instance_transform_update_list_prev); - - // prepare for the next iteration - _interpolation_data.instance_transform_update_list_curr->clear(); } void RenderingServerScene::update_interpolation_frame(bool p_process) { // update interpolation in storage RSG::storage->update_interpolation_frame(p_process); - - if (p_process) { - real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); - - for (unsigned int i = 0; i < _interpolation_data.instance_interpolate_update_list.size(); i++) { - const RID &rid = _interpolation_data.instance_interpolate_update_list[i]; - Instance *instance = instance_owner.getornull(rid); - if (instance) { - TransformInterpolator::interpolate_transform_via_method(instance->transform_prev, instance->transform_curr, instance->transform, f, instance->interpolation_method); - -#ifdef VISUAL_SERVER_DEBUG_PHYSICS_INTERPOLATION - print_line("\t\tinterpolated: " + rtos(instance->transform.origin.x) + "\t( prev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); -#endif - - // make sure AABBs are constantly up to date through the interpolation - _instance_queue_update(instance, true); - } - } // for n - } } void RenderingServerScene::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) { @@ -1031,25 +859,6 @@ void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible) instance->visible = p_visible; - // Special case for physics interpolation, we want to ensure the interpolated data is up to date - if (_interpolation_data.interpolation_enabled && p_visible && instance->interpolated && instance->scenario && !instance->on_interpolate_list) { - // Do all the extra work we normally do on instance_set_transform(), because this is optimized out for hidden instances. - // This prevents a glitch of stale interpolation transform data when unhiding before the next physics tick. - instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis); - _interpolation_data.instance_interpolate_update_list.push_back(p_instance); - instance->on_interpolate_list = true; - _instance_queue_update(instance, true); - - // We must also place on the transform update list for a tick, so the system - // can auto-detect if the instance is no longer moving, and remove from the interpolate lists again. - // If this step is ignored, an unmoving instance could remain on the interpolate lists indefinitely - // (or rather until the object is deleted) and cause unnecessary updates and drawcalls. - if (!instance->on_interpolate_transform_list) { - _interpolation_data.instance_transform_update_list_curr->push_back(p_instance); - instance->on_interpolate_transform_list = true; - } - } - // give the opportunity for the spatial partitioning scene to use a special implementation of visibility // for efficiency (supported in BVH but not octree) @@ -4398,8 +4207,6 @@ bool RenderingServerScene::free(RID p_rid) { Instance *instance = instance_owner.get(p_rid); - _interpolation_data.notify_free_instance(p_rid, *instance); - instance_set_use_lightmap(p_rid, RID(), RID(), -1, Rect2(0, 0, 1, 1)); instance_set_scenario(p_rid, RID()); instance_set_base(p_rid, RID()); diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h index cbead9a99..53a9ff06a 100644 --- a/servers/rendering/rendering_server_scene.h +++ b/servers/rendering/rendering_server_scene.h @@ -398,12 +398,6 @@ public: virtual void set_physics_interpolation_enabled(bool p_enabled); struct InterpolationData { - void notify_free_instance(RID p_rid, Instance &r_instance); - LocalVector instance_interpolate_update_list; - LocalVector instance_transform_update_lists[2]; - LocalVector *instance_transform_update_list_curr = &instance_transform_update_lists[0]; - LocalVector *instance_transform_update_list_prev = &instance_transform_update_lists[1]; - bool interpolation_enabled = false; } _interpolation_data; @@ -664,8 +658,6 @@ public: virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask); virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center); virtual void instance_set_transform(RID p_instance, const Transform &p_transform); - virtual void instance_set_interpolated(RID p_instance, bool p_interpolated); - virtual void instance_reset_physics_interpolation(RID p_instance); virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id); virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight); virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material); diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index b24f72283..01fa29cbf 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -478,8 +478,6 @@ public: FUNC2(instance_set_layer_mask, RID, uint32_t) FUNC3(instance_set_pivot_data, RID, float, bool) FUNC2(instance_set_transform, RID, const Transform &) - FUNC2(instance_set_interpolated, RID, bool) - FUNC1(instance_reset_physics_interpolation, RID) FUNC2(instance_attach_object_instance_id, RID, ObjectID) FUNC3(instance_set_blend_shape_weight, RID, int, float) FUNC3(instance_set_surface_material, RID, int, RID) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 9f6cdca54..e327a18a0 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2168,8 +2168,6 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instance_set_scenario", "instance", "scenario"), &RenderingServer::instance_set_scenario); ClassDB::bind_method(D_METHOD("instance_set_layer_mask", "instance", "mask"), &RenderingServer::instance_set_layer_mask); ClassDB::bind_method(D_METHOD("instance_set_transform", "instance", "transform"), &RenderingServer::instance_set_transform); - ClassDB::bind_method(D_METHOD("instance_set_interpolated", "instance", "interpolated"), &RenderingServer::instance_set_interpolated); - ClassDB::bind_method(D_METHOD("instance_reset_physics_interpolation", "instance"), &RenderingServer::instance_reset_physics_interpolation); ClassDB::bind_method(D_METHOD("instance_attach_object_instance_id", "instance", "id"), &RenderingServer::instance_attach_object_instance_id); ClassDB::bind_method(D_METHOD("instance_set_blend_shape_weight", "instance", "shape", "weight"), &RenderingServer::instance_set_blend_shape_weight); ClassDB::bind_method(D_METHOD("instance_set_surface_material", "instance", "surface", "material"), &RenderingServer::instance_set_surface_material); @@ -2187,7 +2185,9 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instances_cull_aabb", "aabb", "scenario"), &RenderingServer::_instances_cull_aabb_bind, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("instances_cull_convex", "convex", "scenario"), &RenderingServer::_instances_cull_convex_bind, DEFVAL(RID())); + #endif + ClassDB::bind_method(D_METHOD("canvas_create"), &RenderingServer::canvas_create); ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &RenderingServer::canvas_set_item_mirroring); ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &RenderingServer::canvas_set_modulate); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 09df9f7b2..70252ea5e 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -876,8 +876,6 @@ public: virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 0; virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center) = 0; virtual void instance_set_transform(RID p_instance, const Transform &p_transform) = 0; - virtual void instance_set_interpolated(RID p_instance, bool p_interpolated) = 0; - virtual void instance_reset_physics_interpolation(RID p_instance) = 0; virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0; virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0; virtual void instance_set_surface_material(RID p_instance, int p_surface, RID p_material) = 0; From b819224539b9d76e6fcb958538a47af74ae98679 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 08:28:30 +0200 Subject: [PATCH 10/16] Fix compile. --- scene/3d/camera.cpp | 2 +- scene/3d/visual_instance.cpp | 2 +- servers/rendering_server.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 80155a6d3..cbfa5b2a1 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -53,7 +53,7 @@ void Camera::_request_camera_update() { void Camera::fti_update_servers() { if (camera.is_valid()) { Transform tr = _get_adjusted_camera_transform(_get_cached_global_transform_interpolated()); - VisualServer::get_singleton()->camera_set_transform(camera, tr); + RenderingServer::get_singleton()->camera_set_transform(camera, tr); } } diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 937b8874c..0bd2dc0cb 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -87,7 +87,7 @@ void VisualInstance::set_instance_use_identity_transform(bool p_enable) { void VisualInstance::fti_update_servers() { if (!_is_using_identity_transform()) { - VisualServer::get_singleton()->instance_set_transform(get_instance(), _get_cached_global_transform_interpolated()); + RenderingServer::get_singleton()->instance_set_transform(get_instance(), _get_cached_global_transform_interpolated()); } } diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index e327a18a0..b5472eaa5 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1248,7 +1248,7 @@ bool RenderingServer::_mesh_find_format(RS::PrimitiveType p_primitive, const Arr } uint32_t RenderingServer::mesh_find_format_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) { - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_compress_format & rS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[RS::ARRAY_MAX]; @@ -1338,7 +1338,7 @@ void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_p } Array RenderingServer::_get_array_from_surface(uint32_t p_format, PoolVector p_vertex_data, int p_vertex_len, PoolVector p_index_data, int p_index_len) const { - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & rS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[ARRAY_MAX]; uint32_t strides[RS::ARRAY_MAX]; From 70ec81c137a2d3d4bcab990276f547d4e19bcd88 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 09:33:17 +0200 Subject: [PATCH 11/16] Backported move semantics from godot 3.x. with some changes. Original commit: https://github.com/godotengine/godot/commit/d549b98c5cb4b68f38aece3171cd0aac7c63a7a7 --- core/containers/cowdata.h | 20 +++++++++----- core/containers/list.h | 18 +++++++++++++ core/containers/local_vector.h | 49 +++++++++++++++++++++++++++++----- core/string/string_name.h | 19 +++++++++++++ core/typedefs.h | 26 +++++++++++++++--- core/variant/variant.h | 18 ++++++++++++- 6 files changed, 134 insertions(+), 16 deletions(-) diff --git a/core/containers/cowdata.h b/core/containers/cowdata.h index 0525d124a..130d6550e 100644 --- a/core/containers/cowdata.h +++ b/core/containers/cowdata.h @@ -37,6 +37,7 @@ #include "core/error/error_macros.h" #include "core/os/memory.h" #include "core/os/safe_refcount.h" +#include "core/typedefs.h" template class Vector; @@ -161,19 +162,26 @@ public: T *p = ptrw(); int len = size(); for (int i = p_index; i < len - 1; i++) { - p[i] = p[i + 1]; + p[i] = MOVE_VAR(p[i + 1]); }; resize(len - 1); } Error insert(int p_pos, const T &p_val) { - ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); - resize(size() + 1); - for (int i = (size() - 1); i > p_pos; i--) { - set(i, get(i - 1)); + int new_size = size() + 1; + + ERR_FAIL_INDEX_V(p_pos, new_size , ERR_INVALID_PARAMETER); + + Error err = resize(new_size); + ERR_FAIL_COND_V(err, err); + + T *p = ptrw(); + for (int i = new_size - 1; i > p_pos; i--) { + p[i] = MOVE_VAR(p[i - 1]); } - set(p_pos, p_val); + + p[p_pos] = p_val; return OK; } diff --git a/core/containers/list.h b/core/containers/list.h index aeb21e340..9d404eced 100644 --- a/core/containers/list.h +++ b/core/containers/list.h @@ -35,6 +35,7 @@ #include "core/containers/sort_array.h" #include "core/error/error_macros.h" #include "core/os/memory.h" +#include "core/typedefs.h" /** * Generic Templatized Linked List Implementation. @@ -445,6 +446,18 @@ public: } } + void operator=(List &&p_list) { + if (unlikely(this == &p_list)) { + return; + } + + clear(); + + _data = p_list._data; + + p_list._data = nullptr; + } + T &operator[](int p_index) { CRASH_BAD_INDEX(p_index, size()); @@ -686,6 +699,11 @@ public: } } + List(List &&p_list) { + _data = p_list._data; + p_list._data = nullptr; + } + List() { _data = nullptr; }; diff --git a/core/containers/local_vector.h b/core/containers/local_vector.h index 63efc9487..bbb7c010a 100644 --- a/core/containers/local_vector.h +++ b/core/containers/local_vector.h @@ -37,6 +37,7 @@ #include "core/containers/vector.h" #include "core/error/error_macros.h" #include "core/os/memory.h" +#include "core/typedefs.h" template class LocalVector { @@ -66,9 +67,9 @@ public: } if (!HAS_TRIVIAL_CONSTRUCTOR(T) && !force_trivial) { - memnew_placement(&data[count++], T(p_elem)); + memnew_placement(&data[count++], T(MOVE_VAR(p_elem))); } else { - data[count++] = p_elem; + data[count++] = MOVE_VAR(p_elem); } } @@ -76,7 +77,7 @@ public: ERR_FAIL_UNSIGNED_INDEX(p_index, count); count--; for (U i = p_index; i < count; i++) { - data[i] = data[i + 1]; + data[i] = MOVE_VAR(data[i + 1]); } if (!HAS_TRIVIAL_DESTRUCTOR(T) && !force_trivial) { data[count].~T(); @@ -202,13 +203,13 @@ public: void insert(U p_pos, T p_val) { ERR_FAIL_UNSIGNED_INDEX(p_pos, count + 1); if (p_pos == count) { - push_back(p_val); + push_back(MOVE_VAR(p_val)); } else { resize(count + 1); for (U i = count - 1; i > p_pos; i--) { - data[i] = data[i - 1]; + data[i] = MOVE_VAR(data[i - 1]); } - data[p_pos] = p_val; + data[p_pos] = MOVE_VAR(p_val); } } @@ -294,6 +295,16 @@ public: } } + LocalVector(LocalVector &&p_from) { + data = p_from.data; + count = p_from.count; + capacity = p_from.capacity; + + p_from.data = nullptr; + p_from.count = 0; + p_from.capacity = 0; + } + inline LocalVector &operator=(const LocalVector &p_from) { resize(p_from.size()); for (U i = 0; i < p_from.count; i++) { @@ -301,6 +312,23 @@ public: } return *this; } + + inline void operator=(LocalVector &&p_from) { + if (unlikely(this == &p_from)) { + return; + } + + reset(); + + data = p_from.data; + count = p_from.count; + capacity = p_from.capacity; + + p_from.data = nullptr; + p_from.count = 0; + p_from.capacity = 0; + } + inline LocalVector &operator=(const Vector &p_from) { resize(p_from.size()); for (U i = 0; i < count; i++) { @@ -308,6 +336,15 @@ public: } return *this; } + + inline void operator=(Vector &&p_from) { + resize(p_from.size()); + + for (U i = 0; i < count; i++) { + data[i] = MOVE_VAR(p_from[i]); + } + } + inline LocalVector &operator=(const PoolVector &p_from) { resize(p_from.size()); typename PoolVector::Read r = p_from.read(); diff --git a/core/string/string_name.h b/core/string/string_name.h index 22769c19f..3e9f4c184 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -193,6 +193,25 @@ public: }; void operator=(const StringName &p_name); + + StringName &operator=(StringName &&p_name) { + if (_data == p_name._data) { + return *this; + } + + unref(); + + _data = p_name._data; + p_name._data = nullptr; + + return *this; + } + + StringName(StringName &&p_name) { + _data = p_name._data; + p_name._data = nullptr; + } + StringName(const char *p_name, bool p_static = false); StringName(const StringName &p_name); StringName(const String &p_name, bool p_static = false); diff --git a/core/typedefs.h b/core/typedefs.h index fd9926470..f25fad506 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -188,15 +188,35 @@ T *_nullptr() { #define CLAMP(m_a, m_min, m_max) (((m_a) < (m_min)) ? (m_min) : (((m_a) > (m_max)) ? m_max : m_a)) #endif +template +struct REMOVE_REFERENCE { + using type = T; +}; + +template +struct REMOVE_REFERENCE { + using type = T; +}; + +template +struct REMOVE_REFERENCE { + using type = T; +}; + +template +typename REMOVE_REFERENCE::type&& MOVE_VAR(T&& t) { + return static_cast::type&&>(t); +} + /** Generic swap template */ #ifndef SWAP #define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y)) template inline void __swap_tmpl(T &x, T &y) { - T aux = x; - x = y; - y = aux; + T aux = MOVE_VAR(x); + x = MOVE_VAR(y); + y = MOVE_VAR(aux); } #endif //swap diff --git a/core/variant/variant.h b/core/variant/variant.h index 37541af8e..9f144781f 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -180,7 +180,7 @@ private: Transform *_transform; Projection *_projection; void *_ptr; //generic pointer - uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]; + uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]{ 0 }; } _data GCC_ALIGNED_8; void reference(const Variant &p_variant); @@ -485,7 +485,23 @@ public: static void construct_from_string(const String &p_string, Variant &r_value, ObjectConstruct p_obj_construct = nullptr, void *p_construct_ud = nullptr); void operator=(const Variant &p_variant); // only this is enough for all the other types + void operator=(Variant &&p_variant) { + if (unlikely(this == &p_variant)) { + return; + } + + clear(); + + type = p_variant.type; + _data = p_variant._data; + p_variant.type = NIL; + } Variant(const Variant &p_variant); + Variant(Variant &&p_variant) { + type = p_variant.type; + _data = p_variant._data; + p_variant.type = NIL; + } _FORCE_INLINE_ Variant() { type = NIL; } From 2746b8f1e210e3091de6856e717dc238da38d870 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 09:42:19 +0200 Subject: [PATCH 12/16] Fix compile. --- scene/main/scene_tree_fti.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scene/main/scene_tree_fti.h b/scene/main/scene_tree_fti.h index c283d8ce8..49047e0fb 100644 --- a/scene/main/scene_tree_fti.h +++ b/scene/main/scene_tree_fti.h @@ -36,7 +36,7 @@ class Spatial; class Node; -class Transform; +struct Transform; #ifdef _3D_DISABLED // Stubs From f3b83301826fff82ad2deff035460811a4adc176 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 10:44:03 +0200 Subject: [PATCH 13/16] Use HAS_TRIVIAL_DESTRUCTOR macro instead of std::is_trivially_destructible. --- core/config/project_settings.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 3f0b5b524..5ed3cdf6b 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -35,6 +35,7 @@ #include "core/containers/rb_set.h" #include "core/object/object.h" #include "core/os/thread_safe.h" +#include "core/typedefs.h" // Querying ProjectSettings is usually done at startup. // Additionally, in order to keep track of changes to ProjectSettings, @@ -227,7 +228,7 @@ Variant _GLOBAL_DEF_ALIAS(const String &p_var, const String &p_old_name, const V // Cached versions of GLOBAL_GET. // Cached but uses a typed variable for storage, this can be more efficient. #define GLOBAL_GET_CACHED(m_type, m_setting_name) ([](const char *p_name) -> m_type {\ -static_assert(std::is_trivially_destructible::value, "GLOBAL_GET_CACHED must use a trivial type that allows static lifetime.");\ +static_assert(HAS_TRIVIAL_DESTRUCTOR(m_type), "GLOBAL_GET_CACHED must use a trivial type that allows static lifetime.");\ static m_type local_var;\ static uint32_t local_version = 0;\ static Mutex local_mutex;\ From 35b072e1df5398ca9d4d0458d6aae361b0c87913 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 12:51:27 +0200 Subject: [PATCH 14/16] Reworked the new GLOBAL_GET_CACHED macro. --- core/config/project_settings.h | 32 ++++++++----------- core/error/error_macros.cpp | 4 ++- drivers/gles2/rasterizer_gles2.cpp | 2 +- drivers/gles2/rasterizer_scene_gles2.cpp | 7 ++-- drivers/gles2/rasterizer_storage_gles2.cpp | 3 +- drivers/gles2/shader_compiler_gles2.cpp | 8 ++--- drivers/gles2/shader_gles2.cpp | 4 ++- drivers/gles3/rasterizer_gles3.cpp | 2 +- drivers/gles3/rasterizer_scene_gles3.cpp | 25 ++++++++++----- drivers/gles3/rasterizer_storage_gles3.cpp | 3 +- drivers/gles3/shader_compiler_gles3.cpp | 8 ++--- editor/import/resource_importer_texture.cpp | 7 ++-- .../animation_player_editor_plugin.cpp | 5 ++- editor/plugins/canvas_item_editor_plugin.cpp | 4 ++- editor/plugins/editor_preview_plugins.cpp | 3 +- editor/plugins/theme_editor_preview.cpp | 4 ++- .../editor_script_text_editor.cpp | 4 ++- main/main.cpp | 4 ++- modules/gdscript/gdscript_editor.cpp | 2 +- modules/gdscript/gdscript_parser.cpp | 15 +++++++-- .../android/export/gradle_export_util.cpp | 5 ++- scene/3d/mesh_instance.cpp | 6 ++-- scene/main/scene_tree.cpp | 3 +- scene/main/viewport.cpp | 10 ++++-- scene/resources/material/spatial_material.cpp | 4 ++- servers/audio/audio_stream.cpp | 4 ++- .../portals/portal_occlusion_culler.cpp | 4 ++- servers/rendering_server.cpp | 12 ++++--- 28 files changed, 126 insertions(+), 68 deletions(-) diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 5ed3cdf6b..c2336478c 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -224,23 +224,19 @@ Variant _GLOBAL_DEF_ALIAS(const String &p_var, const String &p_old_name, const V #define GLOBAL_DEF_ALIAS_RST(m_var, m_old_name, m_value) _GLOBAL_DEF(m_var, m_old_name, m_value, true) #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) -///////////////////////////////////////////////////////////////////////////////////////// -// Cached versions of GLOBAL_GET. -// Cached but uses a typed variable for storage, this can be more efficient. -#define GLOBAL_GET_CACHED(m_type, m_setting_name) ([](const char *p_name) -> m_type {\ -static_assert(HAS_TRIVIAL_DESTRUCTOR(m_type), "GLOBAL_GET_CACHED must use a trivial type that allows static lifetime.");\ -static m_type local_var;\ -static uint32_t local_version = 0;\ -static Mutex local_mutex;\ -uint32_t new_version = ProjectSettings::get_singleton()->get_version();\ -if (local_version != new_version) {\ - MutexLock lock(local_mutex);\ - local_version = new_version;\ - local_var = ProjectSettings::get_singleton()->get(p_name);\ - return local_var;\ -}\ -MutexLock lock(local_mutex);\ -return local_var; })(m_setting_name) +#define GLOBAL_CACHED(m_name, m_type, m_setting_name) \ + static m_type m_name; \ + { \ + static_assert(HAS_TRIVIAL_DESTRUCTOR(m_type), "GLOBAL_CACHED must use a trivial type that allows static lifetime."); \ + static uint32_t local_version = 0; \ + static Mutex local_mutex; \ + uint32_t new_version = ProjectSettings::get_singleton()->get_version(); \ + if (local_version != new_version) { \ + local_mutex.lock(); \ + local_version = new_version; \ + m_name = ProjectSettings::get_singleton()->get(m_setting_name); \ + local_mutex.unlock(); \ + } \ + } #endif // PROJECT_SETTINGS_H - diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index aaa799fa6..fa2dbe20e 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -147,7 +147,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, warn_count = warn_max; warn_timeout = time_now + warn_timeout_seconds; - if (GLOBAL_GET_CACHED(bool, "debug/settings/physics_interpolation/enable_warnings")) { + GLOBAL_CACHED(debug_setting_enable_warnings, bool, "debug/settings/physics_interpolation/enable_warnings"); + + if (debug_setting_enable_warnings) { // UINT64_MAX means unused. if (p_id == UINT64_MAX) { _err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", ERR_HANDLER_WARNING); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 96f74752f..0ba8d4ca8 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -276,7 +276,7 @@ void RasterizerGLES2::begin_frame(double frame_step) { frame_step = 0.001; } - double time_roll_over = GLOBAL_GET_CACHED(double, "rendering/limits/time/time_rollover_secs"); + GLOBAL_CACHED(time_roll_over, double, "rendering/limits/time/time_rollover_secs"); time_total = Math::fmod(time_total, time_roll_over); storage->frame.time[0] = time_total; diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 0d8c50fbe..3db25a89f 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -4059,9 +4059,12 @@ void RasterizerSceneGLES2::initialize() { } void RasterizerSceneGLES2::iteration() { - shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET_CACHED(int32_t, "rendering/quality/shadows/filter_mode"))); + GLOBAL_CACHED(rendering_quality_shadows_filter_mode, int32_t, "rendering/quality/shadows/filter_mode"); + GLOBAL_CACHED(rendering_quality_directional_shadow_size, int32_t, "rendering/quality/directional_shadow/size"); - const int directional_shadow_size_new = next_power_of_2(GLOBAL_GET_CACHED(int32_t, "rendering/quality/directional_shadow/size")); + shadow_filter_mode = ShadowFilterMode(int(rendering_quality_shadows_filter_mode)); + + const int directional_shadow_size_new = next_power_of_2(rendering_quality_directional_shadow_size); if (directional_shadow_size != directional_shadow_size_new) { directional_shadow_size = directional_shadow_size_new; directional_shadow_create(); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 9a3bcac47..ce6752b30 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -2296,7 +2296,8 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, RS: } //bool has_morph = p_blend_shapes.size(); - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream"); + bool use_split_stream = storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); Surface::Attrib attribs[RS::ARRAY_MAX]; diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 4268e0831..02a10e948 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -1226,9 +1226,9 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { //actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; //actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; - bool force_lambert = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_lambert_over_burley"); + GLOBAL_CACHED(render_force_lambert_over_burley, bool, "rendering/quality/shading/force_lambert_over_burley"); - if (!force_lambert) { + if (!render_force_lambert_over_burley) { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; } @@ -1236,9 +1236,9 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; - bool force_blinn = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_blinn_over_ggx"); + GLOBAL_CACHED(render_force_blinn_over_ggx, bool, "rendering/quality/shading/force_blinn_over_ggx"); - if (!force_blinn) { + if (!render_force_blinn_over_ggx) { actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; } else { actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n"; diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp index e40375c42..e68e7a616 100644 --- a/drivers/gles2/shader_gles2.cpp +++ b/drivers/gles2/shader_gles2.cpp @@ -178,7 +178,9 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { strings.push_back("#define USE_HIGHP_PRECISION\n"); #endif - if (GLOBAL_GET_CACHED(bool, "rendering/gles2/compatibility/enable_high_float.Android")) { + GLOBAL_CACHED(gles2_compat_enable_high_float_android, bool, "rendering/gles2/compatibility/enable_high_float.Android"); + + if (gles2_compat_enable_high_float_android) { // enable USE_HIGHP_PRECISION but safeguarded by an availability check as highp support is optional in GLES2 // see Section 4.5.4 of the GLSL_ES_Specification_1.00 strings.push_back("#ifdef GL_FRAGMENT_PRECISION_HIGH\n #define USE_HIGHP_PRECISION\n#endif\n"); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 7c147c3d1..3c714655c 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -200,7 +200,7 @@ void RasterizerGLES3::begin_frame(double frame_step) { frame_step = 0.001; } - double time_roll_over = GLOBAL_GET_CACHED(double, "rendering/limits/time/time_rollover_secs"); + GLOBAL_CACHED(time_roll_over, double, "rendering/limits/time/time_rollover_secs"); time_total = Math::fmod(time_total, time_roll_over); storage->frame.time[0] = time_total; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index c7b1f326e..a8897bccc 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -5315,22 +5315,31 @@ void RasterizerSceneGLES3::initialize() { } void RasterizerSceneGLES3::iteration() { - shadow_filter_mode = ShadowFilterMode(GLOBAL_GET_CACHED(int32_t, "rendering/quality/shadows/filter_mode")); + GLOBAL_CACHED(rendering_global_filter_mode, int32_t, "rendering/quality/shadows/filter_mode"); + GLOBAL_CACHED(rendering_global_directional_shadow_size, int32_t, "rendering/quality/directional_shadow/size"); + GLOBAL_CACHED(rendering_global_subsurface_scattering_follow_surface, bool, "rendering/quality/subsurface_scattering/follow_surface"); + GLOBAL_CACHED(rendering_global_subsurface_scattering_weight_samples, bool, "rendering/quality/subsurface_scattering/weight_samples"); + GLOBAL_CACHED(rendering_global_subsurface_scattering_quality, int32_t, "rendering/quality/subsurface_scattering/quality"); + GLOBAL_CACHED(rendering_global_subsurface_scattering_scale, float, "rendering/quality/subsurface_scattering/scale"); + GLOBAL_CACHED(rendering_global_lightmapping_use_bicubic_sampling, bool, "rendering/quality/lightmapping/use_bicubic_sampling"); + GLOBAL_CACHED(rendering_global_voxel_cone_tracinghigh_quality, bool, "rendering/quality/voxel_cone_tracing/high_quality"); - const int directional_shadow_size_new = next_power_of_2(GLOBAL_GET_CACHED(int32_t, "rendering/quality/directional_shadow/size")); + shadow_filter_mode = ShadowFilterMode(rendering_global_filter_mode); + + const int directional_shadow_size_new = next_power_of_2(rendering_global_directional_shadow_size); if (directional_shadow_size != directional_shadow_size_new) { directional_shadow_size = directional_shadow_size_new; directional_shadow_create(); } - subsurface_scatter_follow_surface = GLOBAL_GET_CACHED(bool, "rendering/quality/subsurface_scattering/follow_surface"); - subsurface_scatter_weight_samples = GLOBAL_GET_CACHED(bool, "rendering/quality/subsurface_scattering/weight_samples"); - subsurface_scatter_quality = SubSurfaceScatterQuality(int(GLOBAL_GET_CACHED(int32_t, "rendering/quality/subsurface_scattering/quality"))); - subsurface_scatter_size = GLOBAL_GET_CACHED(float, "rendering/quality/subsurface_scattering/scale"); + subsurface_scatter_follow_surface = rendering_global_subsurface_scattering_follow_surface; + subsurface_scatter_weight_samples = rendering_global_subsurface_scattering_weight_samples; + subsurface_scatter_quality = SubSurfaceScatterQuality(int(rendering_global_subsurface_scattering_quality)); + subsurface_scatter_size = rendering_global_subsurface_scattering_scale; - storage->config.use_lightmap_filter_bicubic = GLOBAL_GET_CACHED(bool, "rendering/quality/lightmapping/use_bicubic_sampling"); + storage->config.use_lightmap_filter_bicubic = rendering_global_lightmapping_use_bicubic_sampling; state.scene_shader.set_conditional(SceneShaderGLES3::USE_LIGHTMAP_FILTER_BICUBIC, storage->config.use_lightmap_filter_bicubic); - state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, GLOBAL_GET_CACHED(bool, "rendering/quality/voxel_cone_tracing/high_quality")); + state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, rendering_global_voxel_cone_tracinghigh_quality); } void RasterizerSceneGLES3::finalize() { diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 9011b54d6..14194ef7f 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -3390,7 +3390,8 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, RS: } //bool has_morph = p_blend_shapes.size(); - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + GLOBAL_CACHED(global_storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream") + bool use_split_stream = global_storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); Surface::Attrib attribs[RS::ARRAY_MAX]; diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index a31686262..19bc6cd3d 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -1268,9 +1268,9 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; - bool force_lambert = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_lambert_over_burley"); + GLOBAL_CACHED(render_force_lambert_over_burley, bool, "rendering/quality/shading/force_lambert_over_burley"); - if (!force_lambert) { + if (!render_force_lambert_over_burley) { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; } @@ -1278,9 +1278,9 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; - bool force_blinn = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_blinn_over_ggx"); + GLOBAL_CACHED(render_force_blinn_over_ggx, bool, "rendering/quality/shading/force_blinn_over_ggx"); - if (!force_blinn) { + if (!render_force_blinn_over_ggx) { actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; } else { actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n"; diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 5af645861..abc553635 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -418,13 +418,14 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String if (repeat > 0) { tex_flags |= Texture::FLAG_REPEAT; - const bool min_gles3 = GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3" && - !GLOBAL_GET_CACHED(bool, "rendering/quality/driver/fallback_to_gles2"); + GLOBAL_CACHED(global_fallback_to_gles2, bool, "rendering/quality/driver/fallback_to_gles2"); + + const bool min_gles3 = GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3" && !global_fallback_to_gles2; if (!min_gles3 && !image->is_size_po2()) { // The project can be run using GLES2. GLES2 does not guarantee that // repeating textures with a non-power-of-two size will be displayed // without artifacts (due to upscaling to the nearest power of 2). - if (GLOBAL_GET_CACHED(bool, "rendering/quality/driver/fallback_to_gles2")) { + if (global_fallback_to_gles2) { WARN_PRINT(vformat("%s: Imported a repeating texture with a size of %dx%d, but the project is configured to allow falling back to GLES2.\nNon-power-of-2 repeating textures may not display correctly on some platforms such as HTML5. This is because GLES2 does not mandate support for non-power-of-2 repeating textures.", p_source_file, image->get_width(), image->get_height())); } else { diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 1386d7538..4da3c43ff 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1475,7 +1475,10 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { // Render every past/future step with the capture shader. RS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, onion.capture.material->get_rid()); - onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET_CACHED(Color, "rendering/environment/default_clear_color")); + + GLOBAL_CACHED(global_environment_default_clear_color, Color, "rendering/environment/default_clear_color"); + + onion.capture.material->set_shader_param("bkg_color", global_environment_default_clear_color); onion.capture.material->set_shader_param("differences_only", onion.differences_only); onion.capture.material->set_shader_param("present", onion.differences_only ? RS::get_singleton()->viewport_get_texture(present_rid) : RID()); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index f95e82fe6..d13fd7940 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -3677,7 +3677,9 @@ void CanvasItemEditor::set_current_tool(Tool p_tool) { void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_PHYSICS_PROCESS) { - EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET_CACHED(bool, "gui/common/snap_controls_to_pixels")); + GLOBAL_CACHED(global_gui_common_snap_controls_to_pixels, bool, "gui/common/snap_controls_to_pixels") + + EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(global_gui_common_snap_controls_to_pixels); bool has_container_parents = false; int nb_control = 0; diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index bc5f82d1f..fde5f21bc 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -894,7 +894,8 @@ Ref EditorFontPreviewPlugin::generate_from_path(const String &p_path, c Ref font = sampled_font; - const Color c = GLOBAL_GET_CACHED(Color, "rendering/environment/default_clear_color"); + GLOBAL_CACHED(global_default_clear_color, Color, "rendering/environment/default_clear_color"); + const Color c = global_default_clear_color; const float fg = c.get_luminance() < 0.5 ? 1.0 : 0.0; font->draw(canvas_item, pos, sampled_text, Color(fg, fg, fg)); diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index a0eb1d7bb..f4a7a5560 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -96,7 +96,9 @@ void ThemeEditorPreview::_propagate_redraw(Control *p_at) { void ThemeEditorPreview::_refresh_interval() { // In case the project settings have changed. - preview_bg->set_frame_color(GLOBAL_GET_CACHED(Color, "rendering/environment/default_clear_color")); + GLOBAL_CACHED(rendering_default_clear_color, Color, "rendering/environment/default_clear_color"); + + preview_bg->set_frame_color(rendering_default_clear_color); _propagate_redraw(preview_bg); _propagate_redraw(preview_content); diff --git a/editor_modules/editor_code_editor/editor_script_text_editor.cpp b/editor_modules/editor_code_editor/editor_script_text_editor.cpp index ef2e45956..bf305600e 100644 --- a/editor_modules/editor_code_editor/editor_script_text_editor.cpp +++ b/editor_modules/editor_code_editor/editor_script_text_editor.cpp @@ -446,7 +446,9 @@ void EditorScriptTextEditor::_validate_script() { warnings_panel->clear(); // Add missing connections. - if (GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/enable")) { + GLOBAL_CACHED(global_debug_warning_enable, bool, "debug/gdscript/warnings/enable") + + if (global_debug_warning_enable) { Node *base = get_tree()->get_edited_scene_root(); if (base && missing_connections.size() > 0) { warnings_panel->push_table(1); diff --git a/main/main.cpp b/main/main.cpp index 32f8528d6..55ae0e7e5 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2538,6 +2538,8 @@ bool Main::iteration() { frames++; Engine::get_singleton()->_idle_frames++; + GLOBAL_CACHED(debug_settings_stdout_print_pfs, bool, "debug/settings/stdout/print_fps"); + if (frame > 1000000) { // Wait a few seconds before printing FPS, as FPS reporting just after the engine has started is inaccurate. if (hide_print_fps_attempts == 0) { @@ -2545,7 +2547,7 @@ bool Main::iteration() { if (print_fps) { print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2))); } - } else if (print_fps || GLOBAL_GET_CACHED(bool, "debug/settings/stdout/print_fps")) { + } else if (print_fps || debug_settings_stdout_print_pfs) { print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2))); } } else { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index f78a60bcb..273bba7b9 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2091,7 +2091,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (!_static) { List methods; - bool is_autocompleting_getters = GLOBAL_GET_CACHED(bool, "debug/gdscript/completion/autocomplete_setters_and_getters"); + GLOBAL_CACHED(is_autocompleting_getters, bool, "debug/gdscript/completion/autocomplete_setters_and_getters"); ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters); for (List::Element *E = methods.front(); E; E = E->next()) { if (E->get().name.begins_with("_")) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 765c2dc22..6dcdcb3d3 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -8703,13 +8703,20 @@ void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol } void GDScriptParser::_add_warning(int p_code, int p_line, const Vector &p_symbols) { - if (GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/exclude_addons") && base_path.begins_with("res://addons/")) { + GLOBAL_CACHED(global_warnings_exclude_addons, bool, "debug/gdscript/warnings/exclude_addons") + + if (global_warnings_exclude_addons && base_path.begins_with("res://addons/")) { return; } - if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/enable")) { + + GLOBAL_CACHED(global_warnings_enable, bool, "debug/gdscript/warnings/enable"); + + if (tokenizer->is_ignoring_warnings() || !global_warnings_enable) { return; } + String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower(); + if (tokenizer->get_warning_global_skips().has(warn_name)) { return; } @@ -8820,7 +8827,9 @@ Error GDScriptParser::_parse(const String &p_base_path) { // Resolve warning ignores Vector> warning_skips = tokenizer->get_warning_skips(); - bool warning_is_error = GLOBAL_GET_CACHED(bool, "debug/gdscript/warnings/treat_warnings_as_errors"); + GLOBAL_CACHED(global_treat_warnings_as_errors, bool, "debug/gdscript/warnings/treat_warnings_as_errors"); + + bool warning_is_error = global_treat_warnings_as_errors; for (List::Element *E = warnings.front(); E;) { GDScriptWarning &w = E->get(); int skip_index = -1; diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index f1845853a..03b6099ca 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -215,6 +215,9 @@ String _get_screen_sizes_tag(const Ref &p_preset) { String _get_activity_tag(const Ref &p_preset) { String orientation = _get_android_orientation_label( OS::get_singleton()->get_screen_orientation_from_string(GLOBAL_GET("display/window/handheld/orientation"))); + + GLOBAL_CACHED(display_window_resizable, bool, "display/window/size/resizable"); + String manifest_activity_text = vformat( " &p_preset) { "android:resizeableActivity=\"%s\">\n", bool_to_string(p_preset->get("package/exclude_from_recents")), orientation, - bool_to_string(GLOBAL_GET_CACHED(bool, "display/window/size/resizable"))); + bool_to_string(display_window_resizable)); manifest_activity_text += " \n"; manifest_activity_text += " \n"; return manifest_activity_text; diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index 26a4f95e4..fddc568a3 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -202,12 +202,14 @@ void MeshInstance::_resolve_skeleton_path() { bool MeshInstance::_is_global_software_skinning_enabled() { // Check if forced in project settings. - if (GLOBAL_GET_CACHED(bool, "rendering/quality/skinning/force_software_skinning")) { + GLOBAL_CACHED(force_software_skinning, bool, "rendering/quality/skinning/force_software_skinning"); + if (force_software_skinning) { return true; } // Check if enabled in project settings. - if (!GLOBAL_GET_CACHED(bool, "rendering/quality/skinning/software_skinning_fallback")) { + GLOBAL_CACHED(software_skinning_fallback, bool, "rendering/quality/skinning/software_skinning_fallback"); + if (!software_skinning_fallback) { return false; } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 6c35a9f06..71072f68f 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -655,7 +655,8 @@ bool SceneTree::iteration(float p_time) { call_group_flags(GROUP_CALL_REALTIME, "_pg_process", "trigger_physics_process"); _notify_group_pause("physics_process_internal", Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); - if (GLOBAL_GET_CACHED(bool, "physics/common/enable_pause_aware_picking")) { + GLOBAL_CACHED(global_enable_pause_aware_picking, bool, "physics/common/enable_pause_aware_picking"); + if (global_enable_pause_aware_picking) { call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_process_picking", true); } _notify_group_pause("physics_process", Node::NOTIFICATION_PHYSICS_PROCESS); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 29724ff0c..252c76f92 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -387,7 +387,9 @@ void Viewport::_notification(int p_what) { } } - if (!GLOBAL_GET_CACHED(bool, "physics/common/enable_pause_aware_picking")) { + GLOBAL_CACHED(physics_enable_pause_aware_picking, bool, "physics/common/enable_pause_aware_picking") + + if (!physics_enable_pause_aware_picking) { _process_picking(false); } } break; @@ -2914,11 +2916,15 @@ void Viewport::set_disable_input(bool p_disable) { if (p_disable == disable_input) { return; } - if (p_disable && GLOBAL_GET_CACHED(bool, "gui/common/drop_mouse_on_gui_input_disabled")) { + + GLOBAL_CACHED(global_drop_mouse_on_gui_input_disabled, bool, "gui/common/drop_mouse_on_gui_input_disabled") + + if (p_disable && global_drop_mouse_on_gui_input_disabled) { _drop_mouse_focus(); _drop_mouse_over(); _gui_cancel_tooltip(); } + disable_input = p_disable; } diff --git a/scene/resources/material/spatial_material.cpp b/scene/resources/material/spatial_material.cpp index a91def7f0..9ac2b82c4 100644 --- a/scene/resources/material/spatial_material.cpp +++ b/scene/resources/material/spatial_material.cpp @@ -2194,7 +2194,9 @@ SpatialMaterial::SpatialMaterial(bool p_orm) : flags[i] = false; } - force_vertex_shading = GLOBAL_GET_CACHED(bool, "rendering/quality/shading/force_vertex_shading"); + GLOBAL_CACHED(rendering_force_vertex_shading, bool, "rendering/quality/shading/force_vertex_shading"); + + force_vertex_shading = rendering_force_vertex_shading; diffuse_mode = DIFFUSE_BURLEY; specular_mode = SPECULAR_SCHLICK_GGX; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index d08427ee6..7106879ab 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -185,7 +185,9 @@ void AudioStreamPlaybackMicrophone::start(float p_from_pos) { return; } - if (!GLOBAL_GET_CACHED(bool, "audio/enable_audio_input")) { + GLOBAL_CACHED(audio_enable_audio_input, bool, "audio/enable_audio_input"); + + if (!audio_enable_audio_input) { WARN_PRINT("Need to enable Project settings > Audio > Enable Audio Input option to use capturing."); return; } diff --git a/servers/rendering/portals/portal_occlusion_culler.cpp b/servers/rendering/portals/portal_occlusion_culler.cpp index e26ce8370..523eecc8c 100644 --- a/servers/rendering/portals/portal_occlusion_culler.cpp +++ b/servers/rendering/portals/portal_occlusion_culler.cpp @@ -249,7 +249,9 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c // Bodge to keep settings up to date, until the project settings PR is merged #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && ((Engine::get_singleton()->get_frames_drawn() % 16) == 0)) { - _max_polys = GLOBAL_GET_CACHED(int32_t, "rendering/misc/occlusion_culling/max_active_polygons"); + GLOBAL_CACHED(occlusion_culling_max_polygons, int32_t, "rendering/misc/occlusion_culling/max_active_polygons"); + + _max_polys = occlusion_culling_max_polygons; } #endif _num_spheres = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index b5472eaa5..658ea6432 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -870,7 +870,8 @@ uint32_t RenderingServer::mesh_surface_get_format_stride(uint32_t p_format, int } void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t *r_strides) const { - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream"); + bool use_split_stream = storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); int attributes_base_offset = 0; int attributes_stride = 0; @@ -1248,7 +1249,8 @@ bool RenderingServer::_mesh_find_format(RS::PrimitiveType p_primitive, const Arr } uint32_t RenderingServer::mesh_find_format_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) { - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream"); + bool use_split_stream = storage_split_stream && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[RS::ARRAY_MAX]; @@ -1268,7 +1270,8 @@ uint32_t RenderingServer::mesh_find_format_from_arrays(PrimitiveType p_primitive } void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) { - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream"); + bool use_split_stream = storage_split_stream && !(p_compress_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[RS::ARRAY_MAX]; @@ -1338,7 +1341,8 @@ void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_p } Array RenderingServer::_get_array_from_surface(uint32_t p_format, PoolVector p_vertex_data, int p_vertex_len, PoolVector p_index_data, int p_index_len) const { - bool use_split_stream = GLOBAL_GET_CACHED(bool, "rendering/misc/mesh_storage/split_stream") && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); + GLOBAL_CACHED(storage_split_stream, bool, "rendering/misc/mesh_storage/split_stream"); + bool use_split_stream = storage_split_stream && !(p_format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE); uint32_t offsets[ARRAY_MAX]; uint32_t strides[RS::ARRAY_MAX]; From 56ce6911eb5b10bba6869230541ef220b21ded4c Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 12:55:17 +0200 Subject: [PATCH 15/16] Moved the static_assert in GLOBAL_CACHED to the top. --- core/config/project_settings.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/config/project_settings.h b/core/config/project_settings.h index c2336478c..ca44e0504 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -224,19 +224,19 @@ Variant _GLOBAL_DEF_ALIAS(const String &p_var, const String &p_old_name, const V #define GLOBAL_DEF_ALIAS_RST(m_var, m_old_name, m_value) _GLOBAL_DEF(m_var, m_old_name, m_value, true) #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) -#define GLOBAL_CACHED(m_name, m_type, m_setting_name) \ - static m_type m_name; \ - { \ - static_assert(HAS_TRIVIAL_DESTRUCTOR(m_type), "GLOBAL_CACHED must use a trivial type that allows static lifetime."); \ - static uint32_t local_version = 0; \ - static Mutex local_mutex; \ - uint32_t new_version = ProjectSettings::get_singleton()->get_version(); \ - if (local_version != new_version) { \ - local_mutex.lock(); \ - local_version = new_version; \ - m_name = ProjectSettings::get_singleton()->get(m_setting_name); \ - local_mutex.unlock(); \ - } \ +#define GLOBAL_CACHED(m_name, m_type, m_setting_name) \ + static_assert(HAS_TRIVIAL_DESTRUCTOR(m_type), "GLOBAL_CACHED must use a trivial type that allows static lifetime."); \ + static m_type m_name; \ + { \ + static uint32_t local_version = 0; \ + static Mutex local_mutex; \ + uint32_t new_version = ProjectSettings::get_singleton()->get_version(); \ + if (local_version != new_version) { \ + local_mutex.lock(); \ + local_version = new_version; \ + m_name = ProjectSettings::get_singleton()->get(m_setting_name); \ + local_mutex.unlock(); \ + } \ } #endif // PROJECT_SETTINGS_H From d231b16ed05a5f81284ab2b76f0f58b99b1e3fd1 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 31 Mar 2025 13:30:23 +0200 Subject: [PATCH 16/16] Mark last backported commit. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a974353..b92cb2bf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [Master] -- Backported everything up to and including https://github.com/godotengine/godot/commit/815484b595250f7dca4652f35f55cc269d2bc472 merge commit: https://github.com/godotengine/godot/commit/5a50868b602cd77f0ffffa0405e0abdb400d7abb +- Backported everything up to and including https://github.com/godotengine/godot/commit/31935d6d636ed463f607b3bfeadb604404ec53ca merge commit: https://github.com/godotengine/godot/commit/157f8805c215adc84ac26e14009e02b916336699 ## [4.5.0]