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:
@@ -17,13 +17,57 @@
|
||||
//! 进度上报(Progress suite 的宿主侧)。
|
||||
//!
|
||||
//! 对应 C++ 的 `PluginProgressReporter`。进度/取消经 facade 注册的
|
||||
//! 回调出 crate(M9 的 facade 回调模式,不设全局状态)。
|
||||
//! 取消是粘滞的:一旦回调答 false,本报告器的 [`is_cancelled`]
|
||||
//! (image effect suite 的 abort 查询)持续为真。
|
||||
//! 回调出 crate(M9 的 facade 回调模式)。取消是粘滞的:一旦回调
|
||||
//! 答 false,本报告器的 [`is_cancelled`](image effect suite 的
|
||||
//! abort 查询)持续为真。
|
||||
//!
|
||||
//! ## app 注入点(阶段 6a)
|
||||
//!
|
||||
//! facade C 回调之外,app 可经 [`set_reporter_factory`] 注册一个
|
||||
//! 工厂:渲染未装 C 回调时,Progress suite 的 progressStart 携
|
||||
//! (label, message) 从工厂现造一个 [`UiProgressReporter`](如进度
|
||||
//! 对话框),progressUpdate 转发给它,返回 false 即取消。对应 C++
|
||||
//! `PluginProgressDialogReporter` 的创建路径。
|
||||
|
||||
use std::ffi::c_int;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
/// 进度 UI 报告器(app 实现:对话框、状态栏等)。`update` 返回
|
||||
/// false 表示用户请求取消(映射 kOfxStatReplyNo)。
|
||||
pub trait UiProgressReporter: Send {
|
||||
/// 上报进度(0.0..=1.0);false = 取消。
|
||||
fn update(&mut self, progress: f64) -> bool;
|
||||
}
|
||||
|
||||
/// 报告器工厂:progressStart 携 (label, message) 调用,现造一个
|
||||
/// [`UiProgressReporter`]。
|
||||
pub type ReporterFactory =
|
||||
Arc<dyn Fn(&str, &str) -> Box<dyn UiProgressReporter> + Send + Sync>;
|
||||
|
||||
static REPORTER_FACTORY: OnceLock<Mutex<Option<ReporterFactory>>> = OnceLock::new();
|
||||
|
||||
fn factory_slot() -> &'static Mutex<Option<ReporterFactory>> {
|
||||
REPORTER_FACTORY.get_or_init(|| Mutex::new(None))
|
||||
}
|
||||
|
||||
/// 注册/清除进度 UI 工厂(app 接线点;覆盖式)。
|
||||
pub fn set_reporter_factory(factory: Option<ReporterFactory>) {
|
||||
*factory_slot().lock().unwrap_or_else(|e| e.into_inner()) = factory;
|
||||
}
|
||||
|
||||
pub(crate) fn reporter_factory() -> Option<ReporterFactory> {
|
||||
factory_slot()
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// 是否已注册 UI 工厂(render 路径据此决定是否装静默报告器)。
|
||||
pub fn has_reporter_factory() -> bool {
|
||||
reporter_factory().is_some()
|
||||
}
|
||||
|
||||
/// 进度回调:签名与 `include/plugin/instance.h` 的
|
||||
/// `oakplugin_progress_fn` 逐字一致——`(progress, userdata)`,
|
||||
@@ -36,16 +80,21 @@ pub type ProgressFn = unsafe extern "C" fn(progress: f64, userdata: *mut std::ff
|
||||
pub struct ProgressReporter {
|
||||
callback: Option<ProgressFn>,
|
||||
userdata: usize,
|
||||
/// UI 报告器(progressStart 经 [`reporter_factory`] 现造;
|
||||
/// C 回调优先,无回调时才用)。
|
||||
ui: Mutex<Option<Box<dyn UiProgressReporter>>>,
|
||||
/// 取消标志(粘滞;update 返回 false 时置位)。
|
||||
cancelled: AtomicBool,
|
||||
}
|
||||
|
||||
impl ProgressReporter {
|
||||
/// 无回调(渲染静默进行)。
|
||||
/// 无回调(渲染静默进行;progressStart 仍可能经工厂装上 UI
|
||||
/// 报告器)。
|
||||
pub fn silent() -> Self {
|
||||
Self {
|
||||
callback: None,
|
||||
userdata: 0,
|
||||
ui: Mutex::new(None),
|
||||
cancelled: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
@@ -59,10 +108,26 @@ impl ProgressReporter {
|
||||
callback: Some(callback),
|
||||
// usize 存(裸指针破坏 Send 推导;值语义不变)。
|
||||
userdata: userdata as usize,
|
||||
ui: Mutex::new(None),
|
||||
cancelled: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// progressStart 钩子:无 C 回调且工厂已注册时,现造 UI 报告器
|
||||
/// (已装过则不重复造——progressStart 可嵌套括号)。
|
||||
pub(crate) fn install_ui(&self, label: &str, message: &str) {
|
||||
if self.callback.is_some() {
|
||||
return;
|
||||
}
|
||||
let mut slot = self.ui.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if slot.is_some() {
|
||||
return;
|
||||
}
|
||||
if let Some(factory) = reporter_factory() {
|
||||
*slot = Some(factory(label, message));
|
||||
}
|
||||
}
|
||||
|
||||
/// 报告进度;返回 false 表示应取消(映射
|
||||
/// kOfxStatReplyNo/action 失败由调用点决定)。
|
||||
pub fn update(&self, progress: f64) -> bool {
|
||||
@@ -75,7 +140,19 @@ impl ProgressReporter {
|
||||
}
|
||||
!abort
|
||||
}
|
||||
None => true,
|
||||
None => {
|
||||
let mut slot = self.ui.lock().unwrap_or_else(|e| e.into_inner());
|
||||
match slot.as_mut() {
|
||||
Some(ui) => {
|
||||
let keep_going = ui.update(progress);
|
||||
if !keep_going {
|
||||
self.cancelled.store(true, Ordering::Relaxed);
|
||||
}
|
||||
keep_going
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user