Physics Interpolation - add helper warnings

When physics interpolation is active on a node, it is essential that transforms are updated during "_physics_process()" rather than "_process()" calls, for the interpolation to give the correct result.

This PR adds optional warnings for instances, cameras and multimeshes which can flag updates being incorrectly called, and thus make these problems much easier to fix.
This commit is contained in:
lawnjelly 2022-04-26 12:59:53 +01:00
parent 22d040dfb4
commit ad9b2b3794
4 changed files with 115 additions and 7 deletions

View File

@ -414,6 +414,10 @@
<member name="debug/settings/gdscript/max_call_stack" type="int" setter="" getter="" default="1024"> <member name="debug/settings/gdscript/max_call_stack" type="int" setter="" getter="" default="1024">
Maximum call stack allowed for debugging GDScript. Maximum call stack allowed for debugging GDScript.
</member> </member>
<member name="debug/settings/physics_interpolation/enable_warnings" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables warnings which can help pinpoint where nodes are being incorrectly updated, which will result in incorrect interpolation and visual glitches.
When a node is being interpolated, it is essential that the transform is set during [method Node._physics_process] (during a physics tick) rather than [method Node._process] (during a frame).
</member>
<member name="debug/settings/profiler/max_functions" type="int" setter="" getter="" default="16384"> <member name="debug/settings/profiler/max_functions" type="int" setter="" getter="" default="16384">
Maximum amount of functions per frame allowed when profiling. Maximum amount of functions per frame allowed when profiling.
</member> </member>

View File

@ -1224,6 +1224,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF("debug/settings/stdout/print_fps", false); GLOBAL_DEF("debug/settings/stdout/print_fps", false);
GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false);
GLOBAL_DEF("debug/settings/physics_interpolation/enable_warnings", true);
if (!OS::get_singleton()->_verbose_stdout) { // Not manually overridden. if (!OS::get_singleton()->_verbose_stdout) { // Not manually overridden.
OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout"); OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout");
} }

View File

@ -33,6 +33,10 @@
#include "core/os/os.h" #include "core/os/os.h"
#include "core/print_string.h" #include "core/print_string.h"
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
#include "core/project_settings.h"
#endif
Rasterizer *(*Rasterizer::_create_func)() = nullptr; Rasterizer *(*Rasterizer::_create_func)() = nullptr;
Rasterizer *Rasterizer::create() { Rasterizer *Rasterizer::create() {
@ -350,6 +354,16 @@ void RasterizerStorage::multimesh_instance_set_transform(RID p_multimesh, int p_
ptr[11] = t.origin.z; ptr[11] = t.origin.z;
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi); _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("Interpolated MultiMesh transform should usually be set during physics process (possibly benign).");
}
}
#endif
return; return;
} }
} }
@ -498,6 +512,15 @@ void RasterizerStorage::multimesh_set_as_bulk_array_interpolated(RID p_multimesh
mmi->_data_prev = p_array_prev; mmi->_data_prev = p_array_prev;
mmi->_data_curr = p_array; mmi->_data_curr = p_array;
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi); _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("Interpolated MultiMesh transform should usually be set during physics process (possibly benign).");
}
}
#endif
} }
} }
@ -507,6 +530,15 @@ void RasterizerStorage::multimesh_set_as_bulk_array(RID p_multimesh, const PoolV
if (mmi->interpolated) { if (mmi->interpolated) {
mmi->_data_curr = p_array; mmi->_data_curr = p_array;
_multimesh_add_to_interpolation_lists(p_multimesh, *mmi); _multimesh_add_to_interpolation_lists(p_multimesh, *mmi);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("Interpolated MultiMesh transform should usually be set during physics process (possibly benign).");
}
}
#endif
return; return;
} }
} }

View File

@ -103,14 +103,37 @@ void VisualServerScene::camera_set_transform(RID p_camera, const Transform &p_tr
camera->transform = p_transform.orthonormalized(); camera->transform = p_transform.orthonormalized();
if (_interpolation_data.interpolation_enabled && camera->interpolated) { if (_interpolation_data.interpolation_enabled) {
if (!camera->on_interpolate_transform_list) { if (camera->interpolated) {
_interpolation_data.camera_transform_update_list_curr->push_back(p_camera); if (!camera->on_interpolate_transform_list) {
camera->on_interpolate_transform_list = true; _interpolation_data.camera_transform_update_list_curr->push_back(p_camera);
} camera->on_interpolate_transform_list = true;
}
// decide on the interpolation method .. slerp if possible // decide on the interpolation method .. slerp if possible
camera->interpolation_method = TransformInterpolator::find_method(camera->transform_prev.basis, camera->transform.basis); camera->interpolation_method = TransformInterpolator::find_method(camera->transform_prev.basis, camera->transform.basis);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
// Effectively a WARN_PRINT_ONCE but after a certain number of occurrences.
static int32_t warn_count = -256;
if ((warn_count == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("Interpolated Camera transform should usually be set during physics process (possibly benign).");
}
warn_count++;
}
#endif
} else {
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = -256;
if ((warn_count == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
WARN_PRINT("Non-interpolated Camera transform should not usually be set during physics process (possibly benign).");
}
warn_count++;
}
#endif
}
} }
} }
@ -808,6 +831,30 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform &
#endif #endif
instance->transform = p_transform; instance->transform = p_transform;
_instance_queue_update(instance, true); _instance_queue_update(instance, true);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if ((_interpolation_data.interpolation_enabled && !instance->interpolated) && (Engine::get_singleton()->is_in_physics_frame())) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
String node_name;
ObjectID id = instance->object_id;
if (id != 0) {
if (ObjectDB::get_instance(id)) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
if (node) {
node_name = "\"" + node->get_name() + "\"";
} else {
node_name = "\"unknown\"";
}
}
}
WARN_PRINT("Non-interpolated Instance " + node_name + " transform should usually not be set during physics process (possibly benign).");
}
}
#endif
return; return;
} }
@ -861,6 +908,29 @@ void VisualServerScene::instance_set_transform(RID p_instance, const Transform &
} }
_instance_queue_update(instance, true); _instance_queue_update(instance, true);
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
if (!Engine::get_singleton()->is_in_physics_frame()) {
static int32_t warn_count = 0;
warn_count++;
if (((warn_count % 2048) == 0) && GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
String node_name;
ObjectID id = instance->object_id;
if (id != 0) {
if (ObjectDB::get_instance(id)) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
if (node) {
node_name = "\"" + node->get_name() + "\"";
} else {
node_name = "\"unknown\"";
}
}
}
WARN_PRINT("Interpolated Instance " + node_name + " transform should usually be set during physics process (possibly benign).");
}
}
#endif
} }
void VisualServerScene::InterpolationData::notify_free_camera(RID p_rid, Camera &r_camera) { void VisualServerScene::InterpolationData::notify_free_camera(RID p_rid, Camera &r_camera) {