fix: A bug and a crash.

The crash: When a linked loose socket was deleted, the link remained in the
NodeLinkCache, and caused a crash when trying to ask the already-deleted
socket for removal consent. We fixed this by reporting all socket
removals to the node tree, so that links could be correctly removed
independently of link-change calculation.

The bug: The collection getter was cached improperly; Blender ID types
can't just be saved like that. We need to search every time. Performance
seems unaffected at first glance.
main
Sofus Albert Høgsbro Rose 2024-04-12 15:58:07 +02:00
parent 480679a3c0
commit 7f2bd2e752
Signed by: so-rose
GPG Key ID: AD901CB0F3701434
5 changed files with 25 additions and 6 deletions

View File

@ -1,7 +1,7 @@
# Acute Tasks # Acute Tasks
- [x] Implement Material Import for Maxim Data - [x] Implement Material Import for Maxim Data
- [x] Implement Robust DataFlowKind for list-like / spectral-like composite types - [x] Implement Robust DataFlowKind for list-like / spectral-like composite types
- [ ] Unify random node/socket caches. - [x] Unify random node/socket caches.
- [ ] Finish the "Low-Hanging Fruit" Nodes - [ ] Finish the "Low-Hanging Fruit" Nodes
- [ ] Move preview GN trees to the asset library. - [ ] Move preview GN trees to the asset library.
@ -344,6 +344,10 @@
# Internal / Architecture # Internal / Architecture
## IDEAS
- [ ] Socket "guarding" - let nodes influence the dynamic capabilities of sockets to prevent links (with a `self.report` explanation) to an output socket that won't yet produce a value.
- [ ] Prevents some uses of loose sockets (we want less loose sockets!)
## CRITICAL ## CRITICAL
- [ ] License header UI for MaxwellSimTrees, to clarify the AGPL-compatible potentially user-selected license that trees must be distributed under. - [ ] License header UI for MaxwellSimTrees, to clarify the AGPL-compatible potentially user-selected license that trees must be distributed under.
- [ ] Document the node tree cache semantics thoroughly; it's a VERY nuanced piece of logic, and its invariants may not survive Blender versions / the author's working memory - [ ] Document the node tree cache semantics thoroughly; it's a VERY nuanced piece of logic, and its invariants may not survive Blender versions / the author's working memory
@ -463,6 +467,7 @@ This is where we keep track of them for now.
- [ ] Cloud task socket loads folders before its node shows, which can be slow (and error prone if offline) - [ ] Cloud task socket loads folders before its node shows, which can be slow (and error prone if offline)
- [ ] Dispersive fit is slow, which means lag on normal operations that rely on the fit result - fit computation should be integrated into the node, and the output socket should only appear when the fit is available. - [ ] Dispersive fit is slow, which means lag on normal operations that rely on the fit result - fit computation should be integrated into the node, and the output socket should only appear when the fit is available.
- [ ] Numerical, Physical Constant is missing entries - [ ] Numerical, Physical Constant is missing entries
- [ ] Numerical, Physical Constant is missing entries
BROKE NODES BROKE NODES
- [ ] Numerical constant doesn't switch types - [ ] Numerical constant doesn't switch types

View File

@ -13,7 +13,6 @@ PREVIEW_COLLECTION_NAME = 'BLMaxwell Visible'
#################### ####################
# - Global Collection Handling # - Global Collection Handling
#################### ####################
@functools.cache
def collection(collection_name: str, view_layer_exclude: bool) -> bpy.types.Collection: def collection(collection_name: str, view_layer_exclude: bool) -> bpy.types.Collection:
# Init the "Managed Collection" # Init the "Managed Collection"
# Ensure Collection exists (and is in the Scene collection) # Ensure Collection exists (and is in the Scene collection)

View File

@ -254,8 +254,8 @@ class MaxwellSimTree(bpy.types.NodeTree):
#################### ####################
# - Update Methods # - Update Methods
#################### ####################
def sync_node_removed(self, node: bpy.types.Node): def on_node_removed(self, node: bpy.types.Node):
"""Run by `Node.free()` when a node is being removed. """Run by `MaxwellSimNode.free()` when a node is being removed.
ONLY input socket links are removed from the NodeLink cache. ONLY input socket links are removed from the NodeLink cache.
- `self.update()` handles link-removal from existing nodes. - `self.update()` handles link-removal from existing nodes.
@ -275,6 +275,20 @@ class MaxwellSimTree(bpy.types.NodeTree):
self.node_link_cache.remove_link(link_ptr) self.node_link_cache.remove_link(link_ptr)
self.node_link_cache.remove_sockets_by_link_ptr(link_ptr) self.node_link_cache.remove_sockets_by_link_ptr(link_ptr)
def on_node_socket_removed(self, bl_socket: bpy.types.NodeSocket) -> None:
"""Run by `MaxwellSimNode._prune_inactive_sockets()` when a socket is being removed (but not the node).
Parameters:
bl_socket: The node socket that's about to be removed.
"""
# Compute About-To-Be-Freed Link Ptrs
link_ptrs = {link.as_pointer() for link in bl_socket.links}
if link_ptrs:
for link_ptr in link_ptrs:
self.node_link_cache.remove_link(link_ptr)
self.node_link_cache.remove_sockets_by_link_ptr(link_ptr)
def update(self) -> None: def update(self) -> None:
"""Monitors all changes to the node tree, potentially responding with appropriate callbacks. """Monitors all changes to the node tree, potentially responding with appropriate callbacks.

View File

@ -381,6 +381,7 @@ class MaxwellSimNode(bpy.types.Node):
A socket is considered "inactive" when it shouldn't be defined (per `self.active_socket_defs), but is present nonetheless. A socket is considered "inactive" when it shouldn't be defined (per `self.active_socket_defs), but is present nonetheless.
""" """
node_tree = self.id_data
for direc in ['input', 'output']: for direc in ['input', 'output']:
all_bl_sockets = self._bl_sockets(direc) all_bl_sockets = self._bl_sockets(direc)
active_bl_socket_defs = self.active_socket_defs(direc) active_bl_socket_defs = self.active_socket_defs(direc)
@ -400,6 +401,7 @@ class MaxwellSimNode(bpy.types.Node):
# Remove Sockets # Remove Sockets
for bl_socket in bl_sockets_to_remove: for bl_socket in bl_sockets_to_remove:
node_tree.on_node_socket_removed(bl_socket)
all_bl_sockets.remove(bl_socket) all_bl_sockets.remove(bl_socket)
def _add_new_active_sockets(self): def _add_new_active_sockets(self):
@ -964,7 +966,7 @@ class MaxwellSimNode(bpy.types.Node):
## The NodeTree keeps caches to for optimized event triggering. ## The NodeTree keeps caches to for optimized event triggering.
## However, ex. deleted nodes also deletes links, without cache update. ## However, ex. deleted nodes also deletes links, without cache update.
## By reporting that we're deleting the node, the cache stays happy. ## By reporting that we're deleting the node, the cache stays happy.
node_tree.sync_node_removed(self) node_tree.on_node_removed(self)
# Invalidate Non-Persistent Cache # Invalidate Non-Persistent Cache
## Prevents memory leak due to dangling cache entries for deleted nodes. ## Prevents memory leak due to dangling cache entries for deleted nodes.

View File

@ -210,7 +210,6 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
Returns a bool, whether or not the socket consents to the link change. Returns a bool, whether or not the socket consents to the link change.
""" """
## TODO: Crash if deleting removing linked loose sockets.
if self.locked: if self.locked:
return False return False
if self.is_output: if self.is_output: