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

[3.x] Add deferred notifications #65581

Closed
wants to merge 1 commit into from
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
30 changes: 25 additions & 5 deletions scene/main/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,19 @@ void Node::move_child(Node *p_child, int p_pos) {
}
// notification second
move_child_notify(p_child);
for (int i = motion_from; i <= motion_to; i++) {
data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT);

if (is_inside_tree()) {
SceneTree *tree = get_tree();

for (int i = motion_from; i <= motion_to; i++) {
tree->send_deferred_notification(data.children[i], SceneTree::DEFERRED_NOTIFICATION_MOVED_IN_PARENT);
}
} else {
for (int i = motion_from; i <= motion_to; i++) {
data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT);
}
}

p_child->_propagate_groups_dirty();

data.blocked--;
Expand Down Expand Up @@ -1352,9 +1362,19 @@ void Node::remove_child(Node *p_child) {
child_count = data.children.size();
children = data.children.ptrw();

for (int i = idx; i < child_count; i++) {
children[i]->data.pos = i;
children[i]->notification(NOTIFICATION_MOVED_IN_PARENT);
if (is_inside_tree()) {
SceneTree *tree = get_tree();

for (int i = idx; i < child_count; i++) {
children[i]->data.pos = i;
tree->send_deferred_notification(children[i], SceneTree::DEFERRED_NOTIFICATION_MOVED_IN_PARENT);
}
} else {
// send notifications directly, less efficient
for (int i = idx; i < child_count; i++) {
children[i]->data.pos = i;
children[i]->notification(NOTIFICATION_MOVED_IN_PARENT);
}
}

p_child->data.parent = nullptr;
Expand Down
14 changes: 14 additions & 0 deletions scene/main/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ class Node : public Object {

int process_priority;

// Will probably end up using 32 bits anyway (we don't need 32 flags here), but just in case...
// Can possibly be combined with one of the other wasteful data types above.
uint8_t deferred_notification_pending_flags = 0;

// Keep bitpacked values together to get better packing
PauseMode pause_mode : 2;
PhysicsInterpolationMode physics_interpolation_mode : 2;
Expand Down Expand Up @@ -513,6 +517,16 @@ class Node : public Object {
const Map<StringName, MultiplayerAPI::RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
const Map<StringName, MultiplayerAPI::RPCMode>::Element *get_node_rset_mode(const StringName &p_property);

bool check_and_set_notification_pending(uint8_t p_notification_flag) {
static_assert(SceneTree::DEFERRED_NOTIFICATION_MAX < 8, "To support more than 8 flags, increase the storage type.");
if (data.deferred_notification_pending_flags & p_notification_flag) {
return false; // already set
}
data.deferred_notification_pending_flags |= p_notification_flag;
return true;
}
void clear_notification_pending(uint8_t p_notification_flag) { data.deferred_notification_pending_flags &= ~p_notification_flag; }

Node();
~Node();
};
Expand Down
63 changes: 63 additions & 0 deletions scene/main/scene_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,66 @@ void SceneTree::make_group_changed(const StringName &p_group) {
}
}

void SceneTree::send_deferred_notification(Node *p_node, DeferredNotificationType p_deferred_notification) {
// Uncomment the line below to use a passthrough rather than deferred.
// #define GODOT_SCENE_TREE_DEFERRED_NOTIFICATION_PASSTHROUGH
#ifdef GODOT_SCENE_TREE_DEFERRED_NOTIFICATION_PASSTHROUGH
int notification = 0;

switch (p_deferred_notification) {
case DEFERRED_NOTIFICATION_MOVED_IN_PARENT: {
notification = Node::NOTIFICATION_MOVED_IN_PARENT;
} break;
default: {
ERR_FAIL_MSG("Deferred notification not supported");
} break;
}

p_node->notification(notification);
#else

ERR_FAIL_NULL(p_node);

// We keep a counter on each node for each deferred notification.
// This allows us to check whether a duplicate is being attempted, and reject it.
if (p_node->check_and_set_notification_pending(1 << p_deferred_notification)) {
ObjectID id = p_node->get_instance_id();
_deferred_notification_lists[p_deferred_notification].push_back(id);
}
#endif
}

void SceneTree::flush_deferred_notifications() {
for (unsigned int n = 0; n < DEFERRED_NOTIFICATION_MAX; n++) {
int notification = 0;

switch (n) {
case DEFERRED_NOTIFICATION_MOVED_IN_PARENT: {
notification = Node::NOTIFICATION_MOVED_IN_PARENT;
} break;
default: {
ERR_CONTINUE_MSG(true, "Deferred notification not supported");
} break;
}
uint32_t notification_flag = 1 << notification;

for (unsigned int i = 0; i < _deferred_notification_lists[n].size(); i++) {
ObjectID id = (_deferred_notification_lists[n])[i];
Object *obj = ObjectDB::get_instance(id);
if (obj) {
obj->notification(notification);

// Should always be a node by definition
// (only nodes get sent deferred notifications currently).
Node *node = (Node *)obj;
node->clear_notification_pending(notification_flag);
}
}

_deferred_notification_lists[n].clear();
}
}

void SceneTree::flush_transform_notifications() {
SelfList<Node> *n = xform_change_list.first();
while (n) {
Expand Down Expand Up @@ -563,6 +623,7 @@ void SceneTree::iteration_end() {

bool SceneTree::iteration(float p_time) {
root_lock++;
flush_deferred_notifications();

current_frame++;

Expand Down Expand Up @@ -617,6 +678,7 @@ bool SceneTree::idle(float p_time) {
//print_line("TEXTURE RAM: "+itos(VS::get_singleton()->get_render_info(VS::INFO_TEXTURE_MEM_USED)));

root_lock++;
flush_deferred_notifications();

MainLoop::idle(p_time);

Expand Down Expand Up @@ -686,6 +748,7 @@ bool SceneTree::idle(float p_time) {

process_tweens(p_time, false);

flush_deferred_notifications(); // flush again any notifications issued in the idle before the draw occurs
flush_transform_notifications(); //additional transforms after timers update

_call_idle_callbacks();
Expand Down
9 changes: 9 additions & 0 deletions scene/main/scene_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ class SceneTree : public MainLoop {
STRETCH_ASPECT_EXPAND,
};

enum DeferredNotificationType {
DEFERRED_NOTIFICATION_MOVED_IN_PARENT = 0,
DEFERRED_NOTIFICATION_MAX = 1,
};

private:
struct Group {
Vector<Node *> nodes;
Expand All @@ -110,6 +115,8 @@ class SceneTree : public MainLoop {
void physics_process();
} _client_physics_interpolation;

LocalVector<ObjectID> _deferred_notification_lists[DEFERRED_NOTIFICATION_MAX];

Viewport *root;

uint64_t tree_version;
Expand Down Expand Up @@ -302,8 +309,10 @@ class SceneTree : public MainLoop {
void call_group(const StringName &p_group, const StringName &p_function, VARIANT_ARG_LIST);
void notify_group(const StringName &p_group, int p_notification);
void set_group(const StringName &p_group, const String &p_name, const Variant &p_value);
void send_deferred_notification(Node *p_node, DeferredNotificationType p_deferred_notification);

void flush_transform_notifications();
void flush_deferred_notifications();

virtual void input_text(const String &p_text);
virtual void input_event(const Ref<InputEvent> &p_event);
Expand Down