refactor: drop internal bridge/ffi layers; exporter family lands
Single-lib cleanup: the per-crate src/bridge/ and src/ffi.rs layers are gone (oakundo/oakcommon/oaknode/oaktimeline/oakcodec/oakaudio/ oakrender/oaktask/oakplugin/oakstorage); cross-crate calls are plain Rust, CHandle marshalling shrinks to the oakengine boundary, and tests call the Rust APIs directly (pure C-ABI wrapper tests removed where the domain layer already covers the behavior). exporter.h family implemented: oakengine_export_render (CLI contract), oakengine_export_render_with_params (was a stub), last_error and progress callback; synchronous path reuses task_create_export + start_sync. Fixes on the way: oaktask video ticket self-deadlock, audio params dropped on the export path, codec encoder AAC slicing and H.264 time base. Real-mp4 tests cover both entry points, progress and the illegal-argument matrix. Also: oakstorage session maps null project handles to None (version- info path), configstore test double literal 3.14 -> 3.15 (clippy PI lint), oakaudio output callback scratch buffer + env-aware P1 test, cli media round-trip test uses a generated 16-frame clip (no more minute-long debug runs).
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
//! 已把输出纹理附着为渲染器输出目标(等价 C++ 的
|
||||
//! `PluginRenderer::attach_output_texture`)。clipFreeTexture 对
|
||||
//! Output 只释放句柄、不删纹理(宿主还要读它)。
|
||||
//! - 输入纹理经 [`crate::bridge::render`] 在渲染器上创建(CPU 帧 →
|
||||
//! - 输入纹理经 [`crate::render`] 在渲染器上创建(CPU 帧 →
|
||||
//! GL 上传)。纹理格式:全链路 F32 约束下,像素深度按 clip 协商
|
||||
//! 结果(恒 F32);`format` 参数(kOfxImageEffectGLFormat*)若
|
||||
//! 请求的分量与协商分量不符,Phase 2 不做转换 → Failed(规范要求
|
||||
@@ -164,19 +164,21 @@ mod tests {
|
||||
// ---- 存活纹理表 -----------------------------------------------------------
|
||||
|
||||
/// 存活 GL 纹理表:clipLoadTexture 产出(props 地址 → 属性集 +
|
||||
/// 纹理强引用 + 是否输出 clip);clipFreeTexture 摘除即释放——对应
|
||||
/// 纹理值 + 是否输出 clip);clipFreeTexture 摘除即释放——对应
|
||||
/// HS 的 get/release 配对(HS: ofxhImageEffect.cpp:2336-2351)。
|
||||
/// 纹理是 oakrender 值(drop 自动释放后端 token;原
|
||||
/// `texture_free` 调用面随值模型删除)。
|
||||
///
|
||||
/// 属性集必须**装箱**(Box 稳定堆地址):纹理句柄指向它,函数返回后
|
||||
/// 必须仍存活;栈上临时变量会悬垂(phase-2 实现初版的 bug)。
|
||||
static LIVE_TEXTURES: std::sync::LazyLock<
|
||||
Mutex<HashMap<usize, (Box<PropertySet>, crate::bridge::render::TextureHandle, bool)>>,
|
||||
Mutex<HashMap<usize, (Box<PropertySet>, crate::render::Texture, bool)>>,
|
||||
> = std::sync::LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
/// 登记纹理(clipLoadTexture 内部;`props` 装箱后取地址为句柄基址)。
|
||||
fn register(
|
||||
props: Box<PropertySet>,
|
||||
texture: crate::bridge::render::TextureHandle,
|
||||
texture: crate::render::Texture,
|
||||
is_output: bool,
|
||||
) -> usize {
|
||||
let addr = &*props as *const PropertySet as usize;
|
||||
@@ -191,16 +193,8 @@ fn register(
|
||||
/// 插件在 action 返回前 clipFreeTexture 全部句柄;遗漏的输入纹理在
|
||||
/// 此释放,输出纹理保留——宿主还要读它)。
|
||||
pub(crate) fn purge_leftovers() {
|
||||
let leftovers: Vec<(crate::bridge::render::TextureHandle, bool)> = {
|
||||
let mut live = LIVE_TEXTURES.lock().unwrap_or_else(|e| e.into_inner());
|
||||
live.drain().map(|(_k, (_p, t, o))| (t, o)).collect()
|
||||
};
|
||||
for (texture, is_output) in leftovers {
|
||||
if !is_output && !texture.is_null() {
|
||||
let mut t = texture;
|
||||
unsafe { crate::bridge::render::texture_free(&mut t) };
|
||||
}
|
||||
}
|
||||
let mut live = LIVE_TEXTURES.lock().unwrap_or_else(|e| e.into_inner());
|
||||
live.retain(|_, (_, _, is_output)| *is_output);
|
||||
}
|
||||
|
||||
/// 公共入口模板:panic 兜底。
|
||||
@@ -246,10 +240,12 @@ fn clip_string(clip: &ClipInstance, name: &str, default: &str) -> String {
|
||||
}
|
||||
|
||||
/// 构造纹理属性集(ofxGPURender.h 规定的属性;输入与输出共用,
|
||||
/// 只是数据来源不同)。
|
||||
/// 只是数据来源不同)。`OpenGLTextureIndex` 经
|
||||
/// [`crate::render::texture_id`](桩恒 0——wgpu 后端无 GL 命名空间;
|
||||
/// CPU 回退下插件按规范回退,纹理内容仍可经 clipGetImage 取用)。
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn make_texture_props(
|
||||
texture: crate::bridge::render::TextureHandle,
|
||||
texture: &crate::render::Texture,
|
||||
width: f64,
|
||||
height: f64,
|
||||
components: crate::image::Components,
|
||||
@@ -262,7 +258,7 @@ fn make_texture_props(
|
||||
let props = PropertySet::new();
|
||||
props.set_one(
|
||||
GL_TEXTURE_INDEX,
|
||||
Value::Int(unsafe { crate::bridge::render::texture_id(texture) }),
|
||||
Value::Int(crate::render::texture_id(texture)),
|
||||
);
|
||||
props.set_one(GL_TEXTURE_TARGET, Value::Int(GL_TEXTURE_2D));
|
||||
props.set_one(
|
||||
@@ -342,16 +338,13 @@ unsafe extern "C" fn clip_load_texture(
|
||||
if c.name == "Output" {
|
||||
// Output:返回已附着的输出纹理(format 忽略;渲染目标
|
||||
// 绑定由调用方契约保证——等价 C++ attach_output_texture)。
|
||||
let tex = gl.output_texture;
|
||||
if tex.is_null() {
|
||||
return Err(status::ERR_BAD_HANDLE);
|
||||
}
|
||||
let tex = gl.output_texture.clone();
|
||||
let (w, h) = texture_size(&tex);
|
||||
if w <= 0.0 || h <= 0.0 {
|
||||
return Err(status::ERR_BAD_HANDLE);
|
||||
}
|
||||
let props = make_texture_props(
|
||||
tex,
|
||||
&tex,
|
||||
w,
|
||||
h,
|
||||
crate::image::Components::Rgba,
|
||||
@@ -393,25 +386,16 @@ unsafe extern "C" fn clip_load_texture(
|
||||
if w <= 0.0 || h <= 0.0 {
|
||||
return Err(status::FAILED);
|
||||
}
|
||||
let params = crate::bridge::render::VideoParams {
|
||||
let params = crate::render::VideoParams {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
format: crate::bridge::render::PIXEL_FORMAT_F32,
|
||||
format: crate::render::PIXEL_FORMAT_F32,
|
||||
..Default::default()
|
||||
};
|
||||
let tex = unsafe {
|
||||
crate::bridge::render::texture_create(
|
||||
gl.renderer,
|
||||
¶ms,
|
||||
image.pixels().as_ptr() as *const c_void,
|
||||
image.row_bytes() as i32,
|
||||
)
|
||||
};
|
||||
if tex.is_null() {
|
||||
return Err(status::ERR_MEMORY);
|
||||
}
|
||||
let tex = crate::render::texture_create(¶ms, image.pixels(), image.row_bytes() as i32)
|
||||
.map_err(|_| status::ERR_MEMORY)?;
|
||||
let props = make_texture_props(
|
||||
tex,
|
||||
&tex,
|
||||
w,
|
||||
h,
|
||||
components,
|
||||
@@ -427,10 +411,10 @@ unsafe extern "C" fn clip_load_texture(
|
||||
})
|
||||
}
|
||||
|
||||
/// 纹理尺寸(经 texture_get_params;失败回退 0,0)。
|
||||
fn texture_size(tex: &crate::bridge::render::TextureHandle) -> (f64, f64) {
|
||||
let mut p = crate::bridge::render::VideoParams::default();
|
||||
if unsafe { crate::bridge::render::texture_get_params(*tex, &mut p) } == 0 && p.width > 0 {
|
||||
/// 纹理尺寸(经 [`crate::render::texture_get_params`];失败回退 0,0)。
|
||||
fn texture_size(tex: &crate::render::Texture) -> (f64, f64) {
|
||||
let p = crate::render::texture_get_params(tex);
|
||||
if p.width > 0 {
|
||||
(p.width as f64, p.height as f64)
|
||||
} else {
|
||||
(0.0, 0.0)
|
||||
@@ -456,8 +440,9 @@ fn clip_par(c: &ClipInstance) -> f64 {
|
||||
.unwrap_or(1.0)
|
||||
}
|
||||
|
||||
/// clipFreeTexture:释放纹理句柄(输入 clip 删除 GL 纹理;Output 只
|
||||
/// 释放句柄不删纹理——宿主还要读它)。
|
||||
/// clipFreeTexture:释放纹理(输入 clip 删除纹理值;Output 只释放
|
||||
/// 句柄不删纹理——宿主还要读它)。纹理是值:摘除表条目即 drop
|
||||
/// (原 `texture_free` 调用面随值模型删除)。
|
||||
unsafe extern "C" fn clip_free_texture(texture_handle: *mut c_void) -> c_int {
|
||||
caught(|| {
|
||||
if texture_handle.is_null() {
|
||||
@@ -469,13 +454,7 @@ unsafe extern "C" fn clip_free_texture(texture_handle: *mut c_void) -> c_int {
|
||||
live.remove(&addr)
|
||||
};
|
||||
match entry {
|
||||
Some((_props, texture, is_output)) => {
|
||||
if !is_output && !texture.is_null() {
|
||||
let mut t = texture;
|
||||
unsafe { crate::bridge::render::texture_free(&mut t) };
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Some((_props, _texture, _is_output)) => Ok(()),
|
||||
None => Err(status::ERR_BAD_HANDLE),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
//! 语义,第 1 期账本不需要 → OK no-op,见 memory.rs 文档)。
|
||||
//!
|
||||
//! `// TODO(clip)`:clipGetImage 依赖 [`crate::clip::ClipInstance::fetch_image`]
|
||||
//! (bridge::render 帧访问 C ABI 未冻结),代码已齐、运行时待 clip.rs。
|
||||
//! (oakrender 帧访问随单库化改为本地桩,见 [`crate::render`]),代码已齐、运行时待 clip.rs。
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{c_char, c_double, c_int, c_void, CStr};
|
||||
@@ -258,7 +258,8 @@ unsafe extern "C" fn clip_get_property_set(clip: *mut c_void, out: *mut *mut c_v
|
||||
/// clipGetImage:抓取输入图像并登记到存活表,返回图像属性集 handle
|
||||
/// (HS:2003-2049;`getImage` 失败 → Failed)。
|
||||
///
|
||||
/// `// TODO(clip)`:fetch_image 待 bridge::render 帧访问 C ABI。
|
||||
/// `// TODO(clip)`:fetch_image 待 clip 迁移到
|
||||
/// `oakrender::texture::Texture` 值模型(当前帧访问为本地桩)。
|
||||
unsafe extern "C" fn clip_get_image(
|
||||
clip: *mut c_void,
|
||||
time: c_double,
|
||||
|
||||
@@ -23,10 +23,8 @@
|
||||
//! 到本模块的 `oak_ofx_message_impl`。
|
||||
//!
|
||||
//! 消息出口 = facade 注册的 `oakplugin_message_fn`
|
||||
//! (include/plugin/host.h)。注意:骨架 ffi.rs 声明的 `MessageFn`
|
||||
//! 与头文件不符(头文件是 `(type, message, userdata)`,骨架是
|
||||
//! `(userdata, level, message)`)——做 ffi.rs 时以头文件为准修正。
|
||||
//! 本模块按头文件契约建模。
|
||||
//! (include/plugin/host.h)。本模块按头文件契约建模。
|
||||
//! (原 C ABI 出口层已随单库化删除;注册点由 facade 直接调用。)
|
||||
|
||||
use std::ffi::{c_char, c_int, c_void, CStr};
|
||||
|
||||
@@ -37,14 +35,12 @@ use crate::suites::status;
|
||||
pub(crate) type MessageHandler =
|
||||
unsafe extern "C" fn(*const c_char, *const c_char, *mut c_void) -> c_int;
|
||||
|
||||
/// 消息出口注册表(ffi 层经 `oakplugin_host_set_message_handler`
|
||||
/// 写入,suite 读;userdata 以 usize 存,避免裸指针破坏 static 的
|
||||
/// Send/Sync 推导)。
|
||||
/// 消息出口注册表(facade 写入,suite 读;userdata 以 usize 存,
|
||||
/// 避免裸指针破坏 static 的 Send/Sync 推导)。
|
||||
static HANDLER: std::sync::Mutex<(Option<MessageHandler>, usize)> =
|
||||
std::sync::Mutex::new((None, 0));
|
||||
|
||||
/// 注册/注销消息出口(ffi 层 `oakplugin_host_set_message_handler`
|
||||
/// 调用;公开:测试直接注入捕获器)。
|
||||
/// 注册/注销消息出口(facade 调用;公开:测试直接注入捕获器)。
|
||||
pub fn set_handler(f: Option<MessageHandler>, userdata: *mut c_void) {
|
||||
let mut h = HANDLER.lock().unwrap_or_else(|e| e.into_inner());
|
||||
*h = (f, userdata as usize);
|
||||
|
||||
@@ -171,13 +171,14 @@ pub(crate) fn current_output() -> Option<std::sync::Arc<crate::image::Image>> {
|
||||
/// 纹理(对应 C++ 里实例渲染期的 GL 状态;ofxGPURender.h
|
||||
/// "OpenGL Current Context" 一节要求宿主在 Render 期间持有 GL
|
||||
/// 上下文——本设计的约定是调用方(oakrender 的 PluginJob 路径)在
|
||||
/// 进入 render_job 前已把渲染器上下文置为 current,本表只传递句柄)。
|
||||
#[derive(Clone, Copy)]
|
||||
/// 进入 render_job 前已把渲染器上下文置为 current,本表只传递
|
||||
/// 渲染器引用与输出纹理值)。
|
||||
#[derive(Clone)]
|
||||
pub struct GlCtx {
|
||||
/// 当前渲染器(oakrender 句柄)。
|
||||
pub renderer: crate::bridge::render::RendererHandle,
|
||||
/// 当前渲染器(oakrender 后端上下文)。
|
||||
pub renderer: crate::render::Renderer,
|
||||
/// 已附着的输出纹理(渲染目标;GL 模式下插件把结果画进它)。
|
||||
pub output_texture: crate::bridge::render::TextureHandle,
|
||||
pub output_texture: crate::render::Texture,
|
||||
/// 当前 GL 纹理的实际像素深度(kOfxBitDepth* 静态串;Phase 2
|
||||
/// 全链路 F32,由 render_gl 按插件 kOfxOpenGLPropPixelDepth 协商
|
||||
/// 后填入——纹理句柄的 kOfxImageEffectPropPixelDepth 以它为准)。
|
||||
@@ -197,7 +198,7 @@ pub fn set_gl_ctx(ctx: Option<GlCtx>) {
|
||||
/// 读取 GL 渲染上下文(无上下文返回 None——clipLoadTexture 在非
|
||||
/// GL 渲染期调用时按规范返回 kOfxStatErrMissingHostFeature)。
|
||||
pub(crate) fn gl_ctx() -> Option<GlCtx> {
|
||||
GL_CTX.with(|c| *c.borrow())
|
||||
GL_CTX.with(|c| c.borrow().clone())
|
||||
}
|
||||
|
||||
/// 宿主进程身份(fetchSuite 的 version 检查用;= OFX API 1.5)。
|
||||
@@ -307,16 +308,45 @@ mod tests {
|
||||
#[test]
|
||||
fn gl_ctx_tls() {
|
||||
assert!(gl_ctx().is_none());
|
||||
let renderer = crate::handle::CHandle::null();
|
||||
let tex = crate::handle::CHandle::null();
|
||||
// 最小 GpuContextLike 假实现(无 GPU 适配器需求)。
|
||||
struct FakeGpu;
|
||||
impl oakrender::backend::GpuContextLike for FakeGpu {
|
||||
fn kind(&self) -> oakrender::backend::BackendKind {
|
||||
oakrender::backend::BackendKind::Cpu
|
||||
}
|
||||
fn destroy_texture(&self, _token: u64) {}
|
||||
fn upload(
|
||||
&self,
|
||||
_token: u64,
|
||||
_frame: &oakrender::texture::Frame,
|
||||
) -> oakrender::error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn download(&self, _token: u64) -> oakrender::error::Result<oakrender::texture::Frame> {
|
||||
Ok(oakrender::texture::Frame::new())
|
||||
}
|
||||
fn blit(
|
||||
&self,
|
||||
_src: u64,
|
||||
_dst: u64,
|
||||
_processor: Option<&oakrender::color::ColorProcessor>,
|
||||
) -> oakrender::error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
let renderer: crate::render::Renderer = std::sync::Arc::new(FakeGpu);
|
||||
let tex = crate::render::Texture::dummy();
|
||||
set_gl_ctx(Some(GlCtx {
|
||||
renderer,
|
||||
output_texture: tex,
|
||||
output_texture: tex.clone(),
|
||||
gl_pixel_depth: "OfxBitDepthFloat",
|
||||
}));
|
||||
let got = gl_ctx().unwrap();
|
||||
assert!(got.renderer.is_null());
|
||||
assert!(got.output_texture.is_null());
|
||||
assert_eq!(
|
||||
got.renderer.kind(),
|
||||
oakrender::backend::BackendKind::Cpu
|
||||
);
|
||||
assert!(got.output_texture.is_dummy());
|
||||
assert_eq!(got.gl_pixel_depth, "OfxBitDepthFloat");
|
||||
set_gl_ctx(None);
|
||||
assert!(gl_ctx().is_none());
|
||||
|
||||
Reference in New Issue
Block a user