-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Disconnect graph connections when ports are removed in visual shaders #80312
Conversation
Note: this PR is a re creation of another one I made two weeks ago, which you can find here. The original PR had changes applied directly on the master branch, instead of a separate one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some notes about the style, but my main concern is the overall approach.
While the #79417 mentions a couple of specific nodes, isn't this a more general problem? A number of visual shader nodes can change the number of their input and output connections. So a solution should be generic as well.
We need a new signal, and maybe a more robust way to track if the number of ports was changed, or we can just emit the signal manually every time this happens in any node. Then you can connect to that signal instead. And you probably don't need 2 different methods, you can check both sides in one.
bool port_count_changed = p_op_type < op_type; | ||
op_type = p_op_type; | ||
emit_changed(); | ||
if (port_count_changed) { | ||
emit_changed(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect for several reasons. First of all, the signal means that the resource has been changed for any reason, which is always true by this line since we rule out the new and the old op type being the same at the start of this method.
And second, your proposed change assumes that the order of enum values would somehow correspond to the number of available ports. While it's true in this case, it's a weak relation and such code can very easily lead to annoying mistakes and bugs. We could discuss the merits of it, but as mentioned above there is no need for this change at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check here is not incorrect, although admittedly I should have chosen another name for the boolean. What I am testing specifically is whether the number of ports has decreased, not simply changed, because we do not need to do anything when the port count increased and we do not have any way to know which type of change it was when handing the signal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the signal is there to indicate that the resource has changed, whether the number of ports increased, decreased, or remained the same, it's not relevant. The signal is emitted because a new value for the op type was set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah as I said, I agree this is by no means ideal. I'd be happy to create a new signal instead, I just didn't know how and this one happened to be disconnected. Do you know how to do that by any chance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To add a new signal you need to use the ADD_SIGNAL
macro in the _bind_methods
method of the class that you want to add it to. In this case that would probably be VisualShaderNode
. Then just emit it with emit_signal("your_signal_name")
and connect to it normally.
Sorry about all the unnecessary includes I think clangd added them for some reason. Normally I would agree that the issue is a bit more general, but on the other hand the contribution guidelines explicitly discourage to try implementing general solutions so I wasn't quite sure what to do. Which is also the reason I made the assumption about the enum values corresponding to the port count, both because it made the code simpler and because having any other way would arguably be weird. I do agree adding another signal is the way to go, I tried and failed to do so (yes I still have much to learn about how the code base works), and since this signal was free I simply used it. |
Yes, that's correct, but doesn't apply to this situation. General solution that is discouraged would be something that doesn't have a specific target in mind or has a very broad set of targets. In this case you are dealing with one thing — visual shader nodes, some of which can dynamically shed some of their ports, and we need to handle it properly. Fixing the issue for just the two nodes and leaving the problem open for others potentially affected doesn't make much sense. |
fd3bea3
to
41a376c
Compare
A new 'node_ports_removed' signal was added to the VisualShader class. This signal gets connected to the corresponding '_node_ports_removed' method of the VisualShaderEditor class, which takes care of removing invalid connections whenever the signal is emitted.
41a376c
to
3d6e78b
Compare
if (!node.is_valid()) | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!node.is_valid()) | |
return; | |
if (!node.is_valid()) { | |
return; | |
} |
Code style, we use brackets always for if-statements
const int inputs = node->get_input_port_count(), | ||
outputs = node->get_output_port_count(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const int inputs = node->get_input_port_count(), | |
outputs = node->get_output_port_count(); | |
const int inputs = node->get_input_port_count(); | |
const int outputs = node->get_output_port_count(); |
Readability
@EddieBreeg I've checked it, currently, if you use undo (CTRL+Z) on that action leaves the changed connection disconnected. That should not be - the solution is to record the disconnection/connection actions by |
Here the signal is required anyway but I'll add the UndoRedo record |
I am very confused right now. Based on other examples in the code I tried creating an action and committing it, like so: void VisualShaderEditor::_node_ports_removed(int p_id) {
VisualShader::Type shader_type = visual_shader->get_shader_type();
Ref<VisualShaderNode> node = visual_shader->get_node(shader_type, p_id);
if (unlikely(!node.is_valid()))
return;
const int inputs = node->get_input_port_count();
const int outputs = node->get_output_port_count();
List<VisualShader::Connection> conns;
visual_shader->get_node_connections(shader_type, &conns);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove node ports"));
for (const VisualShader::Connection &c : conns) {
if (unlikely((c.from_node == p_id && c.from_port >= outputs) || (c.to_node == p_id && c.to_port >= inputs))) {
// graph_plugin->disconnect_nodes(shader_type, c.from_node, c.from_port, c.to_node, c.to_port);
// visual_shader->disconnect_nodes(shader_type, c.from_node, c.from_port, c.to_node, c.to_port);
undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", shader_type, c.from_node, c.from_port, c.to_node, c.to_port);
undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", shader_type, c.from_node, c.from_port, c.to_node, c.to_port);
undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", shader_type, c.from_node, c.from_port, c.to_node, c.to_port);
undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", shader_type, c.from_node, c.from_port, c.to_node, c.to_port);
}
}
undo_redo->commit_action();
} But the editor crashes as a result... |
@@ -440,6 +440,10 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool | |||
custom_node->_set_initialized(true); | |||
} | |||
|
|||
if (!p_just_update) { | |||
vsnode->connect("node_ports_removed", callable_mp(editor, &VisualShaderEditor::_node_ports_removed).bind(p_id)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the editor crashes as a result...
vsnode->connect("node_ports_removed", callable_mp(editor, &VisualShaderEditor::_node_ports_removed).bind(p_id)); | |
vsnode->connect("node_ports_removed", callable_mp(editor, &VisualShaderEditor::_node_ports_removed).bind(p_id), CONNECT_DEFERRED); |
I think you should try to use CONNECT_DEFERRED flag to prevent that crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll give it a shot thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It did fix the crash! I have another issue though: the op_type change and the connections removal are not in the same action, which means that hitting ctrl+z only recreates the connections without reverting to the previous op_type. As far as I can understand, handling removed ports should be done in _property_changed
function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then unify it to a single action, (CONNECT_DEFERRED
will not be needed in that case), remove create_action/commit_action from the _node_ports_removed
and uses the same action as usual just call it before the commit_action
of the op_type change function has been called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's what I was proposing: the commit_action
gets called in VisualShaderNodePluginDefaultEditor::_property_changed
so all I need to do is check for invalid connections there.
It means that invalid connections will be checked everytime a property gets called whether this property has an impact on the number of ports or not. On the other hand, it also means that no matter what happens in the future invalid connections will always be removed as they should be
Sorry for the long wait. It looks like this brach doesn't contain your most recent changes (undo/redo). Could you push those? |
Superseded by #89810. Thanks for the contribution! |
For VectorCompose and VectorDecompose nodes, the 'changed' signal is connected to two new functions, which handle the deletion of connections from/to deleted ports.
Note: this revealed a new bug, described in #79417 (comment).
I don't see this new issue being directly caused by the code I wrote, and since this is my first ever contribution to this project, I am not too familiar with the code, so I decided to go ahead with the PR anyway.
Bugsquad edit, fixes #79417