edgefirst-tensor 0.34.0

Zero-copy tensor memory management with DMA, shared memory, and heap backends
Documentation
use edgefirst_tensor::{BufferIdentity, IdentityKind};

#[cfg(target_os = "linux")]
use edgefirst_tensor::{Tensor, TensorMemory, TensorTrait};

#[cfg(any(target_os = "macos", target_os = "ios"))]
use edgefirst_tensor::{Tensor, TensorMemory, TensorTrait};

/// Report a skip as `SKIPPED: {why}`, straight to stderr.
///
/// Not `println!`/`eprintln!`: libtest captures both and replays them only
/// for a *failing* test, and a skip is a pass, so the reason was discarded
/// exactly when it mattered. The `SKIPPED:` prefix is what
/// `scripts/on-target-test.sh` greps a board's log for to count skips.
/// Written out here rather than shared: an integration test is its own
/// compilation unit and cannot reach `edgefirst-tensor`'s `pub(crate)`
/// `test_support::report_skip`.
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn skip(why: &str) {
    use std::io::Write;
    let _ = writeln!(&mut std::io::stderr(), "SKIPPED: {why}");
}

/// The DMA require gate: a skip here fails under `HAL_TEST_REQUIRE_DMA=1`.
#[cfg(target_os = "linux")]
#[path = "support/dma_require.rs"]
mod dma_require;

/// Allocates a small DMA-BUF tensor, or reports why it could not and returns
/// `None` (a failure under `HAL_TEST_REQUIRE_DMA=1`).
#[cfg(target_os = "linux")]
fn dma_tensor_or_skip(what: &str) -> Option<Tensor<u8>> {
    if !dma_require::available_or_skip(edgefirst_tensor::is_dma_available(), what) {
        return None;
    }
    Some(Tensor::<u8>::new(&[4096], Some(TensorMemory::DmaBuf), None).expect("DMA allocation"))
}

#[test]
#[cfg(target_os = "linux")]
fn a_dup_of_the_same_dma_buf_has_the_same_identity() {
    // `from_fd` on a dup'd fd is what a cross-library import does. If dup
    // changed the identity, every such import would miss the GL cache --
    // the measured blocker this derivation exists to fix.
    let Some(t) = dma_tensor_or_skip("a_dup_of_the_same_dma_buf_has_the_same_identity") else {
        return;
    };
    let dup_fd = t.clone_fd().expect("dup");
    let dup = Tensor::<u8>::from_fd(dup_fd, t.shape(), None).expect("import dup");
    assert_eq!(
        t.buffer_identity().id(),
        dup.buffer_identity().id(),
        "dup must preserve identity: the same dma_buf is the same buffer"
    );
}

/// Allocates a small IOSurface-backed tensor, or reports why it could not and
/// returns `None` — a loud skip rather than a silently vacuous pass on a host
/// with no IOSurface support.
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn iosurface_tensor_or_skip() -> Option<Tensor<u8>> {
    match Tensor::<u8>::new(&[4096], Some(TensorMemory::DmaBuf), None) {
        Ok(t) => Some(t),
        Err(e) => {
            skip(&format!("no IOSurface support on this host ({e})"));
            None
        }
    }
}

/// Re-wrap `t`'s underlying IOSurface as a second, independent tensor — the
/// cross-process/cross-library recovery path (`IOSurfaceLookup`'s in-process
/// analog: wrapping the same `IOSurfaceRef` a second time).
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn wrap_same_surface(t: &Tensor<u8>) -> Tensor<u8> {
    let ptr = t.iosurface_ref().expect("iosurface-backed tensor");
    unsafe { Tensor::<u8>::from_iosurface(ptr, t.shape(), None).expect("wrap same surface") }
}

#[test]
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn two_wraps_of_one_iosurface_share_an_identity() {
    // If two wraps of the same surface minted separate ids, the GL backend's
    // EGLImage import cache would miss (and re-import) on every wrap instead
    // of reusing the one already bound for this surface.
    let Some(t) = iosurface_tensor_or_skip() else {
        return;
    };
    let again = wrap_same_surface(&t);
    assert_eq!(
        t.buffer_identity().id(),
        again.buffer_identity().id(),
        "two wraps of the same IOSurface must share an identity"
    );
}

#[test]
fn the_same_system_key_yields_the_same_identity() {
    // The whole modular design rests on this: two independently-linked copies
    // of this crate must agree on a buffer's identity without sharing state.
    let a = BufferIdentity::derived(IdentityKind::DmaBuf, 4242);
    let b = BufferIdentity::derived(IdentityKind::DmaBuf, 4242);
    assert_eq!(a.id(), b.id(), "same key must give the same id");
}

#[test]
fn different_kinds_with_the_same_key_do_not_collide() {
    let a = BufferIdentity::derived(IdentityKind::DmaBuf, 7);
    let b = BufferIdentity::derived(IdentityKind::IoSurface, 7);
    assert_ne!(a.id(), b.id(), "kind must participate in the id");
}

#[test]
fn no_two_kinds_collapse_to_a_shared_constant() {
    // `derived` is `(kind << 56) ^ key`, so any kind used with a fixed key
    // yields a fixed id. This asserts the API cannot be used that way by
    // accident: distinct keys must give distinct ids for every kind.
    for k in [
        IdentityKind::DmaBuf,
        IdentityKind::Shm,
        IdentityKind::IoSurface,
        IdentityKind::AHardwareBufferId,
        IdentityKind::AHardwareBufferPtr,
        IdentityKind::HostPtr,
        IdentityKind::Pbo,
    ] {
        assert_ne!(
            BufferIdentity::derived(k, 1).id(),
            BufferIdentity::derived(k, 2).id(),
            "{k:?}: different keys must give different ids"
        );
    }
}