fix: node graph edge display, teardown crashes, and event/audio lifetime bugs

- capi: oakengine_node_output_connection_at/_at_ex returned the source
  node as the connection destination; the actual destination is
  conn.second.node(). Out-edge enumeration was useless, so the node
  view could only draw in-edges and randomly lost whichever edges
  needed the out-edge path (random per context build order).
  Regression test in oakengine_node_test
- project teardown: Project::clear() pre-notifies node removal while
  nodes are fully constructed (observers used to crash on
  half-destroyed nodes); childEvent suppresses the removal dance while
  clearing; Node::disconnect_all/disconnect_edge get a silent mode for
  teardown so no invalidation/events touch dying members
  (is_being_cleared); ClipBlock marker disconnect guarded against
  dead viewer/markers; ProjectCopier and PreviewAutoCacher drop
  project references on Project::destroyed instead of disconnecting
  dead objects at shutdown
- preview: add oakengine_preview_request_get_audio_sample_count; the
  viewer queried sample count by passing nullptr to get_audio_samples
  which rejects it, so all playback audio was silently dropped
- app: fix unterminated input-id memcpy in ResolveGroupInput
  (nodeparamviewitem, widgetbridge) that corrupted every parameter id
- app: unsubscribe raw C-API event subscriptions in destructors of
  NodeParamViewKeyframeControl, NodeParamViewConnectedLabel and
  ExportDialog; playhead events used to fire into dead widgets
  (crash when dragging the playhead)
- tests: preview request roundtrip (video frame + audio range) and
  free-while-active teardown coverage; env-gated OAK_DEBUG_EDGES /
  OAK_DEBUG_INVALID_INPUT diagnostics
- docs: investigation notes in docs/zh/
This commit is contained in:
2026-08-02 14:29:49 +08:00
parent 30853cbcfd
commit a59c33715f
25 changed files with 441 additions and 32 deletions
+27
View File
@@ -255,6 +255,12 @@ bool NodeViewContext::child_input_disconnected(OakEngineNode *output,
for (int i = 0; i < edges_.size(); i++) {
NodeViewEdge *e = edges_.at(i);
if (e->output() == oak::Node(output) && e->input() == input) {
if (qEnvironmentVariableIsSet("OAK_DEBUG_EDGES")) {
qWarning("EDGE-DEBUG: edge removed: %p -> %p (%s,%d) ctx=%p",
(void *)output, (void *)input.node_handle(),
qPrintable(input.input_id()), input.element(),
(void *)this);
}
delete e;
edges_.removeAt(i);
return true;
@@ -413,6 +419,11 @@ void NodeViewContext::mousePressEvent(QGraphicsSceneMouseEvent *event)
void NodeViewContext::add_node_internal(OakEngineNode *node, NodeViewItem *item)
{
if (qEnvironmentVariableIsSet("OAK_DEBUG_EDGES")) {
qWarning("EDGE-DEBUG: node added to ctx %p: %p", (void *)this,
(void *)node);
}
node_subs_[node].append(bridge_->subscribe(
reinterpret_cast<void *>(node),
OAKENGINE_EVENT_NODE_INPUT_CONNECTED));
@@ -436,6 +447,11 @@ void NodeViewContext::add_node_internal(OakEngineNode *node, NodeViewItem *item)
oak::Input ai(conn.node.handle(), conn.input_id, conn.element);
add_edge_internal(node, ai, item,
other_item->get_item_for_input(ai));
} else if (qEnvironmentVariableIsSet("OAK_DEBUG_EDGES")) {
qWarning("EDGE-DEBUG: out-edge skipped (no item for %p): %p -> %p (%s) ctx=%p",
(void *)conn.node.handle(), (void *)node,
(void *)conn.node.handle(),
qPrintable(conn.input_id), (void *)this);
}
}
}
@@ -449,6 +465,11 @@ void NodeViewContext::add_node_internal(OakEngineNode *node, NodeViewItem *item)
oak::Input ai(conn.node.handle(), conn.input_id, conn.element);
add_edge_internal(conn.source_node.handle(), ai, other_item,
item->get_item_for_input(ai));
} else if (qEnvironmentVariableIsSet("OAK_DEBUG_EDGES")) {
qWarning("EDGE-DEBUG: in-edge skipped (no item for %p): %p -> %p (%s) ctx=%p",
(void *)conn.source_node.handle(),
(void *)conn.source_node.handle(), (void *)node,
qPrintable(conn.input_id), (void *)this);
}
}
}
@@ -461,6 +482,12 @@ void NodeViewContext::add_edge_internal(OakEngineNode *output, const oak::Input
return;
}
if (qEnvironmentVariableIsSet("OAK_DEBUG_EDGES")) {
qWarning("EDGE-DEBUG: edge added: %p -> %p (%s,%d) ctx=%p", (void *)output,
(void *)input.node_handle(), qPrintable(input.input_id()),
input.element(), (void *)this);
}
NodeViewEdge *edge_ui = new NodeViewEdge(oak::Node(output), input, from, to, this);
edge_ui->adjust();