Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C#: Optimize GetProcessDeltaTime() and GetPhysicsProcessDeltaTime() #74078

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/object/script_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,12 @@ void ScriptLanguage::get_core_type_words(List<String> *p_core_type_words) const
void ScriptLanguage::frame() {
}

void ScriptLanguage::set_physics_process_delta_time(double p_time) {
}

void ScriptLanguage::set_process_delta_time(double p_time) {
}

bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) {
if (script->is_placeholder_fallback_enabled()) {
return false;
Expand Down
2 changes: 2 additions & 0 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ class ScriptLanguage : public Object {
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) = 0;

virtual void frame();
virtual void set_physics_process_delta_time(double p_time);
virtual void set_process_delta_time(double p_time);
Comment on lines +431 to +432
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of using the process_frame and physics_frame signals, but our callback needs to have priority, as we want the delta time to be updated before the other callbacks are called.


virtual bool handles_global_class_type(const String &p_type) const { return false; }
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const { return String(); }
Expand Down
8 changes: 8 additions & 0 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,14 @@ void CSharpLanguage::frame() {
}
}

void CSharpLanguage::set_physics_process_delta_time(double p_time) {
GDMonoCache::managed_callbacks.ScriptManagerBridge_SetProcessDeltaTime(p_time, /* physics: */ true);
}

void CSharpLanguage::set_process_delta_time(double p_time) {
GDMonoCache::managed_callbacks.ScriptManagerBridge_SetProcessDeltaTime(p_time, /* physics: */ false);
}

struct CSharpScriptDepSort {
// Must support sorting so inheritance works properly (parent must be reloaded first)
bool operator()(const Ref<CSharpScript> &A, const Ref<CSharpScript> &B) const {
Expand Down
2 changes: 2 additions & 0 deletions modules/mono/csharp_script.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ class CSharpLanguage : public ScriptLanguage {
}

void frame() override;
void set_physics_process_delta_time(double p_time) override;
void set_process_delta_time(double p_time) override;

/* TODO? */ void get_public_functions(List<MethodInfo> *p_functions) const override {}
/* TODO? */ void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {}
Expand Down
28 changes: 25 additions & 3 deletions modules/mono/editor/bindings_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2057,7 +2057,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf

// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
if (!p_imethod.is_virtual && !p_imethod.requires_object_call && !p_imethod.declare_as_partial) {
p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
<< INDENT1 "private static readonly IntPtr " << method_bind_field << " = ";

Expand Down Expand Up @@ -2112,15 +2112,26 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("virtual ");
}

if (cs_in_expr_is_unsafe) {
if (p_imethod.declare_as_partial) {
p_output.append("partial ");
}

if (cs_in_expr_is_unsafe && !p_imethod.declare_as_partial) {
p_output.append("unsafe ");
}

String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters);

p_output.append(return_cs_type + " ");
p_output.append(p_imethod.proxy_name + "(");
p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L1);
p_output.append(arguments_sig + ")");

if (p_imethod.declare_as_partial) {
p_output.append(";\n");
return OK;
}

p_output.append("\n" OPEN_BLOCK_L1);

if (p_imethod.is_virtual) {
// Godot virtual method must be overridden, therefore we return a default value by default.
Expand Down Expand Up @@ -3252,6 +3263,17 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
class_list.pop_front();
}

// Declare `GetProcessDeltaTime` and `GetPhysicsProcessDeltaTime` as partial.
// This allows us to use a custom implementation of these methods, which returns a cached value.

const MethodInterface *process_delta_time_imethod = obj_types.get("Node").find_method_by_name("get_process_delta_time");
ERR_FAIL_COND_V(process_delta_time_imethod == nullptr, false);
const_cast<MethodInterface *>(process_delta_time_imethod)->declare_as_partial = true;

const MethodInterface *physics_process_delta_time_imethod = obj_types.get("Node").find_method_by_name("get_physics_process_delta_time");
ERR_FAIL_COND_V(physics_process_delta_time_imethod == nullptr, false);
const_cast<MethodInterface *>(physics_process_delta_time_imethod)->declare_as_partial = true;

return true;
}

Expand Down
6 changes: 6 additions & 0 deletions modules/mono/editor/bindings_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ class BindingsGenerator {
*/
bool is_internal = false;

/**
* Declare the C# method with the `partial` keyword, skipping the body. Useful
* when we want to replace the generated method body with a custom implementation.
*/
bool declare_as_partial = false;

List<ArgumentInterface> arguments;

const DocData::MethodDoc *method_doc = nullptr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public unsafe struct ManagedCallbacks
public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
public delegate* unmanaged<double, godot_bool, void> ScriptManagerBridge_SetProcessDeltaTime;
public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
Expand Down Expand Up @@ -53,6 +54,7 @@ public static ManagedCallbacks Create()
DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle,
DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle,
ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
ScriptManagerBridge_SetProcessDeltaTime = &ScriptManagerBridge.SetProcessDeltaTime,
ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding,
ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance,
ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ internal static void FrameCallback()
}
}

[UnmanagedCallersOnly]
internal static void SetProcessDeltaTime(double deltaTime, godot_bool physics)
{
if (physics.ToBool())
{
Node.CachedPhysicsProcessDeltaTime = deltaTime;
}
else
{
Node.CachedProcessDeltaTime = deltaTime;
}
}

[UnmanagedCallersOnly]
internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
IntPtr godotObject)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace Godot
{
Expand Down Expand Up @@ -195,5 +197,30 @@ public T GetParentOrNull<T>() where T : class
{
return GetParent() as T;
}

internal static double CachedPhysicsProcessDeltaTime;
internal static double CachedProcessDeltaTime;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public partial double GetPhysicsProcessDeltaTime() => CachedPhysicsProcessDeltaTime;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public partial double GetProcessDeltaTime() => CachedProcessDeltaTime;
Comment on lines +204 to +210
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we hide/deprecate these methods in favor of the properties?


/// <inheritdoc cref="GetPhysicsProcessDeltaTime()"/>
public static double PhysicsDeltaTime
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => CachedPhysicsProcessDeltaTime;
}

/// <inheritdoc cref="GetProcessDeltaTime()"/>
public static double DeltaTime
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => CachedProcessDeltaTime;
}
}
}
2 changes: 2 additions & 0 deletions modules/mono/mono_gd/gd_mono_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ struct ManagedCallbacks {
using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *);
using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *);
using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)();
using FuncScriptManagerBridge_SetProcessDeltaTime = void(GD_CLR_STDCALL *)(double, bool);
using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *);
using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t);
using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
Expand Down Expand Up @@ -112,6 +113,7 @@ struct ManagedCallbacks {
FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle;
FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle;
FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback;
FuncScriptManagerBridge_SetProcessDeltaTime ScriptManagerBridge_SetProcessDeltaTime;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to add CHECK_CALLBACK_NOT_NULL to gd_mono_cache.cpp?

FuncScriptManagerBridge_CreateManagedForGodotObjectBinding ScriptManagerBridge_CreateManagedForGodotObjectBinding;
FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName;
Expand Down
8 changes: 8 additions & 0 deletions scene/main/scene_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,10 @@ bool SceneTree::physics_process(double p_time) {
MainLoop::physics_process(p_time);
physics_process_time = p_time;

for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->set_physics_process_delta_time(p_time);
}

emit_signal(SNAME("physics_frame"));

_notify_group_pause(SNAME("_physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
Expand Down Expand Up @@ -449,6 +453,10 @@ bool SceneTree::process(double p_time) {

process_time = p_time;

for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptServer::get_language(i)->set_process_delta_time(p_time);
}

if (multiplayer_poll) {
multiplayer->poll();
for (KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
Expand Down