1use std::cell::RefCell;
4use std::rc::Rc;
5use std::time::Duration;
6
7use deno_core::futures::channel::oneshot;
8use deno_core::op2;
9use deno_core::v8;
10use deno_core::webidl::WebIdlInterfaceConverter;
11use deno_core::GarbageCollected;
12use deno_core::WebIDL;
13use deno_error::JsErrorBox;
14use wgpu_core::device::HostMap as MapMode;
15
16use crate::Instance;
17
18#[derive(WebIDL)]
19#[webidl(dictionary)]
20pub(crate) struct GPUBufferDescriptor {
21 #[webidl(default = String::new())]
22 pub label: String,
23
24 pub size: u64,
25 #[options(enforce_range = true)]
26 pub usage: u32,
27 #[webidl(default = false)]
28 pub mapped_at_creation: bool,
29}
30
31#[derive(Debug, thiserror::Error, deno_error::JsError)]
32pub enum BufferError {
33 #[class(generic)]
34 #[error(transparent)]
35 Canceled(#[from] oneshot::Canceled),
36 #[class("DOMExceptionOperationError")]
37 #[error(transparent)]
38 Access(#[from] wgpu_core::resource::BufferAccessError),
39 #[class("DOMExceptionOperationError")]
40 #[error("{0}")]
41 Operation(&'static str),
42 #[class(inherit)]
43 #[error(transparent)]
44 Other(#[from] JsErrorBox),
45}
46
47pub struct GPUBuffer {
48 pub instance: Instance,
49 pub error_handler: super::error::ErrorHandler,
50
51 pub id: wgpu_core::id::BufferId,
52 pub device: wgpu_core::id::DeviceId,
53
54 pub label: String,
55
56 pub size: u64,
57 pub usage: u32,
58
59 pub map_state: RefCell<&'static str>,
60 pub map_mode: RefCell<Option<MapMode>>,
61
62 pub mapped_js_buffers: RefCell<Vec<v8::Global<v8::ArrayBuffer>>>,
63}
64
65impl Drop for GPUBuffer {
66 fn drop(&mut self) {
67 self.instance.buffer_drop(self.id);
68 }
69}
70
71impl WebIdlInterfaceConverter for GPUBuffer {
72 const NAME: &'static str = "GPUBuffer";
73}
74
75impl GarbageCollected for GPUBuffer {}
76
77#[op2]
78impl GPUBuffer {
79 #[getter]
80 #[string]
81 fn label(&self) -> String {
82 self.label.clone()
83 }
84 #[setter]
85 #[string]
86 fn label(&self, #[webidl] _label: String) {
87 }
89
90 #[getter]
91 #[number]
92 fn size(&self) -> u64 {
93 self.size
94 }
95 #[getter]
96 fn usage(&self) -> u32 {
97 self.usage
98 }
99
100 #[getter]
101 #[string]
102 fn map_state(&self) -> &'static str {
103 *self.map_state.borrow()
104 }
105
106 #[async_method]
107 async fn map_async(
108 &self,
109 #[webidl(options(enforce_range = true))] mode: u32,
110 #[webidl(default = 0)] offset: u64,
111 #[webidl] size: Option<u64>,
112 ) -> Result<(), BufferError> {
113 let read_mode = (mode & 0x0001) == 0x0001;
114 let write_mode = (mode & 0x0002) == 0x0002;
115 if (read_mode && write_mode) || (!read_mode && !write_mode) {
116 return Err(BufferError::Operation(
117 "exactly one of READ or WRITE map mode must be set",
118 ));
119 }
120
121 let mode = if read_mode {
122 MapMode::Read
123 } else {
124 assert!(write_mode);
125 MapMode::Write
126 };
127
128 {
129 *self.map_state.borrow_mut() = "pending";
130 }
131
132 let (sender, receiver) =
133 oneshot::channel::<wgpu_core::resource::BufferAccessResult>();
134
135 {
136 let callback = Box::new(move |status| {
137 sender.send(status).unwrap();
138 });
139
140 let err = self
141 .instance
142 .buffer_map_async(
143 self.id,
144 offset,
145 size,
146 wgpu_core::resource::BufferMapOperation {
147 host: mode,
148 callback: Some(callback),
149 },
150 )
151 .err();
152
153 if err.is_some() {
154 self.error_handler.push_error(err);
155 return Err(BufferError::Operation("validation error occurred"));
156 }
157 }
158
159 let done = Rc::new(RefCell::new(false));
160 let done_ = done.clone();
161 let device_poll_fut = async move {
162 while !*done.borrow() {
163 {
164 self
165 .instance
166 .device_poll(self.device, wgpu_types::Maintain::wait())
167 .unwrap();
168 }
169 tokio::time::sleep(Duration::from_millis(10)).await;
170 }
171 Ok::<(), BufferError>(())
172 };
173
174 let receiver_fut = async move {
175 receiver.await??;
176 let mut done = done_.borrow_mut();
177 *done = true;
178 Ok::<(), BufferError>(())
179 };
180
181 tokio::try_join!(device_poll_fut, receiver_fut)?;
182
183 *self.map_state.borrow_mut() = "mapped";
184 *self.map_mode.borrow_mut() = Some(mode);
185
186 Ok(())
187 }
188
189 fn get_mapped_range<'s>(
190 &self,
191 scope: &mut v8::HandleScope<'s>,
192 #[webidl(default = 0)] offset: u64,
193 #[webidl] size: Option<u64>,
194 ) -> Result<v8::Local<'s, v8::ArrayBuffer>, BufferError> {
195 let (slice_pointer, range_size) = self
196 .instance
197 .buffer_get_mapped_range(self.id, offset, size)
198 .map_err(BufferError::Access)?;
199
200 let mode = self.map_mode.borrow();
201 let mode = mode.as_ref().unwrap();
202
203 let bs = if mode == &MapMode::Write {
204 unsafe extern "C" fn noop_deleter_callback(
205 _data: *mut std::ffi::c_void,
206 _byte_length: usize,
207 _deleter_data: *mut std::ffi::c_void,
208 ) {
209 }
210
211 unsafe {
213 v8::ArrayBuffer::new_backing_store_from_ptr(
214 slice_pointer.as_ptr() as _,
215 range_size as usize,
216 noop_deleter_callback,
217 std::ptr::null_mut(),
218 )
219 }
220 } else {
221 let slice = unsafe {
223 std::slice::from_raw_parts(slice_pointer.as_ptr(), range_size as usize)
224 };
225 v8::ArrayBuffer::new_backing_store_from_vec(slice.to_vec())
226 };
227
228 let shared_bs = bs.make_shared();
229 let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs);
230
231 if mode == &MapMode::Write {
232 self
233 .mapped_js_buffers
234 .borrow_mut()
235 .push(v8::Global::new(scope, ab));
236 }
237
238 Ok(ab)
239 }
240
241 #[nofast]
242 fn unmap(&self, scope: &mut v8::HandleScope) -> Result<(), BufferError> {
243 for ab in self.mapped_js_buffers.replace(vec![]) {
244 let ab = ab.open(scope);
245 ab.detach(None);
246 }
247
248 self
249 .instance
250 .buffer_unmap(self.id)
251 .map_err(BufferError::Access)?;
252
253 *self.map_state.borrow_mut() = "unmapped";
254
255 Ok(())
256 }
257
258 #[fast]
259 fn destroy(&self) -> Result<(), JsErrorBox> {
260 self
261 .instance
262 .buffer_destroy(self.id)
263 .map_err(|e| JsErrorBox::generic(e.to_string()))
264 }
265}