You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Notification methods in Godot are a special case that don't work like the rest of the API. They are implemented in the core layer of the engine, and scripts and extensions have their own api to handle them, unlike regular registered methods.
The reason for that design is because of the dual nature of _notification().
Most Godot methods fall into 2 categories:
The non-virtual methods that will be generated as part of the API and called directly by the scripts. Godot already implemented their behaviour, and they are not supposed to be overwritten afterward.
The virtual methods. They are the reverse of the previous type. They are supposed to be implemented by users and Godot calls them as part of its regular lifecycle callbacks (_enter_tree(), _process()) or in specific situation.
_notification is actually both at the same time. A lot of Godot classes already have an implementation for it, but their children can overide them as well as script.
What is peculiar about it is that Godot is using macro black magic and method pointers to call the whole inheritance chain of implementation. On top of that, those methods can be called in parent to child order or child to parent order (depending on whether the notification is supposed to be initializing or destroying something, it's the same logic as the order of constructors and destructors for classes).
Because of those requirements, we can't implement notifications like regular methods in Godot. We tested and it doesn't seem possible to call a method in a non-virtual/polymorphic way in Kotlin (Meaning that if class B inherit A, we can't call A's implemention on B if it overrides it).
And we can't ask users to use super() either because the actual call order also depends on the reverse argument, which would lead to weird and error-prone implementation.
Right now the trick is using the following syntax:
It works well. Behind the scene, we are never calling that function during the game execution. Instead, we call it at the very beginning ,for each script, and retrieve a lambda from it. Later, that list of lambda is then executed in natural or reverse order depending on the reverse argument.
The main issue comes with the way Godot uses notifications and the cost of JNI calls. Once you implement that method for any scripts or parent in your inheritance tree, Godot is going to call that method for every single notification it receives. The engine has dozens of kinds of notifications split between many different classes. And Nodes being organized in a tree structure, they propagate the notifications they receive to all their children. This generates a lot of JNI calls if you happen to have a Node implementing that method.
Most of the time, those calls will be useless because an object only cares about a very small amount of notifications. The method will simply be called without any computation being actually done. Ideally, we don't want to make a JNI call if we know the call won't do anything.
I then propose to change the way notifications are implemented by users by making them register the notification that must be processed as well.
I am not sure of what syntax would be best but I have several suggestions:
Same as today, but we return a list of GodotNotification alongside the notification integer:
@RegisterFunction
overridefun_notification() =listOf(
godotNotification(NOTIFICATION_ENTER_TREE) { // Your code }
godotNotification(NOTIFICATION_EXIT_TREE) { // Your code }
)
Individual methods with new annotation:
@RegisterNotification
funnotification1() = godotNotification(NOTIFICATION_ENTER_TREE) { // Your code }
@RegisterNotification
funnotification2() = godotNotification(NOTIFICATION_EXIT_TREE) { // Your code }// OR
@RegisterNotification(NOTIFICATION_ENTER_TREE)
funnotification1() = godotNotification { // Your code }
@RegisterNotification(NOTIFICATION_EXIT_TREE)
funnotification2() = godotNotification { // Your code }
Static methods:
object:GodotNotification<YourScript> {
funnotification1() = godotNotification(NOTIFICATION_ENTER_TREE) { // Your code }funnotification2() = godotNotification(NOTIFICATION_EXIT_TREE) { // Your code }
}
With that kind of design, we can send to the C++ the list of notification supported and only make a JNI call when it's a match, avoiding easily hundreds/thousands of JNI call per frame if a common node with a JVM script need notifications.
The text was updated successfully, but these errors were encountered:
CedNaru
changed the title
Proposal: Optimize notifications.
Proposal: optimize notifications.
Sep 20, 2024
Notification methods in Godot are a special case that don't work like the rest of the API. They are implemented in the core layer of the engine, and scripts and extensions have their own api to handle them, unlike regular registered methods.
The reason for that design is because of the dual nature of
_notification()
.Most Godot methods fall into 2 categories:
_enter_tree()
,_process()
) or in specific situation._notification
is actually both at the same time. A lot of Godot classes already have an implementation for it, but their children can overide them as well as script.What is peculiar about it is that Godot is using macro black magic and method pointers to call the whole inheritance chain of implementation. On top of that, those methods can be called in parent to child order or child to parent order (depending on whether the notification is supposed to be initializing or destroying something, it's the same logic as the order of constructors and destructors for classes).
Because of those requirements, we can't implement notifications like regular methods in Godot. We tested and it doesn't seem possible to call a method in a non-virtual/polymorphic way in Kotlin (Meaning that if class B inherit A, we can't call A's implemention on B if it overrides it).
And we can't ask users to use
super()
either because the actual call order also depends on thereverse
argument, which would lead to weird and error-prone implementation.Right now the trick is using the following syntax:
It works well. Behind the scene, we are never calling that function during the game execution. Instead, we call it at the very beginning ,for each script, and retrieve a lambda from it. Later, that list of lambda is then executed in natural or reverse order depending on the
reverse
argument.The main issue comes with the way Godot uses notifications and the cost of JNI calls. Once you implement that method for any scripts or parent in your inheritance tree, Godot is going to call that method for every single notification it receives. The engine has dozens of kinds of notifications split between many different classes. And Nodes being organized in a tree structure, they propagate the notifications they receive to all their children. This generates a lot of JNI calls if you happen to have a Node implementing that method.
Most of the time, those calls will be useless because an object only cares about a very small amount of notifications. The method will simply be called without any computation being actually done. Ideally, we don't want to make a JNI call if we know the call won't do anything.
I then propose to change the way notifications are implemented by users by making them register the notification that must be processed as well.
I am not sure of what syntax would be best but I have several suggestions:
Same as today, but we return a list of GodotNotification alongside the notification integer:
Individual methods with new annotation:
Static methods:
With that kind of design, we can send to the C++ the list of notification supported and only make a JNI call when it's a match, avoiding easily hundreds/thousands of JNI call per frame if a common node with a JVM script need notifications.
The text was updated successfully, but these errors were encountered: