docs/zh/plans/render-pipeline-threads.md M3 (design 3.2): OpenFX crash isolation moves from "every worker hosts plugins" to a single dedicated host process, served over NDJSON + shared memory. - oak-worker --ofx-host mode (src/ofx_host.rs): loads every plugin once, resolves jobs by the cross-process-stable OFX identifier, and renders through the same in-process executor the workers used to install. - oak-render/ofxhost.rs: the single-host client. The render manager creates and installs it for the Pipeline backend (lazy spawn on the first plugin job); eval::process_plugin_job prefers it and falls back to the in-process executor otherwise, so the process backend keeps its current behavior until M4. - Data plane: input/output FrameSlotPool pairs (the handshake's input_* fields are used for the first time). Named clips and the source frame are written to input slots after the explicit CPU readback; the plugin output returns through an output slot. Pool size/capacity grow by a host restart when a job needs more (safe: submissions are serialized and one job is in flight). - Crash loop: reader EOF fails the in-flight submit, which respawns the host and re-posts the same job (frames are read back once); after three consecutive crashes the client is permanently dead and the evaluator falls back to a purple frame. The dead child is reaped immediately, and a submit mutex enforces the one-job-in-flight contract. - Progress/cancel: the host flushes plugin_progress immediately (live progress), and reads stdin on its own thread so plugin_cancel takes effect mid-render at the plugin's next progressUpdate; the sticky flag resets at progressStart and request_plugin_cancel_all broadcasts to both the worker pool and the host. - JobSpec::Plugin / PluginJobPayload carry the plugin type_id (stable across processes); `--ofx-crash-once` / `--ofx-crash-always` are the deterministic crash hooks, matching the worker's env hooks. - Tests: wire round-trips; host unit tests (crash budget, cancel-flag reset through the factory, source mapping); oak-worker integration tests against the real host + bundled test plugin (render + progress, crash respawn and re-post, three-crash give-up, mid-render cancel on the new slow variant, concurrent submits); eval's purple fallback.
126 lines
3.5 KiB
Rust
126 lines
3.5 KiB
Rust
// Oak Video Editor - Non-Linear Video Editor
|
|
// Copyright (C) 2026 Oak Team
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! oak-worker: headless render worker process (Rust).
|
|
//!
|
|
//! The whole runtime lives in this binary (M14 R2): render backend
|
|
//! selection (dynamic -> OpenGL fallback through the oakrender crate's
|
|
//! direct Rust API), the startup handshake and the NDJSON control loop
|
|
//! ([`worker`], the port of `engine/src/capi/worker.cpp`), plus the
|
|
//! shared-memory frame-slot transport ([`ipc`], the port of
|
|
//! `engine/render/ipc/`). No `liboakengine` dylib is linked; the facade
|
|
//! keeps its own copies of both modules for the frozen
|
|
//! `oakengine_worker_*` / `oakengine_ipc_*` C ABI (external consumers).
|
|
//!
|
|
//! See README.md for the full status.
|
|
|
|
#![deny(unsafe_op_in_unsafe_fn)]
|
|
#![warn(missing_docs)]
|
|
|
|
mod framecache;
|
|
mod ipc;
|
|
mod ofx_host;
|
|
mod worker;
|
|
|
|
use std::process::exit;
|
|
|
|
/// Protocol version announced in the startup handshake
|
|
/// (`k_protocol_version` in worker.cpp).
|
|
pub const PROTOCOL_VERSION: i32 = 1;
|
|
|
|
/// Log a worker-side message to stderr, mirroring worker.cpp `log_error()`
|
|
/// (the `worker: ` prefix).
|
|
pub fn log_error(message: &str) {
|
|
eprintln!("worker: {message}");
|
|
}
|
|
|
|
/// Scan argv for `--backend <name>` (worker.cpp `oakengine_worker_main`).
|
|
/// The default is `"opengl"`; the value is lowercased; the last flag wins.
|
|
fn parse_backend(args: &[String]) -> String {
|
|
let mut backend = "opengl".to_string();
|
|
let mut i = 1usize;
|
|
while i < args.len() {
|
|
if args[i] == "--backend" && i + 1 < args.len() {
|
|
backend = args[i + 1].to_ascii_lowercase();
|
|
i += 2;
|
|
} else {
|
|
i += 1;
|
|
}
|
|
}
|
|
backend
|
|
}
|
|
|
|
fn main() {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
// M3: `--ofx-host` runs the single OpenFX host loop instead of the
|
|
// ticket worker (same binary, no extra target to package).
|
|
if args.iter().any(|arg| arg == "--ofx-host") {
|
|
exit(ofx_host::ofx_host_main(&args));
|
|
}
|
|
let backend = parse_backend(&args);
|
|
exit(worker::worker_main(&backend));
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn protocol_version_is_one() {
|
|
assert_eq!(PROTOCOL_VERSION, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn backend_parsing_matches_engine_main() {
|
|
// Default is opengl.
|
|
assert_eq!(parse_backend(&["oak-worker".to_string()]), "opengl");
|
|
// Value lowercased.
|
|
assert_eq!(
|
|
parse_backend(&[
|
|
"oak-worker".to_string(),
|
|
"--backend".to_string(),
|
|
"Vulkan".to_string()
|
|
]),
|
|
"vulkan"
|
|
);
|
|
// "none" skips renderer creation.
|
|
assert_eq!(
|
|
parse_backend(&[
|
|
"oak-worker".to_string(),
|
|
"--backend".to_string(),
|
|
"none".to_string()
|
|
]),
|
|
"none"
|
|
);
|
|
// Last flag wins.
|
|
assert_eq!(
|
|
parse_backend(&[
|
|
"oak-worker".to_string(),
|
|
"--backend".to_string(),
|
|
"vulkan".to_string(),
|
|
"--backend".to_string(),
|
|
"opengl".to_string()
|
|
]),
|
|
"opengl"
|
|
);
|
|
// Missing value leaves the default.
|
|
assert_eq!(
|
|
parse_backend(&["oak-worker".to_string(), "--backend".to_string()]),
|
|
"opengl"
|
|
);
|
|
}
|
|
}
|