edgefirst-tensor
Zero-copy tensor memory management for edge AI applications.
This crate provides a unified interface for managing multi-dimensional arrays (tensors) with support for different memory backends optimized for ML inference pipelines.
Memory Types
| Type | Description | Use Case |
|---|---|---|
| DMA | Linux DMA-BUF allocation | Hardware accelerators (GPU, NPU, video codecs) |
| SHM | POSIX shared memory | Inter-process communication, zero-copy IPC |
| Mem | Standard heap allocation | General purpose, maximum compatibility |
| PBO | OpenGL Pixel Buffer Object | GPU-accelerated image processing (created by ImageProcessor) |
Features
- Automatic memory selection - Tries DMA → SHM → Mem based on availability
- Zero-copy sharing - Share tensors between processes via file descriptors
- Memory mapping - Efficient CPU access to tensor data
- ndarray integration - Optional conversion to/from
ndarray::Array(feature:ndarray)
Quick Start
use ;
// Create a tensor with automatic memory selection
let tensor = new?;
println!;
// Create with explicit memory type
let dma_tensor = new?;
// Map tensor for CPU access
let mut map = tensor.map?;
map.as_mut_slice.fill;
// Share via file descriptor (Unix only)
let fd = tensor.clone_fd?;
Platform Support
| Platform | DMA | SHM | Mem | PBO |
|---|---|---|---|---|
| Linux | Yes | Yes | Yes | Yes (with OpenGL) |
| macOS | No | Yes | Yes | No |
| Other Unix | No | Yes | Yes | No |
| Windows | No | No | Yes | No |
Feature Flags
ndarray(default) - Enablendarrayintegration for array conversions
Environment Variables
EDGEFIRST_TENSOR_FORCE_MEM- Set to1ortrueto force heap allocation
PlaneDescriptor
PlaneDescriptor wraps a duplicated file descriptor for use with
ImageProcessor::import_image(). It captures optional stride and offset
metadata alongside the fd so that the importer gets a complete picture of the
plane layout without additional out-of-band parameters.
use PlaneDescriptor;
use BorrowedFd;
// SAFETY: replace 42 with a real, valid fd from a DMA-BUF allocation.
let pd = unsafe
.expect
.with_stride // optional: row stride in bytes
.with_offset; // optional: plane offset in bytes
The fd is duplicated eagerly in new() — a bad fd fails immediately rather
than inside import_image. The caller retains ownership of the original fd.
DMA-BUF fd Accessors
TensorDyn exposes two fd accessors for DMA-backed tensors (Linux only):
dmabuf(&self) -> Result<BorrowedFd<'_>>— Borrow the DMA-BUF fd tied to the tensor's lifetime.dmabuf_clone(&self) -> Result<OwnedFd>— Duplicate the DMA-BUF fd. Fails withError::NotImplementedif the tensor is not DMA-backed.
// Share the buffer with an external consumer (e.g. NPU delegate)
let fd = tensor.dmabuf_clone?;
delegate.register_buffer?;
Pixel Format Metadata
Attach a PixelFormat to any tensor for image processing:
set_format(format: PixelFormat) -> Result<()>— Validates shape compatibility and stores the format.with_format(format: PixelFormat) -> Result<Self>— Builder-style consuming variant.
let mut t = new?;
t.set_format?;
Row Stride
For externally allocated buffers with row padding (e.g. V4L2 camera frames):
row_stride(&self) -> Option<usize>— Stored stride,Noneif tightly packed.effective_row_stride(&self) -> Option<usize>— Stored stride, or computed from format and width if not set.set_row_stride(stride: usize) -> Result<()>— Set stride in bytes. Format must be set first.with_row_stride(stride: usize) -> Result<Self>— Builder-style consuming variant.
Plane Offset
For buffers where image data does not start at byte 0 of the fd:
plane_offset(&self) -> Option<usize>— Offset in bytes,Noneif zero.set_plane_offset(offset: usize)— Set byte offset.with_plane_offset(offset: usize) -> Self— Builder-style consuming variant.
BufferIdentity
BufferIdentity provides a stable cache key for a tensor's underlying buffer.
It is created fresh on every allocation or import and carries:
id() -> u64— Monotonically increasing integer. Changes whenever the buffer changes. Suitable as a HashMap key or EGL image cache key.weak() -> Weak<()>— Goes dead when the owning tensor (and all clones) are dropped, allowing caches to detect stale entries without holding a strong reference.
buffer_identity() is accessible on typed tensors via TensorTrait:
use ;
let t = new?;
let key = t.buffer_identity.id;
let guard = t.buffer_identity.weak;
// Later: guard.upgrade().is_none() means the tensor was dropped.
BufferIdentity is used internally by the image processing backends as an EGL
image cache key to avoid redundant GPU texture imports across frames.
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.