feat(oakplugin): wire OpenFX plugins into the node graph and renderer

- oaknode: dynamic node factory registration, PluginNode value model
  pushing PluginJobPayload, traverser texture passthrough for texture
  inputs, type-stamped RefBox::get_checked.
- oakrender: PluginExecutor dependency-inversion slot; eval resolves
  and executes plugin jobs, purple frame on failure.
- oakplugin: node_factory with full OFX param -> node input
  translation (15 types, color semantics heuristic, combo ordering,
  secret/ui_group/ui_page, clip inputs), plugin instance registry,
  render executor + duplicator installation, progress reporter and
  active-viewer provider injection points, U8/U16/F16 input
  conversion with NaN scrubbing, in-place output frame writeback fix.
- gl_bridge.rs documents the wgpu<->GL interop spike: Metal-first on
  macOS rules out wgpu-hal GL interop; offscreen GL context deferred.

End-to-end tests cover registration, param translation, CPU render
pixel assertions, identity passthrough and NaN fallback.
This commit is contained in:
2026-08-18 17:15:13 +08:00
parent 9daa266189
commit 2db1615453
21 changed files with 2595 additions and 189 deletions
+40
View File
@@ -375,6 +375,18 @@ pub fn command_is_done(row: i64, out_value: *mut c_int) -> Result<()> {
Ok(())
}
/// The user-visible label of the row at `row` (the safe twin of the
/// two-stage [`command_text`]; the history panel's row query).
pub fn command_name(row: i64) -> Result<String> {
with_stack(|s| s.command_name(row).map(|n| n.to_string()))
}
/// Whether the row at `row` is done (the safe twin of
/// [`command_is_done`]; the history panel's gray-row query).
pub fn command_done(row: i64) -> Result<bool> {
with_stack(|s| s.command_is_done(row))
}
#[cfg(test)]
mod tests {
use super::*;
@@ -497,4 +509,32 @@ mod tests {
assert!(clear().is_ok());
}
/// The safe row getters answer like the C-ABI twins: labels survive an
/// undo (undone rows stay labeled) and `command_done` flips with the
/// stack pointer.
#[test]
fn value_command_name_and_done() {
let _g = LOCK.lock().unwrap_or_else(|e| e.into_inner());
assert!(clear().is_ok());
let cmd = UndoCommand::from_closures(|| {}, || {});
push(cmd, "alpha").unwrap();
assert_eq!(count().unwrap(), 2);
assert_eq!(command_name(1).unwrap(), "alpha");
assert!(command_done(1).unwrap());
assert_eq!(index().unwrap(), 2);
// Undo keeps the row labeled but marks it undone.
undo().unwrap();
assert_eq!(command_name(1).unwrap(), "alpha");
assert!(!command_done(1).unwrap());
assert_eq!(index().unwrap(), 1);
// Out-of-range rows error, they never panic.
assert!(command_name(2).is_err());
assert!(command_done(-1).is_err());
assert!(clear().is_ok());
}
}