Commit Graph
17 Commits
Author SHA1 Message Date
Mike-Solar a43302d9b7 feat(plugin,node): bridge parametric params into the node/inspector path
- ValueType::Parametric; the node input carries the whole curve set as
  NodeValue::Text(JSON) so undo and project serialization come for free
- translation pass builds the input with the default-curve JSON and the
  dimension/range/ui-colour properties
- edits flow both ways: node input (UI) -> curves_from_json ->
  set_ofx(Parametric) on the instance; plugin-side Set/Add/Delete ->
  notify_instance_changed -> JSON written back to the input (undoable)
- screenshot example: gate the macOS-only offscreen capture items so
  the workspace tests build on Linux/Windows
2026-08-21 04:20:14 +08:00
Mike-Solar 980c41acec feat(plugin,codec): Windows plugin loading and ffmpeg discovery
- oakplugin host: Win32 LoadLibraryExW/GetProcAddress/FreeLibrary
  backend (LOAD_WITH_ALTERED_SEARCH_PATH so bundle-sibling DLLs
  resolve), same dl_open/dl_sym/dlclose surface — the POSIX path is
  untouched; OFX hosts now compile on Windows
- proxymanager: PATH split via std::env::split_paths (Windows ';'),
  ffmpeg.exe name, Windows candidate locations; split logic unit
  tested
2026-08-21 04:06:53 +08:00
Mike-Solar c9d557e127 feat(plugin): OfxParametricParameterSuite v1
Parametric (curve/LUT) parameters: ParamValue::Parametric holds one
ordered control-point curve per dimension (identity default over the
declared range), evaluated as piecewise cubic Hermite with auto
(centered-difference) slopes; the full suite — evaluate / count / get /
set / add / delete / delete-all — with the spec's error codes, descriptor
defaults copied to instances, and instanceChanged notifications on
edits. paramDefine accepts OfxParamTypeParametric; the dimension/range
and UI-colour properties round-trip. 148/148 real plugins discovered,
135 registered (one more than before: the parametric-suite consumer).
2026-08-21 03:03:50 +08:00
Mike-Solar 9003b78176 feat: built-in effect params, clip click-select, effect drag-and-drop, project load with plugins
- serializer resolves node types through the factory's dynamic
  (runtime-registered OpenFX) entries, so a project carrying plugin
  nodes loads again (was: "unknown node type"); covered by a new
  CI-gated round-trip test driving the real fixture plugin
- built-in effect nodes expose their inputs as inspector parameters
  like the C++ parameter editor: localized input names from the
  behavior, combo option tables via the new
  NodeBehavior::input_combo_strings (16 nodes, string-for-string from
  the C++ set_combo_box_strings), connection/data inputs excluded
- effect library: live drag-and-drop — onto the inspector's effect
  stack (lands at the indicator position) and onto the node editor
  canvas (creates the node at the drop point); double-click still
  appends to the selected clip
- inspector parameter controls are no longer recreated per render
  (gpui stack view caches them per effect), so sliders drag and
  checkboxes click; the view observes the engine and silently re-syncs
  values (undo/redo land on the widgets)
- timeline: left-press selects clips (plain/keep-multi/Ctrl-Cmd
  toggle); clip moves clamp the shared delta so no clip of a linked
  group lands before frame 0 instead of failing with "invalid move
  target"
- oakplugin: createInstance-rejected instances skip the destroyInstance
  notification (the plugin never owned them); vendor-suite fetchSuite
  misses moved behind OAK_OFX_TRACE; the worker logs the discovered/
  registered plugin counts
- CI: the OFX probe step also runs the serialization round-trip test
- gpui submodule: params view caching, clip click-select, library
  drag payload, graph_position_at
2026-08-20 23:46:40 +08:00
Mike-Solar 0a7f3766e2 ci: probe OFX plugin discovery with a real fixture plugin; cd: restore Windows NSIS job
- new minimal C OFX plugin fixture (ci_test_plugin.c) compiled into a
  real .ofx.bundle by build_fixture.sh; the CI step points
  OFX_PLUGIN_PATH at it and asserts the scan_probe example discovers
  AND registers it (Linux/macOS)
- host bundle binary search now also covers the OFX-standard Win64
  platform directory
- cd.yml: restore the Windows NSIS packaging job (obsolete oakengine
  cdylib prebuild dropped) and repair the job indentation that had
  silently detached the macos/release jobs from the jobs: map;
  releases now gate on all three platforms
2026-08-20 22:37:22 +08:00
Mike-Solar 5498504398 fix(plugin): full OFX plugin discovery — host conformance fixes
Real openfx-misc/CImg/Shadertoy bundles (148 plugins at
/Library/OFX/Plugins) all failed to load before; every failure was
silent. Root causes found one by one with a probe example + lldb:

- property suite rejected propSet on undefined properties and
  propGetDimension on empty ones, and disallowed the index==size
  append — OFX semantics are create-on-set and appendable dimensions
  (this alone failed every plugin's describe)
- host property set missed the mandatory OfxPropType/OfxPropAPIVersion
  and the capability props ofxs' fetchHostDescription reads with
  throwOnFailure=true (IsBackground, TemporalClipAccess, MaxPages,
  PageRowColumnCount, host SupportedContexts, ...) — one missing prop
  aborted the read chain and left a half-initialised host description,
  which made every temporal plugin refuse to load
- MultiThreadSuiteV1 lacked the five mutex functions (the plugin reads
  past the short table — UB); implemented as a real counting-semaphore
  registry
- the OfxHost struct was a stack local; ofxs keeps the POINTER past
  setHost, so describe/render-time fetchSuite calls dereferenced a
  dangling stack address (bus error once plugins actually loaded) —
  the struct is now a leaked process global
- General is a standard OFX context and is no longer filtered out
  (Roto/AppendClip/STMap declare only it)
- every scan/load/describe early-out now logs its reason; suite entry
  points report non-OK statuses with caller location under
  OAK_OFX_TRACE
- examples/scan_probe.rs: scans the real plugin dirs and prints
  discovered/registered counts (also usable from CI)

Result: 148/148 plugins discovered, 134 registered as node types (the
remaining 14 need vendor suites — Vegas stereoscopic etc. — and are
logged, not silent)
2026-08-20 22:28:23 +08:00
Mike-Solar d7cbeba850 feat(app): window menu checkmarks with panel toggle, OFX standard search paths
- The Window menu lists every panel, checks the open ones, and toggles
  visibility on click (closed panels reopen at their last dock target,
  falling back to the default group) — a panel closed by accident
  (e.g. the inspector) is one menu click away again. The menu refreshes
  on dock structure events.
- OFX plugin scanning now covers the full standard location set:
  per-user (~/.OFX/Plugins, ~/.local/share, ~/Library/OFX/Plugins on
  macOS), system-level (/Library/OFX/Plugins, /usr/OFX/Plugins,
  /usr/local, %ProgramFiles%\Common Files\OFX\Plugins), app-relative,
  and the OFX_PLUGIN_PATH environment variable.
2026-08-20 16:20:49 +08:00
Mike-Solar 55bd1132cd feat(app): OFX Interact viewer integration - overlay drawing and event forwarding
- Main-process interact instances for the selected OFX effect card
  (create on selection change, describe, destroy on deselect/close),
  coexisting with the render-worker plugin instances per the OFX
  multi-instance model.
- Program viewer composites the interact's overlay: draw into a GL
  FBO via gl_bridge, read back, straight-alpha 'over' composite onto
  the displayed frame; cached and only re-rendered on frame/time/
  viewport/instance change or plugin redraw requests.
- Event forwarding: picture-area pointer maps through the contain-fit
  letterbox inverse to OFX pen coordinates (pen_motion/down/up);
  Keystroke to OFX key symbols (ASCII, navigation, F1-F35) for
  key_down/up; a 50ms idle pump; global shortcut consumption keeps
  precedence.
- e2e with the real test plugin: lifecycle marker assertions, pen/key
  event records, and macOS GL overlay compositing verified (265 tests
  green incl. gpui_widgets viewer suite).
2026-08-20 00:28:14 +08:00
Mike-Solar b4ceaa9cab feat(oakplugin): GL render bridge, color picker, push-button action, worker progress, Interact host
- gl_bridge: macOS CGL offscreen context (process-wide singleton,
  serialized GlGuard), real GL output textures/FBOs, glReadPixels
  readback with vertical flip and format conversion; use_opengl now
  really engages for OpenGLRenderSupported plugins (verified with real
  GL rendering: C smoke 11/11, unit tests, GL e2e).
- OfxColor: color params get a swatch button plus a real picker popup
  (RGBA sliders, live preview, hex input, undoable commit) replacing
  the four spinboxes.
- Push buttons route kOfxActionInstanceChanged (UserEdited) per the
  OFX contract; test plugin asserts the callback.
- Worker-side plugin progress flows to the main-process progress
  dialog over the NDJSON control channel, with cancel propagation.
- OFX Interact host: NewInteract/Describe lifecycle, Draw/Pen/Key/Idle
  action surface with proper in-args, DrawSuite v1 host implementation
  sharing the gl_bridge context; interact test plugin verifies the
  event stream and real GL drawing.
2026-08-19 22:14:54 +08:00
Mike-Solar 194d761ade refactor(oakundo): replace the CHandle vtable layer with owned trait objects
With the C ABI facade (oakengine) retired, the frozen-ABI rationale is
gone. UndoCommand now boxes a Send Command trait (new/from_closures/
multi), dropping OakUndoCommandVtable, the userdata trampolines, the
refcount shell, the handle module, and all undostack_* handle exports.
The global facade loses its raw-pointer out-params (can_undo/can_redo
return bool, command_name returns String). oaktimeline/oaknode/
oakplugin/oaktask construct commands directly via UndoCommand::new.
oakundo src is now free of unsafe; behavior (ordering, idempotence,
done flags, groups, observers, 200-row cap) is unchanged and pinned by
the rewritten tests.
2026-08-19 00:41:49 +08:00
Mike-Solar ec7b7e6d13 feat(app): OpenFX UI wiring - effect library, inspector params, startup glue
- src/oakui/ofx.rs: startup sequence (host scan, register_plugin_nodes,
  progress reporter factory -> app progress dialog channel, active
  viewer time provider, project extent sync); all failures degrade to
  logs. oak-worker runtime also scans and registers plugins.
- Effect library groups OpenFX entries by sub-category (Filter/
  Generator/Transition/General); effect insertion goes through
  Factory::create_any so dynamic plugin nodes resolve.
- Inspector renders OFX parameters from node inputs (sliders, combo
  boxes from repeated combo_option/combo_value properties, vec/color
  spinboxes, text with explicit commit, push buttons), edits are
  undoable; persistent plugin messages surface as a card badge.
- oakplugin: push_button_clicked and per-instance persistent message
  counting (thin public layers).
2026-08-18 22:16:08 +08:00
Mike-Solar 2db1615453 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.
2026-08-18 17:15:13 +08:00
Mike-Solar b36cbd6b6f refactor: purge CHandle from module internals (M14 R5)
Module-internal object references are Rust types now (values, Arc,
Mutex); CHandle remains only at the oakengine C-ABI boundary:

- oakundo: the global stack holds UndoStack/UndoCommand values
  directly (stack token is the static's address)
- oaktimeline: marker/workarea boxes carry Arc<Mutex<T>>; commands
  share the same allocation through Arc clones (readers in oakengine
  stubs and the app's graphops updated to lock)
- oaktask/oakstorage: sessions, write-through bindings and the
  database backend pass ProjectArc; the Session drops its manual
  release bookkeeping; nodeutil keeps the CHandle<->Arc boundary
  conversion (release_project restored for the app)
- oakcodec: handle.rs deleted outright (no facade entry needed it);
  texture/block placeholders are unit structs
- oakrender: copier's project handle is an identity u64; alive-count
  machinery removed; handle.rs is make_owned/get/get_mut only
- oakplugin: the instance registry is gone (its unregister key never
  matched, leaking weak entries); handle.rs is the RefBox boundary type
- oaknode/oakcommon: only dead guard/borrow helpers removed; external
  payload handles (texture/processor) documented as the boundary

Flake hunts landed along the way: the audio recording test serializes
on the shared manager lock with a normalized state; the autocacher
cancel test uses a slow producer so cancellation is deterministic.
2026-08-17 16:40:15 +08:00
Mike-Solar ab1a2e9c7b 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).
2026-08-16 00:33:45 +08:00
Mike-Solar 4240df1d71 refactor(crates): implement std::error::Error for all module error enums
thiserror derive across oakundo/oakcommon/oaknode/oaktimeline/oaktask/
oakotio/oakcodec/oakaudio/oakrender/oakplugin/oakengine/oakstorage;
Display carries the module prefix and the Failed context, source()
stays default except oakotio's #[from] forwarding. code() mappings and
variants unchanged; each error.rs gains Display/object-safety/code
regression tests.
2026-08-14 05:45:05 +08:00
Mike-Solar 18ff60f147 feat(engine): clip move, clip effect_input, mandatory static FFmpeg
- oakengine_sequence_move_clip implemented for real (oaktimeline
  TrackMoveBlockCommand; fixes the graph-ownership/gap-anchor/ripple
  trim bugs the stub was hiding); same-track via the frozen C ABI,
  cross-track supported by the module command
- oaknode clip blocks now declare a tex_in texture input and set
  effect_input to it, so timeline clips can host effect chains; facade
  test covers effect insert/remove on a real clip
- oakffmpeg-link: FFMPEG_DIR is now mandatory with a clear panic (a
  Homebrew upgrade left the system ffmpeg .pc pointing at a deleted
  dav1d Cellar path, breaking links); reads a git-ignored workspace
  .env for IDEs that cannot inject env vars (RustRover); links the C++
  stdlib for C++ codec libs (svt-av1)
- oakengine re-exports oaknode so tests share one crate instance;
  it_node uses the direct instance's value type where it calls the
  module FFI (the --workspace dev-dependency feature split builds
  oaknode twice)
2026-08-11 23:04:48 +08:00
Mike-Solar 013a175707 refactor: workspace layout — crates/, app at root, legacy C++ removed
Single mechanical restructure commit:
- root Cargo.toml = oakapp bin + workspace; one cargo build produces
  oakapp, oak-cli, oak-worker, liboakengine.dylib
- app/rust/src -> src/ (app at repo root, no rust/ nesting)
- src/<mod>/rust -> crates/oak<mod>; src/oakcore-rs -> crates/oakcore;
  src/bindings/oakotio -> crates/oakotio; src/engine/rust ->
  crates/oakengine (keeps cdylib+staticlib+rlib)
- public C headers include/<mod>/ -> crates/oakengine/include/<mod>/
- OFX SDK headers vendored into crates/oakplugin/ofx/ (HostSupport gone)
- legacy deleted: old src/ C++ modules, engine/, core/, ffmpeg_bridge/,
  app/ (Qt), cli/worker C++, root CMakeLists, third_party/KDDockWidgets
  submodule, otio-install, all build-* output (~40GB)
- oakstorage kept but excluded from the workspace (skeleton w/ todos);
  gpui excluded (own workspace)
- verified: cargo build green, cargo test --workspace 1845/0
  (with the documented OCIO_RS_* env override for the homebrew OCIO)
2026-08-10 20:24:25 +08:00