1use std::ffi::c_void;
5use std::ptr::null_mut;
6
7use crate::Gpu;
8use crate::driver::{DevPtr, EVENT_DEFAULT, Handle};
9
10pub struct DeviceBuffer<'g> {
12 gpu: &'g Gpu,
13 ptr: DevPtr,
14 len: usize,
15}
16
17impl Gpu {
18 pub fn alloc(&self, len: usize) -> Result<DeviceBuffer<'_>, String> {
20 self.bind()?;
21 let mut ptr = 0;
22 if len > 0 {
23 let bytes = len
24 .checked_mul(size_of::<f32>())
25 .ok_or("GPU buffer size overflow")?;
26 self.drv.check(
27 unsafe { (self.drv.mem_alloc)(&mut ptr, bytes) },
28 "cuMemAlloc",
29 )?;
30 }
31 Ok(DeviceBuffer {
32 gpu: self,
33 ptr,
34 len,
35 })
36 }
37
38 pub fn upload(&self, data: &[f32]) -> Result<DeviceBuffer<'_>, String> {
40 let mut buf = self.alloc(data.len())?;
41 buf.copy_from_host(data)?;
42 Ok(buf)
43 }
44}
45
46impl<'g> DeviceBuffer<'g> {
47 pub fn len(&self) -> usize {
49 self.len
50 }
51
52 pub fn is_empty(&self) -> bool {
53 self.len == 0
54 }
55
56 pub(crate) fn ptr(&self) -> DevPtr {
58 self.ptr
59 }
60
61 pub fn copy_from_host(&mut self, data: &[f32]) -> Result<(), String> {
63 assert_eq!(data.len(), self.len, "拷入长度与显存长度不同");
64 if self.len == 0 {
65 return Ok(());
66 }
67 let g = self.gpu;
68 g.bind()?;
69 let r = unsafe {
70 (g.drv.memcpy_htod)(
71 self.ptr,
72 data.as_ptr() as *const c_void,
73 self.len * size_of::<f32>(),
74 )
75 };
76 g.drv.check(r, "cuMemcpyHtoD")
77 }
78
79 pub fn copy_to_host(&self, out: &mut [f32]) -> Result<(), String> {
81 assert_eq!(out.len(), self.len, "拷回长度与显存长度不同");
82 if self.len == 0 {
83 return Ok(());
84 }
85 let g = self.gpu;
86 g.bind()?;
87 let r = unsafe {
88 (g.drv.memcpy_dtoh)(
89 out.as_mut_ptr() as *mut c_void,
90 self.ptr,
91 self.len * size_of::<f32>(),
92 )
93 };
94 g.drv.check(r, "cuMemcpyDtoH")
95 }
96
97 pub(crate) fn write_prefix(&self, data: &[f32]) -> Result<(), String> {
99 assert!(data.len() <= self.len, "拷入长度超过显存长度");
100 if data.is_empty() {
101 return Ok(());
102 }
103 let g = self.gpu;
104 g.bind()?;
105 let r = unsafe {
106 (g.drv.memcpy_htod)(
107 self.ptr,
108 data.as_ptr() as *const c_void,
109 std::mem::size_of_val(data),
110 )
111 };
112 g.drv.check(r, "cuMemcpyHtoD")
113 }
114
115 pub(crate) fn read_prefix(&self, out: &mut [f32]) -> Result<(), String> {
117 assert!(out.len() <= self.len, "拷回长度超过显存长度");
118 if out.is_empty() {
119 return Ok(());
120 }
121 let g = self.gpu;
122 g.bind()?;
123 let r = unsafe {
124 (g.drv.memcpy_dtoh)(
125 out.as_mut_ptr() as *mut c_void,
126 self.ptr,
127 std::mem::size_of_val(out),
128 )
129 };
130 g.drv.check(r, "cuMemcpyDtoH")
131 }
132
133 pub fn to_vec(&self) -> Result<Vec<f32>, String> {
135 let mut v = vec![0f32; self.len];
136 self.copy_to_host(&mut v)?;
137 Ok(v)
138 }
139
140 pub fn copy_from_device(&mut self, src: &DeviceBuffer<'_>) -> Result<(), String> {
142 assert_eq!(src.len, self.len, "显存拷贝长度不同");
143 if self.len == 0 {
144 return Ok(());
145 }
146 let g = self.gpu;
147 g.bind()?;
148 g.drv.check(
149 unsafe { (g.drv.memcpy_dtod)(self.ptr, src.ptr, self.len * size_of::<f32>()) },
150 "cuMemcpyDtoD",
151 )
152 }
153}
154
155impl Drop for DeviceBuffer<'_> {
156 fn drop(&mut self) {
157 if self.len > 0 && self.gpu.bind().is_ok() {
158 unsafe { (self.gpu.drv.mem_free)(self.ptr) };
159 }
160 }
161}
162
163#[derive(Default)]
166pub(crate) struct Args {
167 slots: Vec<u64>,
168}
169
170impl Args {
171 pub fn ptr(mut self, b: &DeviceBuffer<'_>) -> Self {
172 self.slots.push(b.ptr());
173 self
174 }
175
176 pub fn raw_ptr(mut self, p: DevPtr) -> Self {
177 self.slots.push(p);
178 self
179 }
180
181 pub fn u32(mut self, v: usize) -> Self {
183 self.slots
184 .push(u32::try_from(v).expect("内核参数超出 u32") as u64);
185 self
186 }
187
188 pub fn u64(mut self, v: usize) -> Self {
189 self.slots.push(v as u64);
190 self
191 }
192
193 pub fn f32(mut self, v: f32) -> Self {
194 self.slots.push(v.to_bits() as u64);
195 self
196 }
197}
198
199const _: () = assert!(cfg!(target_endian = "little"), "Args 的槽布局假定小端");
200
201impl Gpu {
202 pub(crate) fn launch(
204 &self,
205 f: Handle,
206 grid: (u32, u32, u32),
207 block: (u32, u32, u32),
208 args: Args,
209 ) -> Result<(), String> {
210 if grid.0 == 0 || grid.1 == 0 || grid.2 == 0 {
211 return Ok(()); }
213 self.bind()?;
214 let mut slots = args.slots;
215 let mut ptrs: Vec<*mut c_void> = slots
216 .iter_mut()
217 .map(|s| s as *mut u64 as *mut c_void)
218 .collect();
219 let r = unsafe {
220 (self.drv.launch_kernel)(
221 f,
222 grid.0,
223 grid.1,
224 grid.2,
225 block.0,
226 block.1,
227 block.2,
228 0,
229 null_mut(),
230 ptrs.as_mut_ptr(),
231 null_mut(),
232 )
233 };
234 self.drv.check(r, "cuLaunchKernel")
235 }
236}
237
238pub struct Timer<'g> {
240 gpu: &'g Gpu,
241 start: Handle,
242 end: Handle,
243}
244
245impl Gpu {
246 pub fn timer(&self) -> Result<Timer<'_>, String> {
248 self.bind()?;
249 let mut t = Timer {
250 gpu: self,
251 start: null_mut(),
252 end: null_mut(),
253 };
254 let d = &self.drv;
255 d.check(
256 unsafe { (d.event_create)(&mut t.start, EVENT_DEFAULT) },
257 "cuEventCreate",
258 )?;
259 d.check(
260 unsafe { (d.event_create)(&mut t.end, EVENT_DEFAULT) },
261 "cuEventCreate",
262 )?;
263 d.check(
264 unsafe { (d.event_record)(t.start, null_mut()) },
265 "cuEventRecord",
266 )?;
267 Ok(t)
268 }
269}
270
271impl Timer<'_> {
272 pub fn stop_ms(&self) -> Result<f32, String> {
274 let d = &self.gpu.drv;
275 self.gpu.bind()?;
276 d.check(
277 unsafe { (d.event_record)(self.end, null_mut()) },
278 "cuEventRecord",
279 )?;
280 d.check(
281 unsafe { (d.event_synchronize)(self.end) },
282 "cuEventSynchronize",
283 )?;
284 let mut ms = 0f32;
285 d.check(
286 unsafe { (d.event_elapsed_time)(&mut ms, self.start, self.end) },
287 "cuEventElapsedTime",
288 )?;
289 Ok(ms)
290 }
291}
292
293impl Drop for Timer<'_> {
294 fn drop(&mut self) {
295 if self.gpu.bind().is_ok() {
296 for e in [self.start, self.end] {
297 if !e.is_null() {
298 unsafe { (self.gpu.drv.event_destroy)(e) };
299 }
300 }
301 }
302 }
303}