Commit Graph
108 Commits
Author SHA1 Message Date
Mike-Solar 431b9ed2b1 feat(oakrender): render-process isolation S1 - dispatcher, scheduler, real worker
Per the M15 design (docs/zh/plans/riir/M15-render-process-isolation.md):

- ipc.rs moved into oakrender with protocol v2: hello_caps,
  render_batch, batch_accepted, frame_failed; main-process-assigned
  slots; BGRA8 slot format. POSIX shm verified to 1GiB on macOS.
- ProcessDispatcher: spawns oak-worker processes, handshake, stdio
  NDJSON control, shm segment lifecycle with generation-tagged keys,
  crash detection with bounded restart and frame redispatch, zero-copy
  ShmFrameRef delivery and copy counters.
- PreviewScheduler: interleaved batch claiming (frame % W per worker,
  no work stealing), seek > playback-distance > background priority,
  credit-based flow control, crash recovery.
- oak-worker renders for real: graph snapshot deserialization, montage
  decode+composite straight into the assigned shm slot, F32->BGRA8
  final conversion in-worker, OFX plugin executor installed in-worker,
  crash hooks for isolation testing.

Thread pool coexists for now (S2 removes it). Integration tests cover
two-worker zero-copy rendering, crash isolation with redelivery, and
real H.264 footage decode into slots.
2026-08-18 18:58:38 +08:00
Mike-Solar 61da70ecf8 fix(build): launch crash — @rpath/libz.1.dylib had no LC_RPATH
The static FFmpeg's external codec libs pull in -lz, which on this
toolchain resolves to a copy whose install name is @rpath/libz.1.dylib
(zlib-ng-compat); without an LC_RPATH entry all three binaries died in
dyld at startup. The app/cli/worker build scripts now emit
-Wl,-rpath,/usr/lib.

Also: FFMPEG_DIR moves into the committed .cargo/config.toml as a
workspace-relative [env] entry — ffmpeg-sys-next's build script cannot
read .env files, and without it the crate silently linked the shared
Homebrew FFmpeg while oakffmpeg-link emitted the static transitive
flags (mixed linkage). docs/build.md updated.
2026-08-17 19:42:45 +08:00
Mike-Solar 21cc2fba24 feat(app): track-header toggles, effect library, panel titles (design parity)
- track headers: name + visible/mute/lock toggles, undoable through
  graphops, honored by the montage (muted tracks are skipped) and by
  the edit guards (locked tracks reject trims/moves/deletes)
- project explorer gains a panel title row; the tree/icons toggle is
  now small icon buttons with tooltips
- new effect library dock panel (every addable effect; double-click
  appends to the selected clip's chain)
- screenshots refreshed (zh/en, window/manager/preferences)
2026-08-17 01:37:31 +08:00
Mike-Solar c5f1d0d76c refactor(engine): pure cdylib + undo-stack test race fix (M14 R4)
- oakengine is now cdylib-only (no rlib/staticlib consumers anywhere;
  cargo tree verified) — the plugin/external C ABI layer; README and
  docs updated
- cd.yml drops the dylib embedding/re-sign steps (the app no longer
  links it)
- test race root-caused and fixed for good: the global undo stack lock
  is now a re-entrant mutex (parking_lot) shared by every test that
  drives the stack, including the previously unlocked node/render
  families; the render-manager serial-ordering bug (an earlier repro
  test initialized the global manager before the not-initialized test)
  is fixed with a shared SERIAL guard and a manager shutdown
- 5 consecutive parallel runs clean; serial 209/209
2026-08-17 00:51:36 +08:00
Mike-Solar f59c7c8476 docs(riir): M14 — frontends link module rlibs directly (plan A) 2026-08-16 19:54:48 +08:00
Mike-Solar e346ea5338 feat(app): P5 — async full-res render, full preferences, shortcut map
- viewers show the 480px proxy immediately and a background thread
  fills the sequence-resolution frame (per-monitor in-flight job,
  generation-based staleness, playback skips full-res)
- preferences dialog complete: cache dir (now consumed by
  default_disk_cache_path), proxy policy/divider, snapshot interval
  (write-through era autosave), default transition length, audio
  in/out devices (new facade device-enumeration exports; audio init
  from config — playback was never creating the audio instance),
  language/theme/renderer backend, all persisted via config
- shortcut map (src/shortcuts.rs): space/J/K/L, I/O, S split, A/^A,
  ⌘Z/⌘⇧Z, ⌘N/⌘O/⌘S/⌘E/⌘Q, frame step, Home, track zoom; dispatch
  shares the menu action path and stays silent over modals
- screenshots: preferences dialog zh/en captured and reviewed
2026-08-16 18:05:03 +08:00
Mike-Solar 0f82092e95 refactor(app): retire manual-save semantics (M13 D5)
- AppEngine: save_project -> export_project_path(explicit path);
  project_modified removed (write-through has no dirty flag)
- File menu's save/save-as are the export path (extension-dispatched
  ove/otio/fcpxml); the library row export path is unchanged
- facade project_save/is_modified stay frozen, documented as the
  legacy export surface
- docs: M13 D5 checked off; M12 inventory updated
2026-08-16 14:17:45 +08:00
Mike-Solar 025dc88c25 feat(storage,app): PostgreSQL backend (D3) + project manager window (D4)
D3: oakdb+pg:// fully wired (shared sea-orm entities, BIGSERIAL DDL,
connect-probe instead of pool retry on dead servers); Storage/Backend=pg
+ Storage/PgUrl config; 13 OAK_TEST_PG_URL-gated PG tests (verified
against a Docker postgres:16), always-on clean-error tests otherwise.

D4: DaVinci-style project manager — list with derived stats, create/
rename/duplicate/delete (confirm)/import/export (native dialogs,
ove/otio/fcpxml), shown at startup and from the file menu; facade
oakengine_library_* exports (list/create/delete/rename/duplicate/
import/export + project_load_library that binds write-through);
save/save-as menu becomes 'export project file', open splits into
from-library/from-file; status bar shows library write state; storage
activates on app start and flushes on exit; spawn_modal reentrancy
fixed (window-callback path) with a doc note.

Also: the P1 audio test's environment probe was lost in the ffi purge;
restored on cpal (the output device is cpal now).
2026-08-16 10:39:31 +08:00
Mike-Solar 5fabad8efd feat(engine): write-through persistence (M13 D2)
- facade storage session manager: project handles bind to the default
  SQLite library on project_new/load; every undo push/group_end/jump
  write-throughs via DatabaseBackend::save (diff journal); project_free
  flushes and unbinds
- background snapshot thread (Storage/SnapshotIntervalSec, latest-wins,
  newest 3 kept) with exit flush (oakengine_storage_flush)
- config-gated: storage only activates with an explicit
  Storage/Backend=sqlite, so headless consumers and tests never touch
  the real library; new exports: storage_flush/is_bound/last_error
- it_storage: kill -9 recovery, cross-session undo, snapshot pruning,
  multi-project isolation, graceful degradation
- also fixes a real config test polluting the user config.ini and the
  undo-stack test races
2026-08-16 08:52:23 +08:00
Mike-Solar b35f3b49bc docs: project storage architecture (en+zh), linked from README and docs index 2026-08-16 01:40:53 +08:00
Mike-Solar 530bc762dc docs(riir): M13 final — node-granular journal (diff-produced), snapshots, persistent undo 2026-08-16 01:19:41 +08:00
Mike-Solar 78d8073c89 docs(riir): M13 — database backend, write-through persistence, project manager 2026-08-16 00:40:02 +08:00
Mike-Solar 2248be8567 feat(storage): real oakstorage file backends + full-timeline .ove serializer
oakstorage (new workspace member): URI dispatch, pluggable backends
(ove-xml built in, otio/fcpxml via oakotio, C-vtable foreign
registration), the M10 C API surface, version info codes, last-error
and alive accounting; round-trip tests per backend.

oaknode serializer: persists the full timeline — sequence track lists,
track block lists, block ranges/media_in/speed/flags, clip footage
references, footage filename+streams, folder children — through
<custom> behavior hooks with two-phase reference resolution; loads the
C++ <olive><project><layout> containers (golden: tests/
project_with_footage.ove); round-trip is field-by-field and
byte-idempotent.
2026-08-14 15:14:38 +08:00
Mike-Solar f0517fe6af feat(app): UI walkthrough pass — faithful screenshots, dock labels, timeline clips, status bar, meter
- screenshot example inits i18n and captures both zh-CN and en-US
  (docs/screenshot-window{,-en}.png)
- dock tabs size to content (no more 64px truncation); viewer/panel
  titles follow the design (素材查看器·name etc.)
- mock project carries V1/V2 video + A1/A2 audio clips rendered as
  rounded green bars; timeline clip geometry/rounded corners match the
  design; status bar visible with full content; audio meter strip
  docked at the program viewer's right edge; project explorer rows
  have icons
- tests/waveform_e2e.rs: waveform cache extracts real peaks and hits
  cache on re-query (P4 acceptance)
2026-08-13 23:37:57 +08:00
Mike-Solar 3625e37081 docs(riir): M12 app rewrite plan (gpui shell, P0-P5 phases) 2026-08-12 14:35:14 +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 05e42668cb build(ffmpeg): static GPL FFmpeg 8.0 via project script + FFMPEG_DIR
- tooling/ffmpeg/build-ffmpeg.sh builds release/8.0 static+PIC into
  .cache/ffmpeg: GPL/version3, every free-license external codec lib
  probed via pkg-config (enabled when present), per-OS hardware
  acceleration (VideoToolbox/AudioToolbox, VAAPI/VDPAU/libdrm,
  D3D11VA/DXVA2/MediaFoundation, nvenc when ffnvcodec exists)
- tooling/install-deps.sh installs those libraries on Homebrew / MSYS2
  UCRT64 / Debian-Ubuntu / Fedora / Arch; nothing in the build sudo's
- ffmpeg-next's own build feature is unusable (every crate-version to
  FFmpeg-release pairing is broken upstream: 9.0.0->FF9 AVCodec fields,
  8.1.0->FF8.1 new enum variants, 8.0.0->FF8 FF_PROFILE rename), so
  ffmpeg-next 9 + FFmpeg 8.x headers via FFMPEG_DIR it is
- new links-crate oakffmpeg-link emits the static FFmpeg's transitive
  link flags from its .pc files (cargo only propagates them from links
  crates, and rustc prunes the flags unless the rlib is referenced —
  hence the force_link statics)
- docs/build.md updated for the Rust workspace flow
2026-08-11 20:04:44 +08:00
Mike-Solar 063837446d feat(app): link liboakengine.dylib via frozen C ABI; UI fixes
- App no longer depends on the oakengine rlib: build.rs links the
  built liboakengine.dylib (+rpath, -export_dynamic, IOSurface) and
  src/oakui/ffi.rs declares the pure-C surface; RealEngine calls only
  the frozen oakengine_* C ABI
- host_syms.rs provides the oakcore_*/fb_* host symbols the dylib
  imports via dynamic lookup
- Fix Preferences dialog crash (spawn_modal reentrancy) with a
  regression test
- Timeline toolbar and viewer transport render C++-era icons (16px
  grid, dark/light themes) with localized tooltips
- i18n: complete en-US table, add untranslated-key detection test
- New dialogs module (preferences, export, progress)
2026-08-11 00:25:20 +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
Mike-Solar f8540e3892 refactor(engine): single-lib phase 3 — plugin/timeline/task/codec bridges converted
- oakplugin bridges -> direct (dlsym 28->0; OFX suites untouched)
- oaktimeline bridges -> direct (teststubs slimmed to pure-Rust mocks)
- oaktask bridges -> direct; facade EncodingParamsPOD -> oakcodec alias
- dlsym total 102->24 (all in documented dead/host directions)
- All crates build+test green; dylib exports unchanged
2026-08-10 19:11:25 +08:00
Mike-Solar 96e57b3705 refactor(engine): single-lib phase 2 — node/render/audio bridges to direct calls
- oaknode bridge/{common,codec} -> oakcommon/oakcodec direct calls
  (drops node's test-stubs XML mocks; fixes decoder_probe signature drift)
- oakrender bridge/common -> direct; dead bridge/codec deleted;
  dead copier kept as dlsym (documented)
- oakaudio bridge/{codec,common} -> direct (CHandle alignment)
- oakcodec test-stubs split to support node test linkage
- dlsym: node 52->20, render 22->4
- single-lib.md updated (sec 11.3/11.4)
2026-08-10 18:29:18 +08:00
Mike-Solar e1423553c7 refactor(engine): single-lib phase 1 — direct Rust calls across the facade
- unify CHandle in oakcore-rs (all crates re-export it)
- facade bridge: 618/624 externs converted to direct module calls
- signature-drift fixes surfaced by direct calls (videoparams,
  seconds, sync estimate, POD unification)
- dead node<->render call sites removed; cancelatom -> oakcommon
- node bridge/undo -> oakundo direct calls
- docs/zh/plans/riir/single-lib.md: design + live status
2026-08-10 17:48:13 +08:00
Mike-Solar f0d551d4a8 docs(riir): settle stale tech-debt entries in notes.md 2026-08-10 17:00:37 +08:00
Mike-Solar e563b340ac feat: oakengine facade, oaknode/oakrender impls, worker+CLI, app skeleton
- oaknode Rust crate: full implementation (core engine, sequence/
  track/block/footage, traverser, serializer, 43 node behaviors;
  493 tests green)
- oakrender Rust crate: full implementation incl. wgpu backend
  skeleton, ticket arena, worker pool (136 tests green; fixed
  lost-wakeup and ticket ordering races)
- src/facade/rust (oakfacade): 222 oakengine_* exports over the
  module C ABIs (61 tests green); worker_main + real POSIX shm
  frame-slot transport (SpscRingBuffer/FrameSlotPool, wire-compatible
  with engine/render/ipc)
- cli/rust + worker/rust binaries (29 + 29 tests green)
- oakotio: FCPXML import/export (49 tests green)
- oaktask: OTIO/FCPXML format dispatch (90 tests green)
- app/rust: gpui app skeleton — dock panels (viewers/timeline/
  explorer/inspector/node editor), transport, olive themes,
  i18n (en/zh), 37 tests green
- gpui submodule: menu checkmarks, dock ratios, vertical meter,
  CPU-frame viewer surface, drop-frame timecode
2026-08-10 08:12:08 +08:00
Mike-Solar cac41d92c1 feat(rust): oakcodec/oaktask/oakplugin crates + oakotio + node/render/storage skeletons
- oakcodec: full crate incl. real FFmpeg decode/encode via ffmpeg-next
  (162 tests, 84.4% cov); new oakcodec_encoding_* metadata family
  (include/codec/format.h, C++ + Rust sides)
- oaktask: manager/tasks/project load-save incl. OTIO via oakotio
  (82 tests, 86.4% cov); concurrent render loop with reorder buffer
- oakplugin: M11 phase 1+2 — self-contained OFX host, GL path,
  ofxColour, pluginrenderer absorbed as render_driver (99 tests,
  81.7% cov); instance.h additions documented
- oakotio: native serde-based OTIO read/write (24 tests)
- oaknode gap fill: dragger/keyframe-helper/multicam C ABI families
  (113/113 gtest); fixes a latent NodeInputDragger segfault
- oaknode Rust skeleton: 43 built-in node type declarations
- oakrender/oakstorage: declaration skeletons (implementation pending)
- notes.md: gap analysis + tech-debt ledger
2026-08-09 05:49:15 +08:00
Mike-Solar 4b24aa9d67 feat(rust): first green Rust crates — oakcore-rs, oakundo, oakcommon, oaktimeline, oakaudio
- oakcore-rs: Rational/TimeRange/TimeRangeList/PixelFormat/SampleFormat
  mirroring oakcore C++ semantics (91.6% coverage)
- oakundo: UndoCommand/UndoStack with vtable commands (99.6%)
- oakcommon: config/XML/VideoParams/logging etc. (89.8%, 516 tests);
  XML via quick-xml, logging via the log facade
- oaktimeline: markers/workarea/edit commands (95.4%)
- oakaudio: processor/manager/sync/waveform/levelmeter (88.4%)
- unified -MMCCCC error codes across all C ABI headers (registry in
  include/common/error.h; pass-through rule)
- olive::Variant moved node -> common (cross-module value type)
- oakcommon_videoparams_set_is_3d added to the C ABI
- M11 (oakplugin Rust rewrite) plan + notes.md freeze line for the
  inter-module C ABI wiring
2026-08-09 04:24:28 +08:00
Mike-Solar 5a564f30ca refactor(node,render): route oaknode->oakrender calls through the C ABI
- oakrender cache C API: add create_for_node (four cache kinds),
  get_uuid, request, load/save_state, set_saving_enabled,
  set_passthrough, get_passthroughs, get_valid_cache_filename,
  get_timebase, lock/unlock, invalidate_range (rational variant),
  get_native (C++ only, for the in-module PreviewAutoCacher)
- Node's four caches become owned OakRenderCache value handles; all
  cache access in node.cpp/clip/viewer/traverser/serializers goes
  through the C ABI
- oaknode_node_get_video_frame_cache returns an addref'd
  OakRenderCache; drop OakNodeFrameCache; oaktask precache/export and
  oakrender_video_ticket_params take the value handle
- color: OCIOBaseNode/OCIOLutNode hold OakColorProcessor handles
  (dtors added); oakrender gains color_processor_create_transform/
  create_lut/create_grading_primary/get_native and LUT library
  queries, keeping all OCIO transform construction inside oakrender;
  oaknode gains colormanager_wrap_borrowed/get_native
- RenderManager::instance() check -> oakrender_manager_available();
  cancel_video_tasks and disk cache path go through manager C API
- remaining node->render C++ symbol: only olive::Texture::~Texture
  via Variant's shared_ptr payload (recorded exception, same class as
  the UndoCommand inheritance)

Tests: new cache/color/manager/colormanager cases; all standalone
trees green (common 196, codec 22, audio 40, task 112, render 63,
timeline 125, node 98, plugin 102).
2026-08-08 03:56:17 +08:00
Mike-Solar 81431d180d refactor(node,timeline): route oaknode->oaktimeline calls through the C ABI
- ViewerOutput owns timeline markers/work area via refcounted
  OakTimeline* value handles (oaktimeline_*_create) instead of C++
  objects
- oaktimeline C API: add marker_list_create/marker_add/
  workarea_create/workarea_set_enabled; handle boxes gain an optional
  deleter for owning handles
- oaknode_node_get_markers/get_work_area return addref'd OakTimeline*
  handles via out params (named-struct forward declarations avoid the
  public header cycle); drop OakNodeMarkerList/OakNodeWorkArea
- serializers 210528-230220 load/save markers and work areas through
  the oaktimeline C API; SerializedMarker POD replaces
  std::vector<TimelineMarker*> in the serializer interface
- Sequence::add_default_nodes uses oaktimeline_add_track_command
- clip.cpp display-mode enums come from the new neutral
  include/timeline/displaymode.h; delete src/node/transition/timeline
- oakcommon xml: add oakcommon_xml_reader/writer_wrap_native borrowed
  wrappers (C++ adapter use)
- liboaknode now has zero C++ symbol references into liboaktimeline

Tests: new owning-handle and wrap_native cases; all standalone trees
green (common 196, codec 22, audio 40, task 110, render 49,
timeline 123, node 96, plugin 100).
2026-08-07 22:53:44 +08:00
Mike-Solar fd2111d560 refactor(plugin): de-Qt oakplugin and wrap it in a pure C ABI
- de-Qt src/plugin/src (olivehost/oliveplugininstance/oliveclip/
  paraminstance/image/pluginprogressreporter); QMessageBox/
  QApplication replaced by facade callbacks
- new C ABI in include/plugin/{error,host,instance}.h with
  refcounted OakPluginInstance value handle
- paraminstance bridges via oaknode C ABI (OakNodeNode value handle,
  oaknode_node_identity registry); undo via oakundo C ABI
- oliveclip textures are OakRenderTexture value handles; oakrender
  gains texture/copier/ticket C API additions
- oaknode gains get_input_at_time/set_input_at_time_undoable/
  node_identity/sequence_from_node/sequence_set_default_parameters/
  find_input_footage
- move avframeptr.h to src/common/src (shared by codec and render)
- node transition pluginSupport stubs bridge the real oakplugin
  headers; all oaknode-building standalone trees add src/plugin and
  link oakplugin into their test binaries
- plugin tests self-sufficient (ipc shim, OfxHost force_load,
  PRE_TEST discovery, OCIO env); timeline standalone gains
  render/c_api
- restore ffmpegdecoder.cpp in src/codec/src/ffmpeg/CMakeLists.txt
  (dropped in 3d004c081, caused jump-to-0 in decoder/encoder tests)

All six standalone trees green: codec 22, audio 40, task 110,
render 49, timeline 121, plugin 100.
2026-08-07 22:18:36 +08:00
Mike-Solar 66831a870e refactor(timeline): de-Qt oaktimeline and wrap it in a pure C ABI
- de-Qt all 11 sources: markers/workarea lose QObject/signals (list
  re-sort via direct resort() call, drawing moves to the app layer),
  explicit unique_ptr ownership for marker lists and viewer's
  workarea/markers
- timeline undo command families (split/pointer/ripple/general/track)
  now hold oaknode C handles and do all graph work through the oaknode
  C ABI (no adapter layer); gaps are attached to the project graph
  before insertion, orphan handles tracked and freed
- fix the de-Qt severed Block->Track length-changed chain with a
  direct block_length_changed() call (positions were stale after
  split/trim)
- expand oaknode C API by ~20 functions needed by timeline
  (copy_in_graph, block kind/casts, connect_element, input arrays,
  tracklist family, marker/workarea borrowed outlets)
- pure C ABI in include/timeline (marker/workarea/edit, command
  factories returning OakUndoCommand), oakcommon XML get_native
  accessors
- oaknode<->oaktimeline resolve each other at runtime; standalone
  drivers link both into test binaries
- tests: oaktimeline 117, regressions oakcommon 193 / oaknode 96 /
  oakrender 42 / oakcodec 18 / oakaudio 36 all green
2026-08-07 04:37:24 +08:00
Mike-Solar edbd3913af refactor(render): de-Qt oakrender and wrap it in a pure C ABI
- copy engine/render to src/render/src (sunk param types excluded),
  de-Qt in five parallel groups: core machinery (tickets/worker pool/
  jobs), caches, color/texture, preview/IPC, GPU backends
- replace Qt GL/Vulkan wrappers with native context abstractions
  (CGL/EGL/WGL, raw vulkan.h), QProcess with POSIX WorkerProcess,
  QJsonObject with a minimal NDJSON-compatible workerjson (wire
  protocol unchanged), QDataStream disk state with a byte-compatible
  BinaryStream
- signals become single std::function callbacks or facade-triggered
  calls per the documented signal/slot strategy
- pure C ABI in include/render + src/render/c_api (renderer/cache/
  color/manager families, OAKRENDER_E_* codes), 37 gtest cases
- bridge src/node/transition/render/* stubs to the real oakrender
  headers, closing the node<->render cycle: liboaknode links
  liboakrender, zero dangling symbols
- docs: M7 implementation status + oakrender semantic-change notes
2026-08-06 03:21:26 +08:00
Mike-Solar d77348ad9f refactor(node): de-Qt oaknode and wrap it in a pure C ABI
- copy engine/node (188 files) to src/node/src, de-Qt in waves:
  core infra (Node/Param/Value/Variant/mathtypes), project/serializer,
  block/output, color, effect leaves, generator, gizmo/plugins
- strip QObject/signals/slots: notifications move to the facade's
  oakengine_event channel, ownership becomes explicit (unique_ptr,
  add_keyframe/add_gizmo), sender() replaced by current_gizmo
- QVariant replaced by olive::Variant, Qt math types by POD mathtypes,
  QXmlStreamReader/Writer by oakcommon's expat-based classes
- sink VideoParams/SubtitleParams/LoopMode/ColorTransform to oakcommon
  (M3.5); polygon/text rasterization behind backend hooks
- pure C ABI in include/node + src/node/c_api (oaknode_ prefix,
  OAKNODE_E_* codes, undoable variants take OakUndoCommand out-params)
- fix Project::clear() root_ reset + disconnect assert, Sequence
  TrackList leak
- 96 gtest cases green in standalone build (build-oaknode)
- docs: signal/slot handling strategy + M3 implementation status
2026-08-05 23:55:54 +08:00
Mike-Solar c50017127b refactor(undo): de-Qt oakundo and wrap it in a pure C ABI
- strip QAbstractItemModel/QAction/signals-slots from UndoStack (UI
  concerns belong to the app layer), index_changed becomes a
  std::function callback
- decouple UndoCommand from Project via an optional modified-flag
  callback pair
- add pure C ABI in include/undo + src/undo/c_api (oakundo_ prefix,
  vtable-based command wrapper, OAKUNDO_E_* error codes)
- fix three engine-side defects: jump(0) infinite loop,
  MultiUndoCommand child leak, push_pre_executed not undoable
- add gtest suites (22 cases) and a standalone build driver
2026-08-05 18:02:24 +08:00
Mike-Solar bad52feda3 refactor(common): de-Qt oakcommon and wrap it in a pure C ABI
- de-Qt all classes under src/common (std::string/vector/mutex,
  std::filesystem, expat-based XmlStreamReader/Writer)
- add pure C ABI in include/common + src/common/c_api: opaque handles,
  init returns NULL on failure, free(NULL) is a no-op, out-params with
  negative OAKCOMMON_E_* error codes (include/common/error.h)
- remove single-consumer classes from oakcommon (html, jobtime,
  otioutils, playbackaudioclock, tohex, util, avframeptr,
  crashpadinterface/crashpadutils, autoscroll, digit, range); their
  destinations are recorded in docs/zh/plans/riir/notes.md
- add gtest suites under src/common/tests (127 cases), standalone
  build driver in src/common/standalone
2026-08-05 17:20:19 +08:00
Mike-Solar 774eda75fe docs(riir): design inter-module C ABI interfaces and split out oakstorage
- riir.md: split project file read/write into standalone oakstorage
  module (pluggable backend, DB-replaceable); add Mermaid data flow
  diagram of all modules; adjust batch table and milestones
- riir/04-interfaces.md: new provides/consumes contract matrix for
  all modules
- riir/M10-oakstorage.md: new manual with frozen C API (typed opaque
  handles, manual vtable for storage backends, URI addressing)
- riir/01: codify pure-C OO interface rules (typed handles instead of
  void*, init/free pairs, manual vtables, no C++ objects or member
  calls across shared library boundaries); upper layers issue commands
  only, no module-to-module event subscriptions (async task callbacks
  excepted)
- sync 00/02/03 and M2-M9 manuals: remove subscribe APIs, replace
  void* with typed handles, update tests accordingly
2026-08-05 14:48:08 +08:00
Mike-Solar b55d626c0c docs: dependency analysis and two-person split for issues 9-20 (#34-#45) 2026-08-05 01:16:07 +08:00
Mike-Solar a030f2da20 app: migrate playhead/undo/modified events off EventBridge (issues 5-8)
- issue 5 (#53): ExportDialog playhead via PlaybackController
- issue 6 (#54): timebasedwidget/timelinewidget playhead via PlaybackController
- issue 7 (#55): historywidget via app-internal Core::undo_index_changed
- issue 8 (#33): PROJECT_MODIFIED_CHANGED re-broadcast as Core::project_modified_changed
2026-08-05 00:52:37 +08:00
Mike-Solar b77344b506 app: PlaybackController + playhead subscription migration (issues 0c, 1-4)
New app/playback/playbackcontroller.{h,cpp}: a lazily created
process-wide singleton whose set_playhead() forwards to
oakengine_viewer_set_playhead and re-broadcasts playhead_changed as a
plain Qt signal. Every app-side oakengine_viewer_set_playhead call site
(17 across viewer, timelinewidget, timelineview, import tool,
timebasedwidget, keyframecontrol, export dialog, mainwindow) now goes
through it.

Migrate the first four playhead subscribers off the raw C event
subscription / EngineEventBridge to the controller signal:
- TimeBasedView (issue 1)
- NodeParamViewWidgetBridge (issue 2)
- NodeParamViewKeyframeControl (issue 3)
- NodeParamViewConnectedLabel (issue 4)

Each stores a QMetaObject::Connection filtered on its viewer node and
disconnects on teardown; the viewer_sub_ members are gone. Plan docs
mark issues 0c and 1-4 as done.

Closes #30, closes #31, closes #32, closes #51, closes #52.
2026-08-05 00:01:12 +08:00
Mike-Solar 712badbaa7 build/ci: green builds and tests on all three platforms
Compiler hygiene (all platforms):
- Silence warnings across the tree: missing override, -Wreorder ctor
  init, -Wshadow, -Wsign-compare, missing switch cases, unused
  functions/captures, Qt 6.11 deprecations (QMouseEvent/QDropEvent
  accessors, qAsConst, Q_FOREACH over non-shared containers,
  AA_UseHighDpiPixmaps) and the .bak/ backup tree removal.
- Fix regressions from the cleanup: missing clip decls in
  capi/timeline.cpp, plugin.cpp rename fallout, panel setFocus
  ambiguity, QGraphicsItem::pos vs event->position(), duplicate
  k_push_button case, boolean test variable.

Windows:
- Qt portability: CommandLineParser is_set/add_option, QTimeZone
  systemTimeZone (QTimeZone::LocalTime is 6.11-only), k_progress_* enum.
- Linking: stop adding oakengine to OLIVE_LIBRARIES (import lib plus
  oakengine-obj caused multiple definitions); add OAKENGINE_STATIC so
  internal consumers no longer reference __imp_* stubs.
- oakengine.ver: export olive::Renderer typeinfo so liboakgl.so can be
  dlopened (Linux), DynamicRenderer no longer dlcloses backend libraries
  (crash in RenderManager's dtor calling into unmapped memory).
- OTIO runtime: copy DLLs next to every binary on Windows instead of
  relying on PATH (0xc0000135 in gtest discovery).
- Headless GL: the runner only has GDI OpenGL 1.1, killing every render
  worker. Deploy Mesa llvmpipe as opengl32sw.dll (Qt's software-GL
  channel) with QT_OPENGL=software, and let QT_OPENGL override the
  AA_UseDesktopOpenGL default. ExportTask fails fast after 8 consecutive
  undelivered frames instead of segfaulting or grinding forever;
  FFmpegEncoder::write_frame tolerates null frames.
- Tests: GetTempPathA+PID temp dirs, GetLongPathNameA for 8.3 names,
  forward-slash normalization when comparing project filenames.

Linux:
- Install libshaderc-dev so oakvulkan compiles GLSL (Vulkan tests).
- Accept UNORM floor-or-round (63/64) in the blit ping-pong test.
- Skip MainWindow construction test on the offscreen QPA (cannot paint
  QOpenGLWidget).

Also: oak_cli_transcode gets a 300s ctest timeout, worker logs GL
context version and LoadGraph/render_frame stages, and
docs/plans/eliminate-event-bridge-issues.md (English translation).
2026-08-04 21:34:31 +08:00
Mike-Solar 22438e0596 docs: plan for eliminating EngineEventBridge (architecture + good first issues) 2026-08-02 15:22:24 +08:00
Mike-Solar a59c33715f fix: node graph edge display, teardown crashes, and event/audio lifetime bugs
- capi: oakengine_node_output_connection_at/_at_ex returned the source
  node as the connection destination; the actual destination is
  conn.second.node(). Out-edge enumeration was useless, so the node
  view could only draw in-edges and randomly lost whichever edges
  needed the out-edge path (random per context build order).
  Regression test in oakengine_node_test
- project teardown: Project::clear() pre-notifies node removal while
  nodes are fully constructed (observers used to crash on
  half-destroyed nodes); childEvent suppresses the removal dance while
  clearing; Node::disconnect_all/disconnect_edge get a silent mode for
  teardown so no invalidation/events touch dying members
  (is_being_cleared); ClipBlock marker disconnect guarded against
  dead viewer/markers; ProjectCopier and PreviewAutoCacher drop
  project references on Project::destroyed instead of disconnecting
  dead objects at shutdown
- preview: add oakengine_preview_request_get_audio_sample_count; the
  viewer queried sample count by passing nullptr to get_audio_samples
  which rejects it, so all playback audio was silently dropped
- app: fix unterminated input-id memcpy in ResolveGroupInput
  (nodeparamviewitem, widgetbridge) that corrupted every parameter id
- app: unsubscribe raw C-API event subscriptions in destructors of
  NodeParamViewKeyframeControl, NodeParamViewConnectedLabel and
  ExportDialog; playhead events used to fire into dead widgets
  (crash when dragging the playhead)
- tests: preview request roundtrip (video frame + audio range) and
  free-while-active teardown coverage; env-gated OAK_DEBUG_EDGES /
  OAK_DEBUG_INVALID_INPUT diagnostics
- docs: investigation notes in docs/zh/
2026-08-02 14:29:49 +08:00
Mike-Solar 66d761b4b7 R8: finish app/ pure C ABI migration (P3-P9) and make OTIO required
- app/ no longer includes engine C++ headers nor holds engine C++ types:
  engine access goes through the oakengine C ABI plus C++ wrappers
  (oakutil/oaknode.h, oakutil/oakvideo.h) and app-local mirror types
  (tooltypes, trackreferencehandle, timelinecommonapp, keyframetypes,
  subtitleapp, serializedlayoutinfoapp, nodevaluehandle, sliderdisplaytypeapp)
- engine: new C ABI functions for block/track/clip/transition navigation
  and predicates, links, caches, waveform/playback, disk folder,
  sequence_track_list, node_free, footage_is_valid, block_get_track,
  get_brush; loadotio/saveotio ported to the current engine API
- OTIO is now a required dependency: CI and CD build it on every
  platform, FindOpenTimelineIO fixed for OTIO 0.16/0.19 (the old deps
  include requirement silently disabled OTIO everywhere), runtime
  libraries are bundled into packages and copied next to macOS binaries
  (oak_copy_otio_runtime)
- fix ProjectViewModel drag&drop mime read/write size mismatch (segfault)
- unify color label naming (k_olive -> "Oak") in the app-side mirror
- docs: OTIO required, FFmpeg minimum corrected to 6.0 (en/zh)
- gtest suite: 1925 passed, 0 failed
2026-07-31 22:46:52 +08:00
Mike-Solar c0dcd33de0 R8 phase 1: extract app/engine shared utilities into shared/include/oakutil
Move the pure-header utilities shared by app/ and engine/ out of
engine/common/ into a new shared/include/oakutil/ layer (define, lerp,
decibel, digit, range, crashpadutils, autoscroll, qtutils, filefunctions
declarations, and a trimmed xmlutils exposing a CancelAtom-free void*
overload). engine/common/ keeps forwarding headers so internal include
paths are unchanged; app/ now includes oakutil/* directly.

engine/node/project.h gains an explicit NodeGroup forward declaration
previously obtained transitively through the old xmlutils.h.
2026-07-27 17:14:56 +08:00
Mike-Solar dd5508e571 docs: archive completed C ABI campaign docs under plans/completed/
Move the finished migration campaign docs (handoffs v3-v6, roadmap,
R5 guides, R6 cleanup, R7 pure-ABI) from docs/zh/ into
docs/zh/plans/completed/ with an archive README; fix all
cross-references; refresh plans/README.md index (active plans now
marked unlocked).
2026-07-27 01:52:52 +08:00
Mike-Solar d7c0970d91 docs: mark R7 complete, unlock plans/riir module split 2026-07-27 00:55:12 +08:00
Mike-Solar eb634b53ef R7: pure C ABI display boundary + engine visibility closure
R7-A (Qwen 3.8 Max): oakengine/display.h rewritten to the POD contract
from r7-pure-abi-plan.md - oak_video_params everywhere, opaque
texture/frame handles with retain/free protocol (engine-heap control
blocks), OakSharedBuffer refcounted wrapper for the QVariant playback
path. All TexturePtr/FramePtr gone from app (47 sites).

R7-B (Qwen 3.8 Max): liboakengine.so exports 3486 -> 19 C++ symbols
(version script oakengine.ver: oakengine_* plus the documented
oakgl/oakvulkan dlopen plugin ABI). oakengine-obj OBJECT library feeds
both the shared lib and the test binaries (-rdynamic so dlopen'd
backends resolve engine objects).

Fix (Kimi K3): producer/consumer type mismatch - viewerdisplay
unpacks OakSharedBufferPtr but viewer.cpp pushed raw void* handles,
so no frame ever reached the display widget (all 5 vulkan viewer
tests timed out with 'never received a texture'). Producers now wrap
with oak_make_shared_frame / oak_make_shared_texture(retain).

Verified: build 0 errors, ctest 45/45, nm U _ZN5olive = 0 in
oak-editor/oak-render-worker/oak-cli, 19 exported C++ symbols in
liboakengine.so (all documented plugin ABI).
2026-07-27 00:54:46 +08:00
Mike-Solar 9b25772267 docs: module-split execution manuals for RIIR stage 1 (plans/riir/)
13 files: 00 overview, 01 two-layer adapter spec (init/free/func +
opaque handles + same-name adapter classes, frozen naming/ownership/
error/event rules), 02 dependency matrix + split order M1-M9 (from a
490-file include scan), 03 Google Test spec (per-C-API coverage,
roundtrip tests, leak counters), M1-M9 per-module manuals with frozen
C APIs (common/undo/node/timeline/codec/audio/render/task/plugin) and
the facade assembly-layer verdict.
2026-07-27 00:06:33 +08:00
Mike-Solar c486c853ff docs: C ABI migration campaign plans, handoffs and roadmap
Complete documentation set for the facade migration (B1-R6) and beyond:
facade-migration-roadmap, handoffs v3-v6, R5 guides, R6 cleanup plan,
R7 pure-ABI plan, long-term plans (riir/ai-agent/gtest/ui-redesign),
updated Chinese README draft, UI design mockups, and the Google Test +
struct-typedef rules in CONTRIBUTING.
2026-07-26 22:42:44 +08:00
Mike-Solar fcf717f6a7 docs: correct the icon plan - move the registry to the app, not to core 2026-07-20 19:08:09 +08:00
Mike-Solar 3e37f7d702 docs: lay out the phase-4 coupling-reduction campaign in the roadmap 2026-07-20 19:03:41 +08:00