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:
@@ -84,10 +84,29 @@ pub struct ProgressSuiteV2 {
|
||||
pub end: unsafe extern "C" fn(*mut c_void) -> c_int,
|
||||
}
|
||||
|
||||
/// progressStart:括号起点,OK(label/message 留作未来 UI 展示,
|
||||
/// 第 1 期不建模)。
|
||||
unsafe extern "C" fn progress_start_v1(_handle: *mut c_void, _label: *const c_char) -> c_int {
|
||||
caught(|| status::OK)
|
||||
/// C 字符串解码(空指针/非法 UTF-8 → 空串)。
|
||||
unsafe fn decode<'a>(p: *const c_char) -> &'a str {
|
||||
if p.is_null() {
|
||||
return "";
|
||||
}
|
||||
unsafe { std::ffi::CStr::from_ptr(p) }
|
||||
.to_str()
|
||||
.unwrap_or("")
|
||||
}
|
||||
|
||||
/// progressStart:括号起点;携 label(v2 另携 message)经
|
||||
/// [`crate::progress`] 工厂现造 UI 报告器(无工厂/已有报告器时
|
||||
/// no-op),状态恒 OK。
|
||||
unsafe extern "C" fn progress_start_v1(_handle: *mut c_void, label: *const c_char) -> c_int {
|
||||
caught(|| {
|
||||
let label = unsafe { decode(label) }.to_string();
|
||||
CURRENT.with(|c| {
|
||||
if let Some(r) = c.borrow().as_ref() {
|
||||
r.install_ui(&label, "");
|
||||
}
|
||||
});
|
||||
status::OK
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C" fn progress_update_v1(_handle: *mut c_void, progress: c_double) -> c_int {
|
||||
@@ -100,10 +119,19 @@ unsafe extern "C" fn progress_end_v1(_handle: *mut c_void) -> c_int {
|
||||
|
||||
unsafe extern "C" fn progress_start_v2(
|
||||
_handle: *mut c_void,
|
||||
_label: *const c_char,
|
||||
_message: *const c_char,
|
||||
label: *const c_char,
|
||||
message: *const c_char,
|
||||
) -> c_int {
|
||||
caught(|| status::OK)
|
||||
caught(|| {
|
||||
let label = unsafe { decode(label) }.to_string();
|
||||
let message = unsafe { decode(message) }.to_string();
|
||||
CURRENT.with(|c| {
|
||||
if let Some(r) = c.borrow().as_ref() {
|
||||
r.install_ui(&label, &message);
|
||||
}
|
||||
});
|
||||
status::OK
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C" fn progress_update_v2(_handle: *mut c_void, progress: c_double) -> c_int {
|
||||
|
||||
@@ -18,14 +18,54 @@
|
||||
//! ([`crate::suites::RenderCtx`],frame range 来自 clip 桥)。
|
||||
//! 参照 HS: ofxhImageEffect.cpp gTimelineSuite。
|
||||
//!
|
||||
//! 无渲染上下文(渲染外调用)→ 时间 0 / 时间域 (0,0) 的 headless
|
||||
//! 默认;gotoTime 第 1 期无时间线驱动 → OK no-op(渲染时间由驱动
|
||||
//! 固定,见 [`crate::suites::set_render_ctx`])。
|
||||
//! 无渲染上下文(渲染外调用)→ 回退 app 注入的活动 viewer 时间源
|
||||
//! ([`set_active_viewer_provider`],阶段 6a 注入点);也未注入则
|
||||
//! 时间 0 / 时间域 (0,0) 的 headless 默认。gotoTime 第 1 期无时间线
|
||||
//! 驱动 → OK no-op(渲染时间由驱动固定,见
|
||||
//! [`crate::suites::set_render_ctx`])。
|
||||
|
||||
use std::ffi::{c_double, c_int, c_void};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
use crate::suites::{render_ctx, status};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// app 注入点:活动 viewer 时间源
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 活动 viewer 的时间信息(timeline suite 渲染外回退源的最小集)。
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ViewerTimeInfo {
|
||||
/// viewer 当前时间(秒)。
|
||||
pub time: f64,
|
||||
/// 时间域下界(秒)。
|
||||
pub range_min: f64,
|
||||
/// 时间域上界(秒)。
|
||||
pub range_max: f64,
|
||||
}
|
||||
|
||||
/// 活动 viewer 提供器:返回 None 表示当前无活动 viewer。
|
||||
pub type ActiveViewerProvider = Arc<dyn Fn() -> Option<ViewerTimeInfo> + Send + Sync>;
|
||||
|
||||
static ACTIVE_VIEWER: OnceLock<Mutex<Option<ActiveViewerProvider>>> = OnceLock::new();
|
||||
|
||||
fn viewer_slot() -> &'static Mutex<Option<ActiveViewerProvider>> {
|
||||
ACTIVE_VIEWER.get_or_init(|| Mutex::new(None))
|
||||
}
|
||||
|
||||
/// 注册/清除活动 viewer 提供器(app 接线点;覆盖式)。
|
||||
pub fn set_active_viewer_provider(provider: Option<ActiveViewerProvider>) {
|
||||
*viewer_slot().lock().unwrap_or_else(|e| e.into_inner()) = provider;
|
||||
}
|
||||
|
||||
fn active_viewer() -> Option<ViewerTimeInfo> {
|
||||
let provider = viewer_slot()
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clone()?;
|
||||
provider()
|
||||
}
|
||||
|
||||
/// 函数表布局(OfxTimeLineSuiteV1)。
|
||||
#[repr(C)]
|
||||
pub struct TimeLineSuiteV1 {
|
||||
@@ -54,8 +94,11 @@ unsafe extern "C" fn timeline_get_time(handle: *mut c_void, time: *mut c_double)
|
||||
if time.is_null() {
|
||||
return status::ERR_VALUE;
|
||||
}
|
||||
// 渲染上下文缺省 → 0(headless 默认)。
|
||||
*time = render_ctx().map_or(0.0, |c| c.time);
|
||||
// 渲染上下文 → 活动 viewer → 0(headless 默认)。
|
||||
*time = render_ctx()
|
||||
.map(|c| c.time)
|
||||
.or_else(|| active_viewer().map(|v| v.time))
|
||||
.unwrap_or(0.0);
|
||||
status::OK
|
||||
})
|
||||
}
|
||||
@@ -77,7 +120,11 @@ unsafe extern "C" fn timeline_get_time_bounds(
|
||||
if min.is_null() || max.is_null() {
|
||||
return status::ERR_VALUE;
|
||||
}
|
||||
let r = render_ctx().map_or((0.0, 0.0), |c| (c.range.min, c.range.max));
|
||||
// 渲染上下文 → 活动 viewer → (0,0)(headless 默认)。
|
||||
let r = render_ctx()
|
||||
.map(|c| (c.range.min, c.range.max))
|
||||
.or_else(|| active_viewer().map(|v| (v.range_min, v.range_max)))
|
||||
.unwrap_or((0.0, 0.0));
|
||||
*min = r.0;
|
||||
*max = r.1;
|
||||
status::OK
|
||||
|
||||
Reference in New Issue
Block a user