Skip to content

ENG2-P4-01: RFC-0007: Command-buffer FFI + versioned fn-table entry point #779

Description

@aram-devdocs

Parent

  • Program: ENG2 — GoudEngine v2 Rebuild (see the pinned master tracking issue)
  • Phase / Milestone: Phase 4 — FFI v2 Convergence (eng2-p4-ffi-v2)
  • Batch / Group: Batch 4.0 — Decision record, Grp —
  • Runbook spec: docs/src/runbook/phases/phase-4.md (committed with the roadmap)

Summary

Write RFC-0007 deciding the command-buffer opcode format, shared-memory/ownership model, per-frame drain semantics, versioned fn-table entry point shape, and the deprecation path for the existing 682 flat #[no_mangle] exports.

Architecture Context

Layer: FFI (Layer 5) — decision record covering goud_engine/src/ffi/ as a whole (see tools/lint_layers.rs — downward-only deps).
Modules/types touched:

  • docs/rfcs/RFC-0007-command-buffer-ffi.md (new) — decision record, no code change in this issue.
  • Informs (does not modify) goud_engine/src/ffi/ (682 exports), codegen/ffi_manifest.json, codegen/gen_jni.py.

Boundary constraints (only those that apply):

  • FFI exports: #[no_mangle] extern "C", #[repr(C)], null checks, // SAFETY: comments — this RFC must specify how the new opcode/fn-table surface keeps these invariants.
  • Generated *.g.rs/*.g.cs/*.g.ts are never hand-edited; change Rust + FFI + schema, rerun ./codegen.sh.

Pattern to follow: docs/rfcs/RFC-0000-template.md structure; existing RFC-0001..0004 for tone/format. GDExtension's versioned get_proc_address-style entry point is the reference model for the fn-table decision.

Scope

  • RFC-0007 decides: packed opcode/command format (op codes for component write/read-request, transform set, spawn/despawn, draw-submit), shared-memory ownership (who allocates the command buffer, lifetime), drain semantics (one engine-side lock acquisition per frame), and fn-table versioning scheme (goud_get_interface(version) returning a fn-pointer struct).
  • RFC specifies bulk transform-upload ops writing directly into the ENG2-P2-13 SoA transform store, plus a 0-alloc decode path for the command buffer.
  • RFC states the deprecation path for per-object flat exports — no compat shims kept long-term (binding user decision; breaking changes land, Throne is fixed after).
  • Decision list amendments folded back into RFC-0005 (ENG2-P0-09) and RFC-0006 (ENG2-P3-01) where the SoA/columnar transform view is a shared dependency.
  • Tests: N/A (decision record) — acceptance is RFC merged + reviewed, not a spec test.
  • Docs/rules updates: .agents/rules/ffi-patterns.md gets an addendum once the RFC lands, pointing at the command-buffer/fn-table pattern.

Acceptance Criteria

  • RFC-0007 merged to docs/rfcs/, following the RFC-0000 template, with an explicit decision (not just options) on opcode format, ownership, drain semantics, and fn-table versioning.
  • RFC explicitly states the 0-alloc decode path requirement and the P2-13 SoA write-through requirement.
  • RFC explicitly states no compat shim is kept for deprecated per-object exports, and names the Throne follow-up issues (THR-A-01, THR-A-02).
  • cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings clean (no code changes expected, but gate stays green).

Breaking Change & Throne Follow-up

None directly — this issue is a decision record with no code change. It authorizes the breaking changes executed in ENG2-P4-02/03/05; those issues carry the Throne follow-up links (THR-A-01, THR-A-02).

Blocked By

None.

Files Likely Touched

  • New/Modified/Generated: docs/rfcs/RFC-0007-command-buffer-ffi.md (new)

Agent Notes

  • Verified current state: grep -rn "no_mangle" goud_engine/src/ffi/ | wc -l = 682 flat #[no_mangle] exports today, plus ~1347 generated JNI functions (gen_jni.py output), with no versioning or bulk submission surface.
  • Batch APIs already prove the amortization pattern this RFC generalizes: goud_component_add_batch in goud_engine/src/ffi/component/batch.rs:72 takes one lock, then loops the entity slice internally (goud_engine/src/ffi/component/batch.rs:145-172 region) instead of one FFI crossing per entity. The command-buffer design should read this as the existing proof-of-concept for "one lock per frame."
  • Per design-expansion.md (ENG2-P2-13 note): "ENG2-P4-01 (RFC-0007) MUST specify bulk transform-upload ops writing directly into the P2-13 SoA store, and a 0-alloc decode path." Do not treat this as optional — it's the acceptance-relevant coupling to ENG2-P2-13 and ENG2-P4-04.
  • Phase 4 as a whole requires the Phase 2 AND Phase 3 gates to have passed before execution starts (design-technical.md:292), even though this issue's own Blocked-By is empty — the RFC can be drafted in parallel, but implementation issues (P4-02 onward) assume the render-core and data-core rewrites are gated green.
  • roadmap-inputs.md decision 1 is binding: breaking the FFI API is acceptable, no legacy shims are kept long-term. This RFC is where that decision gets made concrete for the FFI surface specifically.

Verification

cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings
cargo test
./codegen.sh && git diff --exit-code

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions