Skip to main content

mnn/tensor/
tensor_any.rs

1use super::*;
2/// This is a zero sized type
3#[repr(transparent)]
4pub struct AnyTensorRef {
5    __marker: PhantomData<[u8]>,
6}
7
8impl AnyTensorRef {
9    /// Get a raw pointer to the underlying MNN tensor
10    pub(crate) fn as_ptr(&self) -> *mut mnn_sys::Tensor {
11        self as *const Self as *mut mnn_sys::Tensor
12    }
13
14    pub(crate) unsafe fn from_ptr<'s>(tensor: *mut mnn_sys::Tensor) -> &'s Self {
15        unsafe { &*tensor.cast() }
16    }
17
18    pub(crate) unsafe fn from_mut_ptr<'s>(tensor: *mut mnn_sys::Tensor) -> &'s mut Self {
19        unsafe { &mut *tensor.cast() }
20    }
21
22    /// Get the device id of the tensor
23    pub fn device_id(&self) -> u64 {
24        unsafe { mnn_sys::Tensor_deviceId(self.as_ptr()) }
25    }
26
27    /// Get the shape of the tensor
28    pub fn shape(&self) -> TensorShape {
29        unsafe { mnn_sys::Tensor_shape(self.as_ptr()) }.into()
30    }
31
32    /// Get the dimensions of the tensor
33    #[doc(alias = "dims")]
34    pub fn dimensions(&self) -> usize {
35        unsafe { mnn_sys::Tensor_dimensions(self.as_ptr()) as usize }
36    }
37
38    /// Get the width of the tensor
39    pub fn width(&self) -> u32 {
40        unsafe { mnn_sys::Tensor_width(self.as_ptr()) as u32 }
41    }
42
43    /// Get the height of the tensor
44    pub fn height(&self) -> u32 {
45        unsafe { mnn_sys::Tensor_height(self.as_ptr()) as u32 }
46    }
47
48    /// Get the channel size of the tensor
49    pub fn channel(&self) -> u32 {
50        unsafe { mnn_sys::Tensor_channel(self.as_ptr()) as u32 }
51    }
52
53    /// Get the batch size of the tensor
54    pub fn batch(&self) -> u32 {
55        unsafe { mnn_sys::Tensor_batch(self.as_ptr()) as u32 }
56    }
57
58    /// Get the size of the tensor when counted by bytes
59    ///
60    /// Note that for packed layouts (NC4HW4) this includes the padding MNN adds to the
61    /// channel dimension, so it can be larger than the product of the shape.
62    pub fn size(&self) -> usize {
63        unsafe { mnn_sys::Tensor_usize(self.as_ptr()) }
64    }
65
66    /// Get the size of the tensor when counted by elements
67    ///
68    /// Note that for packed layouts (NC4HW4) this includes the padding MNN adds to the
69    /// channel dimension, so it can be larger than the product of the shape.
70    pub fn element_size(&self) -> usize {
71        unsafe { mnn_sys::Tensor_elementSize(self.as_ptr()) as usize }
72    }
73
74    /// Get the halide data type of the tensor
75    pub fn get_type(&self) -> mnn_sys::halide_type_t {
76        unsafe { mnn_sys::Tensor_getType(self.as_ptr()) }
77    }
78
79    /// Check if the tensor is of the specified data type
80    pub fn is_type_of<Ha: HalideType>(&self) -> bool {
81        let htc = mnn_sys::halide_type_of::<Ha>();
82        unsafe { mnn_sys::Tensor_isTypeOf(self.as_ptr(), htc) }
83    }
84
85    /// Check if both tensors have the same halide data type
86    pub fn is_same_type(&self, other: &AnyTensorRef) -> bool {
87        unsafe { mnn_sys::Tensor_isTypeOf(self.as_ptr(), other.get_type()) }
88    }
89
90    /// Get the dimension type of the tensor
91    pub fn get_dimension_type(&self) -> DimensionType {
92        debug_assert!(!self.as_ptr().is_null());
93        From::from(unsafe { mnn_sys::Tensor_getDimensionType(self.as_ptr()) })
94    }
95
96    /// Check if the tensor is dynamic and needs resizing
97    pub fn is_dynamic_unsized(&self) -> bool {
98        self.shape().as_ref().contains(&-1)
99    }
100
101    /// Check that `other` has the same element type and logical shape as `self`.
102    ///
103    /// Byte sizes are deliberately not compared: a packed layout (e.g. NC4HW4) is
104    /// allowed to differ in allocation size from the host layout it converts into.
105    fn ensure_compatible(&self, other: &AnyTensorRef) -> Result<()> {
106        crate::ensure!(
107            self.is_same_type(other),
108            ErrorKind::TensorError;
109            format!(
110                "Tensor element types do not match: {:?} vs {:?}",
111                self.get_type(),
112                other.get_type()
113            )
114        );
115        crate::ensure!(
116            self.shape().as_ref() == other.shape().as_ref(),
117            ErrorKind::SizeMismatch {
118                expected: self.element_size(),
119                got: other.element_size()
120            };
121            format!(
122                "Tensor shapes do not match: {:?} vs {:?}",
123                self.shape(),
124                other.shape()
125            )
126        );
127        Ok(())
128    }
129
130    /// Copies the data from a host tensor to the self.as_ptr()
131    pub fn copy_from_host_tensor(&mut self, tensor: &AnyTensorRef) -> Result<()> {
132        self.ensure_compatible(tensor)?;
133        let ret = unsafe { mnn_sys::Tensor_copyFromHostTensor(self.as_ptr(), tensor.as_ptr()) };
134        crate::ensure!(ret != 0, ErrorKind::TensorCopyFailed(ret));
135        Ok(())
136    }
137
138    /// Copies the data from the self.as_ptr() to a host tensor
139    pub fn copy_to_host_tensor(&self, tensor: &mut AnyTensorRef) -> Result<()> {
140        self.ensure_compatible(tensor)?;
141        let ret = unsafe { mnn_sys::Tensor_copyToHostTensor(self.as_ptr(), tensor.as_ptr()) };
142        crate::ensure!(ret != 0, ErrorKind::TensorCopyFailed(ret));
143        Ok(())
144    }
145
146    /// Create a host tensor from the device tensor with same dimensions and data type and
147    /// optionally copy the data from the device tensor
148    ///
149    /// # Panics
150    /// Panics if the tensor's element type is not `T` or the copy fails.
151    pub fn create_host_tensor_from_device<T: HalideType + Copy>(
152        &self,
153        copy_data: bool,
154    ) -> Tensor<Owned<T>, Host> {
155        assert!(
156            self.is_type_of::<T>(),
157            "Tensor element type does not match {}",
158            core::any::type_name::<T>()
159        );
160        let shape = self.shape();
161        assert!(
162            !self.is_dynamic_unsized(),
163            "Cannot create a host tensor from a device tensor with a dynamic shape {shape:?}; resize the session first"
164        );
165        let dm_type = self.get_dimension_type();
166        let mut out = Tensor::new(shape, dm_type);
167
168        if copy_data {
169            self.copy_to_host_tensor(unsafe { out.as_any_tensor_mut() })
170                .expect("Failed to copy data from device tensor");
171        }
172        out
173    }
174
175    /// Try to wait for the device tensor to finish processing
176    pub fn wait(this: &Self, map_type: MapType, finish: bool) {
177        unsafe {
178            mnn_sys::Tensor_wait(this.as_ptr(), map_type, finish as i32);
179        }
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    #[test]
188    fn copy_rejects_element_type_mismatch() {
189        let src = TensorOwned::<f32, Host>::from_shape_data(
190            [1, 2, 3],
191            DimensionType::Caffe,
192            &[1., 2., 3., 4., 5., 6.],
193        );
194        let mut dst = TensorOwned::<u8, Host>::new([1, 2, 3], DimensionType::Caffe);
195        let err = unsafe {
196            src.as_any_tensor()
197                .copy_to_host_tensor(dst.as_any_tensor_mut())
198        }
199        .expect_err("copying f32 into u8 must fail");
200        assert!(format!("{err}").contains("element types do not match"));
201        let err = unsafe {
202            dst.as_any_tensor_mut()
203                .copy_from_host_tensor(src.as_any_tensor())
204        }
205        .expect_err("copying f32 into u8 must fail");
206        assert!(format!("{err}").contains("element types do not match"));
207    }
208
209    #[test]
210    fn copy_rejects_shape_mismatch() {
211        let src = TensorOwned::<f32, Host>::new([1, 2, 3], DimensionType::Caffe);
212        let mut dst = TensorOwned::<f32, Host>::new([1, 3, 2], DimensionType::Caffe);
213        let err = unsafe {
214            src.as_any_tensor()
215                .copy_to_host_tensor(dst.as_any_tensor_mut())
216        }
217        .expect_err("mismatched shapes must fail");
218        assert!(format!("{err}").contains("shapes do not match"));
219    }
220}