our engine C++ is a fresh native engine project that starts from the first
professional milestone we agreed on:
- timeline core
- multi-clip support
- multi-layer support
- playback session
- timeline resolver
- preview contracts
This repository is intentionally independent from UI code. A Qt/QML frontend
can now be attached through a dedicated bridge without pulling Qt into the core
engine itself.
The project now includes:
- step 1: timeline model, playback session, resolver, preview contracts
- step 2: editing commands, undo/redo history, project JSON serialization
- step 3: project JSON loading and round-trip verification
- step 4: FFmpeg-based preview frame extraction from real media
- step 5: timeline-driven preview extraction and media asset probing
- step 6: timeline decode session with preroll and cache behavior
- step 7: transport-driven preview loop that advances playback and refreshes buffered frames
- step 8: async decode worker with background scheduling and deterministic waiting
- step 9: async transport preview loop that queues decode work and applies completed frames
- step 10: presentation queue with paced frame delivery above async transport
- step 11: presentation handoff sink for UI-facing frame delivery without coupling Qt into the core
- step 12: CPU multi-layer composition core as the correctness reference before mobile GPU backends
- step 13: composition-aware handoff so the UI receives fully composited frames, not per-clip frames
- step 14: GPU resource abstraction with a backend-neutral upload contract and mock-GPU verification
- step 15: GPU presentation handoff so the UI-facing boundary can receive GPU texture bindings instead of CPU RGBA frames
- step 16: mobile GPU surface boundary so presented GPU textures can be attached to Android/iOS-style surfaces through a neutral contract
- step 17: first real backend adapter with optional Apple Metal texture upload and surface presentation verification
- step 18: first Android backend adapter with host-aware optional Vulkan texture allocation and Android-surface presentation verification
- step 19: shared GPU render graph planning for offscreen and surface targets above Metal/Vulkan backend boundaries
- step 20: shared GPU render executor that runs the render graph and feeds existing upload/presentation paths
- step 21: backend-aware GPU command encoding so shared timeline plans compile into deterministic command buffers before native backend submission
- step 22: device-level GPU command submission boundary so backend devices can accept validated command buffers before native renderer execution lands
- step 23: GPU resource binding boundary so encoded commands resolve into backend-owned textures and surfaces before bound submission
- step 24: backend-native GPU pass execution boundary so bound command buffers execute against real backend-owned render targets before deeper native renderer specialization
- step 25: GPU surface pass presentation boundary so executed bound passes can present their output texture to mobile-style surfaces through the same GPU-owned resource path
- step 26: transport GPU surface handoff integration so supported backends can drive presentation from the bound GPU pass path while older backends keep an explicit fallback
- step 27: native backend bound-surface adoption so
Metaland AndroidVulkancan execute bound passes into backend texture state and use the direct transport surface path when available - step 28: timeline audio rendering, sink presentation, and playback-clock sync evaluation with real FFmpeg-decoded audio blocks
- step 29: first platform audio-device backend with Apple AVFoundation sink control above the step 28 audio transport boundary
- step 30: export pipeline that renders timeline video and audio into a real muxed output file through a dedicated FFmpeg export boundary
- step 31: mobile optimization planning that computes proxy scale, decode cache size, audio queue lead, and hardware-decode preference from memory pressure
- step 32: dedicated
Qt/QMLbridge withQObjecttransport control,QImageframe delivery, andQQuickImageProviderintegration above the existing composited handoff - step 33: extension foundations for templates, scripts, plugins, and marketplace packages with manifest validation, clip bindings, and JSON round-trip preservation
- step 34: template runtime boundary that resolves active template clips into deterministic parameter and script state at a timeline time
Project / Sequence / Track / Layer / Clipdata model- multiple clips in the same timeline
- multiple layers per track
- separate video and audio tracks
- playback session with play, pause, seek, loop range, and preview requests
- resolver that turns the editable timeline into resolved segments and live snapshots
- smoke example and test target
- editing commands for:
- split
- trim left
- trim right
- move
- transform update
- mute update
- delete
- snapshot-based undo/redo history
- project serialization to JSON
- example for editing flow and project save
- dedicated test coverage for command history and serialization
- full Android runtime audio backend parity
- deeper native mobile GPU execution parity and hardware decode integration
- persistent command journal
- templates runtime beyond manifest and parameter foundations
- script runtime beyond manifest and package foundations
- plugins runtime beyond manifest and permission foundations
- marketplace packaging runtime beyond manifest registration and validation
These are intentionally planned for later phases so the core model stays clean.
We currently validate the engine with:
- compile verification through
cmake --build build - unit and integration tests through
ctest --test-dir build --output-on-failure - runnable smoke examples for:
- timeline and resolver
- editing commands
- JSON round-trip load/save
- FFmpeg preview extraction from real media
- timeline-driven preview extraction from active clips
- preroll/buffering decode session behavior
- transport-driven preview updates across seek, play, tick, and pause
- background async decode requests with deterministic wait-based verification
- async transport updates with enqueue, poll, and wait-for-frame verification
- paced presentation delivery with queued frames and deterministic present intervals
- UI-facing sink delivery for presented frames and clear events
- multi-layer composition correctness with real media on multiple active layers
- composition-aware UI handoff with fully composited frame delivery
- backend-neutral GPU texture upload verification above the composited handoff boundary
- GPU presentation handoff verification with texture delivery and clear events
- GPU surface presentation verification with Android/iOS-ready surface contracts
- optional Apple Metal backend verification through the same transport, upload, and surface pipeline
- host-aware optional Android Vulkan backend verification through the same transport, upload, and surface pipeline
- shared GPU render graph planning verification for offscreen and surface targets with deterministic layer ordering
- shared GPU render execution verification with decoded inputs, executed draw nodes, and uploaded output texture
- backend-aware GPU command-buffer encoding verification with Vulkan and Metal opcode plans above the shared executor
- device-level GPU command submission verification with backend-matched queue labels, submission ids, and mismatch rejection
- GPU resource-binding verification with per-layer sampled textures, render-target allocation, surface binding, and bound-command submission
- backend-native GPU pass-execution verification with bound-resource validation, executed pass counts, and output render-target inspection
- GPU surface pass-presentation verification with executed output texture delivery, composite metadata, and bound surface-state updates
- transport GPU-surface handoff verification with bound-path reuse, stable surface ownership, texture turnover, and cleanup on clear
- native-backend bound-path verification with
Metaland host-available AndroidVulkanshadow execution, direct surface presentation, and zero-upload transport delivery - timeline-audio verification with FFmpeg-decoded tone assets, mixed audio blocks, queued sink delivery, and playback-clock sync evaluation
- platform-audio verification with Apple AVFoundation queueing, device start/stop control, and transport-driven playback on real rendered audio blocks
- export-pipeline verification with real muxed media output, stream probing, preview-frame validation, and decoded audio verification from the exported file
- mobile-optimization verification with deterministic playback-plan derivation under nominal, elevated, and critical memory pressure
- Qt-bridge verification with
QObjecttransport control, compositedQImagedelivery, andQQuickImageProvideraccess to the current engine frame - extension-foundation verification with template/script/plugin/package manifests, template and compound clip bindings, validator checks, and JSON round-trip preservation
- template-runtime verification with sequence-backed clip snapshots, resolved template parameter overrides, script attachments, and runtime round-trip after JSON load
The following runtime families are now reserved in documentation and must land inside the engine after step 32, not inside UI code:
- motion and animation evaluation
- templates and presets
- script packages
- plugin boundaries
- marketplace packaging
The engine-side contracts that are intentionally reserved for those later phases are:
- animatable properties for clip, text, and effect parameters
- parameter schemas for templates and presets
- deterministic frame evaluation for motion
- a declarative script/animation package format
- plugin ABI and package manifest/versioning
Step 33 now fixes the first engine-owned layer for that reservation:
- template, script, plugin, and marketplace manifests now live in
Project - template clips can carry parameter overrides and attached script ids
- compound clips can bind nested sequences explicitly
- validation and JSON round-trip now preserve those contracts inside the core
The current strongest correctness check is round-trip testing:
- create a project in memory
- edit it
- serialize it to JSON
- load it again
- resolve timeline state and compare expected behavior
For media-facing validation we now also:
- generate a temporary video with
ffmpeg - decode a preview frame at a requested timestamp
- convert it to RGBA
- write it to
PPM - verify the output file exists and the frame size is correct
For timeline-media validation we now also:
- probe media metadata from real files
- import probed assets into the engine
- place multiple clips on multiple layers
- ask the engine for the active preview frame at a timeline time
- verify that the top-most active clip was selected correctly
For decode-session validation we now also:
- create a timeline decode session
- request a center time with preroll slots
- verify decoded slot count
- verify cache hits on repeated requests
- verify cache eviction behavior when the center moves
For transport validation we now also:
- seek to a timeline position and prime the current frame
- play from a fixed clock instant
- tick the transport forward with deterministic time deltas
- verify that the current preview frame follows the active clip across layers
- pause and confirm the transport stops advancing while preview remains valid
For async-worker validation we now also:
- enqueue decode work from the main thread
- wait for a specific request id to complete
- verify the worker publishes the expected center frame
- verify cache reuse across consecutive async requests
- verify an out-of-range request completes cleanly without a center clip
For async-transport validation we now also:
- seek and enqueue a background frame request
- poll transport state before completion
- wait for the frame that matches the current transport position
- tick transport forward and confirm later frames are applied only after completion
- verify no-active-clip positions clear the current presentation frame
For presentation validation we now also:
- wait for decoded frames to be staged into a presentation queue
- present the first frame immediately
- confirm later frames stay queued until the pacing interval elapses
- present the queued frame only when the due time is reached
- clear the presented frame when the transport moves to a no-clip region
For handoff validation we now also:
- attach a recording presentation sink
- verify no callback fires before a frame is actually presented
- verify each newly presented frame is delivered exactly once
- verify clip identity and frame dimensions across handoff events
- verify a clear event is delivered when playback enters a no-clip region
For composition validation we now also:
- generate two solid-color media sources
- place them on two active layers at the same timeline time
- compose them into a sequence-sized RGBA frame
- verify base-layer pixels remain visible outside the overlay area
- verify overlay-layer pixels dominate inside the overlay area
For composited-handoff validation we now also:
- attach a sink that receives fully composited frames
- verify no delivery happens before a frame is actually presented
- verify the delivered frame contains multiple composited layers
- verify pixel colors in the delivered frame match the expected composition
- verify a clear event is delivered when playback enters a no-clip region
For GPU-resource validation we now also:
- receive a fully composited frame from the handoff boundary
- upload it into a backend-neutral GPU texture contract
- verify the created texture shape, format, and usage flags
- verify uploaded pixel colors still match the expected composition
- verify repeated uploads reuse the same texture before reset destroys it
For GPU-presentation validation we now also:
- attach a sink that receives GPU presentation packets
- verify no GPU callback fires before a frame is actually presented
- verify the delivered texture binding references a valid uploaded GPU texture
- verify the uploaded texture pixels still match the expected composition
- verify a clear event is delivered when playback enters a no-clip region
For GPU-surface validation we now also:
- create a platform-tagged GPU surface contract for mobile-style output
- present a delivered GPU texture into that surface
- verify the surface metadata and backend expectations match the texture
- verify the presented texture still contains the expected composition
- verify the surface is cleared when playback enters a no-clip region
For GPU-command encoding validation we now also:
- build one shared render graph from active timeline clips
- encode that graph into a backend-aware command buffer for
Vulkan - encode the same timeline into a second command buffer for
Metal - verify opcode ordering and command counts for both targets without changing timeline logic
- verify the shared render executor now carries the encoded command buffer alongside runtime execution
For GPU-command submission validation we now also:
- submit a surface-target command buffer through a backend-matched
GpuDevice - verify the submission report records backend name, queue label, and submission id
- verify submitted clear, bind, draw, and present counts match the encoded command buffer
- verify backend-mismatched command buffers are rejected at submission time
- verify optional
Metaland AndroidVulkandevice submission paths follow the same contract when available
For GPU-resource binding validation we now also:
- bind sampled-input resources into backend textures using real decoded media frames
- allocate a render-target texture for the graph output resource
- bind an optional surface resource when the graph targets a presentation surface
- verify bound submission rejects missing resource bindings
- verify bound resources can be released cleanly after submission
For Apple-Metal validation we now also:
- create a real
MetalGPU device backend - drive it through the existing transport, composition, upload, and surface pipeline
- verify the presented surface expects the
Metalbackend - verify the backend exposes a real native texture handle and uploaded texture contents
- verify the surface clears correctly when playback enters a no-clip region
For Android-Vulkan validation we now also:
- create a real Vulkan-backed Android device adapter from the official Android SDK runtime
- drive it through the existing transport, composition, upload, and surface pipeline
- verify the presented surface expects the
Vulkanbackend on the Android contract - verify the backend exposes a real native image handle and uploaded texture contents
- verify the surface clears correctly when playback enters a no-clip region
For GPU-render-graph validation we now also:
- build a backend-neutral render graph directly from active timeline clips
- verify the graph creates one color target plus sampled inputs per active layer
- verify draw-node ordering matches layer priority and
z-index - verify surface targets append a final
Presentnode while offscreen targets do not - verify the same timeline can target
Mock,Metal, orVulkangraph outputs without changing higher-level composition rules
For GPU-render-executor validation we now also:
- execute the planned GPU render graph into a composited output packet
- decode one input frame per active graph layer at the requested timeline time
- blend the decoded inputs using the shared graph ordering rules
- verify the executed output still matches the expected timeline composition
- upload the executed output through the existing GPU uploader to prove the executor fits the current backend path
This project follows a strict phase-gate rule:
- no later phase should be started before the current phase builds cleanly
- every step must include a real test or runnable verification path
- requests to skip ahead should be treated as a quality risk and explicitly flagged
- architecture features planned for later must be accounted for, but not implemented early if doing so destabilizes the engine
The composition layer now exists as a CPU correctness reference. This is
intentional: the future mobile GPU path for Android/iOS should match this
behavior rather than replace the rules ad hoc.
The UI handoff path now also has a composited variant. This means a future
Qt/QML layer can receive already-composited RGBA frames from the engine even
before the dedicated mobile GPU backend lands.
Step 14 adds a backend-neutral GPU resource contract and a MockGpuDevice
implementation. This is intentional: it lets us lock the upload and texture
rules before we attach Metal or Vulkan paths for iOS/Android.
Step 15 moves the presentation boundary itself onto GPU-aware contracts. This
means a future Qt/QML or mobile runtime can receive stable texture bindings
from the engine rather than raw RGBA CPU frames whenever the GPU path is active.
Step 16 adds the next mobile-first boundary above that: a neutral GPU surface
contract that can stand in for Android external textures or iOS layer-backed
surfaces before the real platform backends land.
Step 17 is the first real backend implementation on top of those contracts. On
Apple hosts, the engine can now create and upload real Metal textures while
still using the same higher-level presentation path.
Step 18 adds the matching Android-side backend adapter using the official Android SDK Vulkan runtime available on the host. The adapter now performs a real availability probe before advertising itself as usable, so unsupported hosts degrade cleanly instead of failing later in the presentation path.
Step 19 adds the shared GPU render-graph layer above those backend boundaries.
This is the piece that lets future Metal and Android Vulkan execution paths
consume the same ordered composition plan instead of duplicating layer logic in
each backend.
Step 20 adds the shared GPU render executor above that plan. This means the engine now has one place that actually runs the planned graph semantics and hands the result to the existing uploader and surface path, instead of calling the older direct compositor path from each GPU handoff separately.
Step 21 adds backend-aware GPU command encoding above that executor. This means
the same timeline can now compile into deterministic Vulkan, Metal, or
OpenGles command buffers before we replace CPU execution with native command
submission in later phases.
Step 22 adds the device-level submission boundary above those command buffers. This means each backend device can now accept, validate, and report submitted command buffers through one shared contract before true native rendering and resource binding are attached in later phases.
Step 23 adds the resource-binding boundary above that submission layer. This means the engine now resolves sampled inputs, render targets, and optional surface targets into backend-owned handles before handing bound work to the GPU device.
Step 24 adds backend-native pass execution above those bound resources. This means the engine can now execute validated render passes against backend-owned targets and inspect the produced output before later phases deepen native renderer specialization.
Step 25 adds surface presentation above that execution boundary. This means the same bound GPU pass pipeline can now present its executed output texture to a mobile-style surface without falling back to the older CPU upload path.
Step 26 connects that path into the transport-facing GPU surface handoff. This means supported backends can now present transport-driven frames through the GPU-owned bound-pass path directly, while unfinished native backends still keep their older explicit uploader fallback until their execution path becomes real.
Step 27 deepens the native backends themselves. Metal and host-available
Android Vulkan now execute bound passes into backend texture state and can
adopt the same direct transport surface path instead of falling back to CPU
upload-style delivery.
Step 29 closes the device-facing audio gap on Apple and leaves Android parity explicitly reserved behind the same sink boundary.
Step 30 turns the current compositor and audio renderer into a true export path. The engine now renders timeline frames and mixed audio blocks into temporary media inputs and muxes them into a real output file through a dedicated FFmpeg export boundary.
Step 31 adds the first mobile-runtime policy layer above the existing decode and audio transport systems. The engine can now derive a deterministic playback plan for proxy size, cache depth, queue lead, and hardware-decode preference from memory pressure instead of scattering those choices through UI code.
Step 32 adds the first true Qt/QML bridge above the engine. The bridge speaks
through QObject, QImage, and QQuickImageProvider, while still consuming
the same transport and composited-handoff path that the engine already tests.
Step 33 adds the first extension-model boundary above the current core. The engine now stores and validates:
- template manifests with parameter schemas and entry sequences
- script manifests with package format and output targets
- plugin manifests with runtime and permission declarations
- marketplace packages that bind those objects into distributable roots
This keeps the future motion/template/script/plugin runtime inside the engine
instead of drifting into Qt/QML.
Step 34 adds the first true runtime boundary on top of that model. The engine can now resolve an active template clip into:
- its entry sequence binding
- its local playback time
- its final parameter values after overrides
- its attached script manifests
This keeps future motion and script execution deterministic and engine-owned, instead of rebuilding that state in the UI layer.
The step 1 model is already shaped to grow into:
CompoundclipsTemplateclips- reusable sequences
- script-driven transitions
- saved presets and packageable templates
- engine-to-Qt/QML bridge extensions
include/our_engine/coreinclude/our_engine/compositioninclude/our_engine/extensionsinclude/our_engine/gpuinclude/our_engine/exportinclude/our_engine/modelinclude/our_engine/mobileinclude/our_engine/playbackinclude/our_engine/presentationinclude/our_engine/previewinclude/our_engine/qtinclude/our_engine/resolveinclude/our_engine/enginesrcexamplestestsdocs
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure./build/our_engine_step1_smoke
./build/our_engine_step2_editing
./build/our_engine_step3_roundtrip
./build/our_engine_step4_ffmpeg_preview /path/to/video.mp4 1.0
./build/our_engine_step5_timeline_preview /path/to/video.mp4 1.0
./build/our_engine_step6_decode_session /path/to/video.mp4 1.0
./build/our_engine_step7_transport_preview /path/to/video.mp4 0.5
./build/our_engine_step8_async_decode_worker /path/to/video.mp4 0.5
./build/our_engine_step9_async_transport_preview /path/to/video.mp4 0.5
./build/our_engine_step10_presentation_queue /path/to/video.mp4 0.5
./build/our_engine_step11_presentation_handoff /path/to/video.mp4 0.5
./build/our_engine_step12_multi_layer_composition /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step13_composited_handoff /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step14_gpu_resources /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step15_gpu_presentation_handoff /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step16_gpu_surface_handoff /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step17_apple_metal_backend /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step18_android_vulkan_backend /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step19_gpu_render_graph /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step20_gpu_render_executor /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step21_gpu_command_encoder /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step22_gpu_command_submission /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step23_gpu_resource_binding /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step24_gpu_pass_execution /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step25_gpu_surface_pass_presentation /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step26_transport_gpu_surface_bound_path /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step27_native_backend_bound_surface /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step28_audio_output_sync /path/to/base.wav /path/to/overlay.wav
./build/our_engine_step29_platform_audio_output /path/to/base.wav /path/to/overlay.wav
./build/our_engine_step30_export_pipeline /path/to/base.mp4 /path/to/overlay.mp4 /path/to/output.mp4
./build/our_engine_step31_mobile_optimization
./build/our_engine_step32_qt_bridge /path/to/base.mp4 /path/to/overlay.mp4
./build/our_engine_step33_extension_foundations
./build/our_engine_step34_template_runtime