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:
@@ -126,34 +126,48 @@ impl Traverser {
|
||||
return Err(Error::State);
|
||||
}
|
||||
|
||||
let entry = graph.get(node).ok_or(Error::NotFound)?;
|
||||
|
||||
// Build this node's input row from its upstream outputs. The
|
||||
// C++ picks the last value of the matching type per input;
|
||||
// the Rust model keys rows by input id.
|
||||
// the Rust model keys rows by input id. Inputs declared as
|
||||
// texture take the upstream texture directly (the scalar
|
||||
// chain below would otherwise hand a plugin node's tagged
|
||||
// param passthrough to a downstream clip input).
|
||||
let mut row: NodeValueRow = std::collections::BTreeMap::new();
|
||||
for (from, input_id, element) in graph.input_connections(node) {
|
||||
let _ = element;
|
||||
if let Some(from_table) = tables.get(&from) {
|
||||
let value = from_table
|
||||
.get(ValueType::Float)
|
||||
.or_else(|| from_table.get(ValueType::Int))
|
||||
.or_else(|| from_table.get(ValueType::Color))
|
||||
.or_else(|| from_table.get(ValueType::Vec2))
|
||||
.or_else(|| from_table.get(ValueType::Vec3))
|
||||
.or_else(|| from_table.get(ValueType::Vec4))
|
||||
.or_else(|| from_table.get(ValueType::Boolean))
|
||||
.or_else(|| from_table.get(ValueType::Rational))
|
||||
.or_else(|| from_table.get(ValueType::Text))
|
||||
.or_else(|| from_table.get(ValueType::Combo))
|
||||
.or_else(|| from_table.get(ValueType::StrCombo))
|
||||
.cloned()
|
||||
.unwrap_or(NodeValue::None);
|
||||
let value = if entry.core.input_data_type(&input_id)
|
||||
== Some(ValueType::Texture)
|
||||
{
|
||||
from_table
|
||||
.get(ValueType::Texture)
|
||||
.cloned()
|
||||
.unwrap_or(NodeValue::None)
|
||||
} else {
|
||||
from_table
|
||||
.get(ValueType::Float)
|
||||
.or_else(|| from_table.get(ValueType::Int))
|
||||
.or_else(|| from_table.get(ValueType::Color))
|
||||
.or_else(|| from_table.get(ValueType::Vec2))
|
||||
.or_else(|| from_table.get(ValueType::Vec3))
|
||||
.or_else(|| from_table.get(ValueType::Vec4))
|
||||
.or_else(|| from_table.get(ValueType::Boolean))
|
||||
.or_else(|| from_table.get(ValueType::Rational))
|
||||
.or_else(|| from_table.get(ValueType::Text))
|
||||
.or_else(|| from_table.get(ValueType::Combo))
|
||||
.or_else(|| from_table.get(ValueType::StrCombo))
|
||||
.or_else(|| from_table.get(ValueType::Texture))
|
||||
.cloned()
|
||||
.unwrap_or(NodeValue::None)
|
||||
};
|
||||
row.insert(input_id, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the node's behavior into its output table.
|
||||
let mut table = NodeValueTable::default();
|
||||
let entry = graph.get(node).ok_or(Error::NotFound)?;
|
||||
// The behavior writes outputs; the default no-op leaves the
|
||||
// table empty (C++ `Node::value` default).
|
||||
entry
|
||||
|
||||
Reference in New Issue
Block a user