1use num_traits::AsPrimitive;
2use std::ffi::c_void;
3use std::fmt::Display;
4use tract_core::internal::*;
5use tract_core::tract_linalg::block_quant::{BlockQuantFact, BlockQuantStorage};
6
7use crate::device::{DeviceBuffer, get_context};
8use crate::utils::check_strides_validity;
9
10use super::{DeviceTensor, OwnedDeviceTensor};
11
12#[derive(Debug, Clone, Hash, PartialEq, Eq)]
22pub struct DeviceView {
23 pub(crate) buffer: Arc<Box<dyn OwnedDeviceTensor>>,
24 pub(crate) dt: DatumType,
25 pub(crate) len: usize,
26 pub(crate) shape: TVec<usize>,
27 pub(crate) strides: TVec<isize>,
28 pub(crate) offset_bytes: usize,
29 pub(crate) exotic_fact: Option<Box<dyn ExoticFact>>,
30}
31
32impl DeviceView {
33 #[inline]
34 pub fn shape(&self) -> &[usize] {
35 self.shape.as_slice()
36 }
37
38 #[inline]
40 pub fn datum_type(&self) -> DatumType {
41 self.dt
42 }
43
44 #[inline]
45 pub fn strides(&self) -> &[isize] {
46 self.strides.as_slice()
47 }
48
49 pub fn device_buffer(&self) -> &dyn DeviceBuffer {
51 self.buffer.device_buffer()
52 }
53
54 pub fn device_buffer_ptr(&self) -> *const c_void {
55 self.buffer.device_buffer().ptr()
56 }
57
58 pub fn buffer_offset<I: Copy + 'static>(&self) -> I
60 where
61 usize: AsPrimitive<I>,
62 {
63 self.offset_bytes.as_()
64 }
65
66 pub fn exotic_fact(&self) -> Option<&dyn ExoticFact> {
67 self.exotic_fact.as_deref()
68 }
69
70 #[inline]
72 #[allow(clippy::len_without_is_empty)]
73 pub fn len(&self) -> usize {
74 self.len
75 }
76
77 pub fn as_bytes(&self) -> Vec<u8> {
78 let len = if let Some(of) = &self.exotic_fact {
79 of.mem_size().as_i64().unwrap() as usize
80 } else {
81 self.len() * self.dt.size_of()
82 };
83 self.buffer.get_bytes_slice(self.offset_bytes, len)
84 }
85
86 pub fn reshaped(&self, shape: impl Into<TVec<usize>>) -> TractResult<Self> {
88 ensure!(self.exotic_fact.is_none(), "Can't reshape exotic tensor");
89 let shape = shape.into();
90 if self.len() != shape.iter().product::<usize>() {
91 bail!("Invalid reshape {:?} to {:?}", self.shape(), shape);
92 }
93 if shape.as_slice() != self.shape() {
94 Ok(Self {
95 buffer: Arc::clone(&self.buffer),
96 dt: self.dt,
97 len: self.len,
98 strides: Tensor::natural_strides(&shape),
99 shape,
100 offset_bytes: self.offset_bytes,
101 exotic_fact: None,
102 })
103 } else {
104 Ok(self.clone())
105 }
106 }
107
108 pub fn restrided(&self, strides: impl Into<TVec<isize>>) -> TractResult<Self> {
109 ensure!(self.exotic_fact.is_none(), "Can't restride exotic tensor");
110 let strides = strides.into();
111 check_strides_validity(self.shape().into(), strides.clone())?;
112
113 if strides.as_slice() != self.strides() {
114 Ok(Self {
115 buffer: Arc::clone(&self.buffer),
116 dt: self.dt,
117 len: self.len,
118 strides,
119 shape: self.shape.clone(),
120 offset_bytes: self.offset_bytes,
121 exotic_fact: None,
122 })
123 } else {
124 Ok(self.clone())
125 }
126 }
127
128 pub fn is_packed(&self) -> bool {
131 self.strides.as_slice() == Tensor::natural_strides(&self.shape).as_slice()
132 }
133
134 pub fn to_host(&self) -> TractResult<Tensor> {
135 if !self.is_packed() {
138 ensure!(self.exotic_fact.is_none(), "an exotic tensor is never strided");
139 let packed = DeviceTensor::uninitialized_dt(self.dt, &self.shape)?;
140 let me: DeviceTensor = self.clone().into();
141 get_context()?.copy_nd(
142 &me,
143 0,
144 &self.strides,
145 &packed,
146 0,
147 &self.shape,
148 packed.strides(),
149 )?;
150 return Ok((*packed.to_host()?).clone());
151 }
152 get_context()?.synchronize()?;
153 let content = self.as_bytes();
154 unsafe {
155 if let Some(bqf) =
156 self.exotic_fact.as_ref().and_then(|of| of.downcast_ref::<BlockQuantFact>())
157 {
158 Ok(BlockQuantStorage::new(
159 bqf.format.clone(),
160 bqf.m(),
161 bqf.k(),
162 Arc::new(Blob::from_bytes(&content)?),
163 )?
164 .into_tensor_with_shape(self.dt, bqf.shape()))
165 } else {
166 Tensor::from_raw_dt(self.dt, &self.shape, &content)
167 }
168 }
169 }
170}
171
172impl Display for DeviceView {
173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174 let content = self
175 .clone()
176 .to_host()
177 .unwrap()
178 .dump(false)
179 .unwrap_or_else(|e| format!("Error : {e:?}"));
180 write!(f, "DeviceView: {{ {content} }}")
181 }
182}