use crate::{DType, Result, TensorDesc, TensorDyn};
impl TensorDyn {
pub fn import_descriptor(desc: &TensorDesc) -> Result<Self> {
let (dtype, shape) = validate_descriptor(desc)?;
let mut t = Self::import_storage(desc, &shape, dtype)?;
restore_descriptor_metadata(&mut t, desc, &shape)?;
Ok(t)
}
}
fn validate_descriptor(desc: &TensorDesc) -> Result<(DType, Vec<usize>)> {
if desc.version != crate::ABI_VERSION {
return Err(crate::Error::NotImplemented(format!(
"tensor interop ABI v{} (this build understands v{})",
desc.version,
crate::ABI_VERSION
)));
}
if desc.ndim as usize > crate::TensorDesc::MAX_NDIM {
return Err(crate::Error::NotImplemented(format!(
"descriptor declares rank {}, but this build's descriptor \
carries only {} shape slots; importing would address a \
prefix of a larger allocation and silently read wrong data",
desc.ndim,
crate::TensorDesc::MAX_NDIM
)));
}
if desc.flags & crate::protocol::flags::SYNC_PRESENT != 0
&& desc.kind != crate::protocol::kind::D3D11_TEXTURE
{
return Err(crate::Error::NotImplemented(
"descriptor advertises SYNC_PRESENT, but waiting on a \
producer fence is not implemented in this build; importing \
would alias memory with device work still in flight"
.to_owned(),
));
}
if desc.flags & crate::protocol::flags::SYNC_PRESENT != 0
&& desc.kind == crate::protocol::kind::D3D11_TEXTURE
&& desc.ptr.is_null()
{
return Err(crate::Error::InvalidArgument(
"D3D11_TEXTURE descriptor advertises SYNC_PRESENT but carries no \
fence handle in `ptr`; the completion it names cannot be waited on"
.to_owned(),
));
}
let dtype = crate::protocol::dtype_to_dtype(desc.dtype).ok_or_else(|| {
crate::Error::NotImplemented(format!("tensor interop dtype code {}", desc.dtype))
})?;
let shape: Vec<usize> = desc.shape().iter().map(|d| *d as usize).collect();
Ok((dtype, shape))
}
fn restore_descriptor_metadata(
t: &mut TensorDyn,
desc: &TensorDesc,
shape: &[usize],
) -> Result<()> {
let format = if desc.format != crate::protocol::format::NONE {
crate::protocol::format_from_code(desc.format)
} else if desc.fourcc != 0 {
crate::PixelFormat::from_fourcc(desc.fourcc)
} else {
None
};
if let Some(fmt) = format {
t.set_format(fmt)?;
restore_imported_row_stride(t, desc, shape, fmt);
}
t.set_colorimetry(if desc.colorimetry == 0 {
None
} else {
Some(crate::Colorimetry::unpack(desc.colorimetry))
});
restore_d3d11_logical_shape(t, desc, shape)?;
Ok(())
}
fn restore_d3d11_logical_shape(
t: &mut TensorDyn,
desc: &TensorDesc,
shape: &[usize],
) -> Result<()> {
if desc.kind != crate::protocol::kind::D3D11_TEXTURE || t.shape() == shape {
return Ok(());
}
t.set_logical_shape(shape)
}
fn restore_imported_row_stride(
t: &mut TensorDyn,
desc: &TensorDesc,
shape: &[usize],
fmt: crate::PixelFormat,
) {
if !matches!(
desc.kind,
crate::protocol::kind::HOST
| crate::protocol::kind::DMABUF
| crate::protocol::kind::IOSURFACE
| crate::protocol::kind::PBO
) {
return;
}
let row_dim = match fmt.layout() {
crate::PixelLayout::Planar if desc.ndim as usize >= 3 => 1,
_ => 0,
};
if (desc.ndim as usize) < 2 {
return;
}
let Some(&stride_bytes) = desc.strides().get(row_dim) else {
return;
};
let Ok(stride_bytes) = usize::try_from(stride_bytes) else {
return;
};
let rows: usize = match fmt.layout() {
crate::PixelLayout::Planar if shape.len() >= 2 => shape[0].saturating_mul(shape[1]),
_ => shape.first().copied().unwrap_or(0),
};
apply_imported_row_stride(t, fmt, stride_bytes, rows);
}
fn apply_imported_row_stride(
t: &mut TensorDyn,
fmt: crate::PixelFormat,
stride_bytes: usize,
rows: usize,
) {
let capacity = t.capacity_bytes();
match stride_bytes.checked_mul(rows) {
Some(needed) if needed <= capacity => {
if let Err(e) = t.set_row_stride(stride_bytes) {
log::warn!(
"import_descriptor: producer row_stride \
{stride_bytes} bytes rejected for {fmt:?} \
({e}); keeping the freshly-imported tight \
pitch instead"
);
}
}
Some(needed) => {
log::warn!(
"import_descriptor: producer row_stride \
{stride_bytes} bytes × {rows} rows needs {needed} \
bytes, exceeding this import's capacity \
{capacity}; keeping the freshly-imported tight \
pitch instead of trusting an oversized descriptor"
);
}
None => {
log::warn!(
"import_descriptor: producer row_stride \
{stride_bytes} bytes × {rows} rows overflows \
usize computing bytes needed; keeping the \
freshly-imported tight pitch instead of trusting \
a malformed descriptor"
);
}
}
}