# rill-graph
Static DAG signal graph — topology and port connections only.
Processing is driven by `Port::propagate` (not an external engine).
## Key components
- **`Graph`** — immutable DAG container, topology is fixed at build time
- **`GraphBuilder`** — the only way to build a graph (`Source` → `Processor` → `Sink`),
fills `downstream_input_ptrs`, `parent`, `upstream_buffer` for zero-copy routing
- **Kahn's algorithm** — topological sort with cycle detection
- **`Port::propagate`** — recursive signal propagation:
1. Copy data to downstream input ports (skipped for zero-copy `upstream_buffer` ports)
2. Run port algorithm (`run_action`)
3. Call `pre_process` (feedback mix)
4. Call the downstream node's `process_block` (`generate`/`process`/`consume`)
5. `snapshot_feedback` on output ports
6. Recurse through output ports' `downstream_input_ptrs`
- **Zero-copy routing** — 1:1 and fan-out connections read directly from upstream
output buffer via `upstream_buffer`. Copy only for fan-in and feedback.
- **Hard-RT safe** — no heap allocations, no locks, no syscalls in the
signal path. All `Port::propagate` data structures are pre-allocated at
graph construction time (`downstream_nodes`, `downstream_input_ptrs`).
Communication with the control thread is exclusively through
lock-free `MpscQueue<ParameterCommand>`.
- **SIMD-friendly** — fixed buffer position in memory for the graph's lifetime
- **Port routing** — connections and feedback buffers live on ports
- **Feedback support** — `port.pre_process` / `port.snapshot_feedback`
- **Port types** — `Signal`, `Control`, `Clock`, `Feedback`, `Param`
## Top-level processing entry point The processing callback drains the command queue, calls
`Source::generate`, then `Port::propagate` to cascade through the DAG.
## Dependencies
- `rill-core` — `Node`, `Source`/`Processor`/`Sink` traits, `ClockTick`
## Links
- Repository: <https://github.com/DigitalRats/rill>
- Documentation: <https://docs.rs/rill-graph>