1use std::ops::Range;
6use std::rc::Rc;
7use std::string::String;
8
9use dom_struct::dom_struct;
10use ipc_channel::ipc::IpcSharedMemory;
11use js::typedarray::ArrayBuffer;
12use webgpu_traits::{Mapping, WebGPU, WebGPUBuffer, WebGPURequest};
13use wgpu_core::device::HostMap;
14use wgpu_core::resource::BufferAccessError;
15
16use crate::conversions::Convert;
17use crate::dom::bindings::buffer_source::DataBlock;
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
20 GPUBufferDescriptor, GPUBufferMapState, GPUBufferMethods, GPUFlagsConstant,
21 GPUMapModeConstants, GPUMapModeFlags, GPUSize64,
22};
23use crate::dom::bindings::error::{Error, Fallible};
24use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
25use crate::dom::bindings::root::{Dom, DomRoot};
26use crate::dom::bindings::str::USVString;
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::promise::Promise;
29use crate::dom::webgpu::gpudevice::GPUDevice;
30use crate::realms::InRealm;
31use crate::routed_promise::{RoutedPromiseListener, route_promise};
32use crate::script_runtime::{CanGc, JSContext};
33
34#[derive(JSTraceable, MallocSizeOf)]
35pub(crate) struct ActiveBufferMapping {
36 pub(crate) data: DataBlock,
40 mode: GPUMapModeFlags,
42 range: Range<u64>,
44}
45
46impl ActiveBufferMapping {
47 pub(crate) fn new(mode: GPUMapModeFlags, range: Range<u64>) -> Fallible<Self> {
49 let size = range.end - range.start;
51 if size > (1 << 53) - 1 {
53 return Err(Error::Range("Over MAX_SAFE_INTEGER".to_string()));
54 }
55 let size: usize = size
56 .try_into()
57 .map_err(|_| Error::Range("Over usize".to_string()))?;
58 Ok(Self {
59 data: DataBlock::new_zeroed(size),
60 mode,
61 range,
62 })
63 }
64}
65
66#[dom_struct]
67pub(crate) struct GPUBuffer {
68 reflector_: Reflector,
69 #[ignore_malloc_size_of = "defined in webgpu"]
70 #[no_trace]
71 channel: WebGPU,
72 label: DomRefCell<USVString>,
73 #[no_trace]
74 buffer: WebGPUBuffer,
75 device: Dom<GPUDevice>,
76 size: GPUSize64,
78 usage: GPUFlagsConstant,
80 #[ignore_malloc_size_of = "promises are hard"]
82 pending_map: DomRefCell<Option<Rc<Promise>>>,
83 mapping: DomRefCell<Option<ActiveBufferMapping>>,
85}
86
87impl GPUBuffer {
88 fn new_inherited(
89 channel: WebGPU,
90 buffer: WebGPUBuffer,
91 device: &GPUDevice,
92 size: GPUSize64,
93 usage: GPUFlagsConstant,
94 mapping: Option<ActiveBufferMapping>,
95 label: USVString,
96 ) -> Self {
97 Self {
98 reflector_: Reflector::new(),
99 channel,
100 label: DomRefCell::new(label),
101 device: Dom::from_ref(device),
102 buffer,
103 pending_map: DomRefCell::new(None),
104 size,
105 usage,
106 mapping: DomRefCell::new(mapping),
107 }
108 }
109
110 #[allow(clippy::too_many_arguments)]
111 pub(crate) fn new(
112 global: &GlobalScope,
113 channel: WebGPU,
114 buffer: WebGPUBuffer,
115 device: &GPUDevice,
116 size: GPUSize64,
117 usage: GPUFlagsConstant,
118 mapping: Option<ActiveBufferMapping>,
119 label: USVString,
120 can_gc: CanGc,
121 ) -> DomRoot<Self> {
122 reflect_dom_object(
123 Box::new(GPUBuffer::new_inherited(
124 channel, buffer, device, size, usage, mapping, label,
125 )),
126 global,
127 can_gc,
128 )
129 }
130}
131
132impl GPUBuffer {
133 pub(crate) fn id(&self) -> WebGPUBuffer {
134 self.buffer
135 }
136
137 pub(crate) fn create(
139 device: &GPUDevice,
140 descriptor: &GPUBufferDescriptor,
141 can_gc: CanGc,
142 ) -> Fallible<DomRoot<GPUBuffer>> {
143 let desc = wgpu_types::BufferDescriptor {
144 label: (&descriptor.parent).convert(),
145 size: descriptor.size as wgpu_types::BufferAddress,
146 usage: wgpu_types::BufferUsages::from_bits_retain(descriptor.usage),
147 mapped_at_creation: descriptor.mappedAtCreation,
148 };
149 let id = device.global().wgpu_id_hub().create_buffer_id();
150
151 device
152 .channel()
153 .0
154 .send(WebGPURequest::CreateBuffer {
155 device_id: device.id().0,
156 buffer_id: id,
157 descriptor: desc,
158 })
159 .expect("Failed to create WebGPU buffer");
160
161 let buffer = WebGPUBuffer(id);
162 let mapping = if descriptor.mappedAtCreation {
163 Some(ActiveBufferMapping::new(
164 GPUMapModeConstants::WRITE,
165 0..descriptor.size,
166 )?)
167 } else {
168 None
169 };
170
171 Ok(GPUBuffer::new(
172 &device.global(),
173 device.channel().clone(),
174 buffer,
175 device,
176 descriptor.size,
177 descriptor.usage,
178 mapping,
179 descriptor.parent.label.clone(),
180 can_gc,
181 ))
182 }
183}
184
185impl Drop for GPUBuffer {
186 fn drop(&mut self) {
187 self.Destroy()
188 }
189}
190
191impl GPUBufferMethods<crate::DomTypeHolder> for GPUBuffer {
192 #[allow(unsafe_code)]
193 fn Unmap(&self) {
195 if let Some(promise) = self.pending_map.borrow_mut().take() {
197 promise.reject_error(Error::Abort, CanGc::note());
198 }
199 let mut mapping = self.mapping.borrow_mut().take();
201 let mapping = if let Some(mapping) = mapping.as_mut() {
202 mapping
203 } else {
204 return;
205 };
206
207 mapping.data.clear_views();
209 if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
211 buffer_id: self.id().0,
212 mapping: if mapping.mode >= GPUMapModeConstants::WRITE {
213 Some(Mapping {
214 data: IpcSharedMemory::from_bytes(mapping.data.data()),
215 range: mapping.range.clone(),
216 mode: HostMap::Write,
217 })
218 } else {
219 None
220 },
221 }) {
222 warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
223 }
224 }
225
226 fn Destroy(&self) {
228 self.Unmap();
230 if let Err(e) = self
232 .channel
233 .0
234 .send(WebGPURequest::DestroyBuffer(self.buffer.0))
235 {
236 warn!(
237 "Failed to send WebGPURequest::DestroyBuffer({:?}) ({})",
238 self.buffer.0, e
239 );
240 };
241 }
242
243 fn MapAsync(
245 &self,
246 mode: u32,
247 offset: GPUSize64,
248 size: Option<GPUSize64>,
249 comp: InRealm,
250 can_gc: CanGc,
251 ) -> Rc<Promise> {
252 let promise = Promise::new_in_current_realm(comp, can_gc);
253 if self.pending_map.borrow().is_some() {
255 promise.reject_error(Error::Operation, can_gc);
256 return promise;
257 }
258 *self.pending_map.borrow_mut() = Some(promise.clone());
260 let host_map = match mode {
262 GPUMapModeConstants::READ => HostMap::Read,
263 GPUMapModeConstants::WRITE => HostMap::Write,
264 _ => {
265 self.device
266 .dispatch_error(webgpu_traits::Error::Validation(String::from(
267 "Invalid MapModeFlags",
268 )));
269 self.map_failure(&promise, can_gc);
270 return promise;
271 },
272 };
273
274 let sender = route_promise(
275 &promise,
276 self,
277 self.global().task_manager().dom_manipulation_task_source(),
278 );
279 if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync {
280 sender,
281 buffer_id: self.buffer.0,
282 device_id: self.device.id().0,
283 host_map,
284 offset,
285 size,
286 }) {
287 warn!(
288 "Failed to send BufferMapAsync ({:?}) ({})",
289 self.buffer.0, e
290 );
291 self.map_failure(&promise, can_gc);
292 return promise;
293 }
294 promise
296 }
297
298 #[allow(unsafe_code)]
300 fn GetMappedRange(
301 &self,
302 _cx: JSContext,
303 offset: GPUSize64,
304 size: Option<GPUSize64>,
305 can_gc: CanGc,
306 ) -> Fallible<ArrayBuffer> {
307 let range_size = if let Some(s) = size {
308 s
309 } else {
310 self.size.saturating_sub(offset)
311 };
312 let mut mapping = self.mapping.borrow_mut();
314 let mapping = mapping.as_mut().ok_or(Error::Operation)?;
315
316 let valid = offset % wgpu_types::MAP_ALIGNMENT == 0 &&
317 range_size % wgpu_types::COPY_BUFFER_ALIGNMENT == 0 &&
318 offset >= mapping.range.start &&
319 offset + range_size <= mapping.range.end;
320 if !valid {
321 return Err(Error::Operation);
322 }
323
324 let rebased_offset = (offset - mapping.range.start) as usize;
328 mapping
329 .data
330 .view(rebased_offset..rebased_offset + range_size as usize, can_gc)
331 .map(|view| view.array_buffer())
332 .map_err(|()| Error::Operation)
333 }
334
335 fn Label(&self) -> USVString {
337 self.label.borrow().clone()
338 }
339
340 fn SetLabel(&self, value: USVString) {
342 *self.label.borrow_mut() = value;
343 }
344
345 fn Size(&self) -> GPUSize64 {
347 self.size
348 }
349
350 fn Usage(&self) -> GPUFlagsConstant {
352 self.usage
353 }
354
355 fn MapState(&self) -> GPUBufferMapState {
357 if self.mapping.borrow().is_some() {
359 GPUBufferMapState::Mapped
360 } else if self.pending_map.borrow().is_some() {
361 GPUBufferMapState::Pending
362 } else {
363 GPUBufferMapState::Unmapped
364 }
365 }
366}
367
368impl GPUBuffer {
369 fn map_failure(&self, p: &Rc<Promise>, can_gc: CanGc) {
370 let mut pending_map = self.pending_map.borrow_mut();
371 if pending_map.as_ref() != Some(p) {
373 assert!(p.is_rejected());
374 return;
375 }
376 assert!(p.is_pending());
378 pending_map.take();
380 if self.device.is_lost() {
382 p.reject_error(Error::Abort, can_gc);
383 } else {
384 p.reject_error(Error::Operation, can_gc);
385 }
386 }
387
388 fn map_success(&self, p: &Rc<Promise>, wgpu_mapping: Mapping, can_gc: CanGc) {
389 let mut pending_map = self.pending_map.borrow_mut();
390
391 if pending_map.as_ref() != Some(p) {
393 assert!(p.is_rejected());
394 return;
395 }
396
397 assert!(p.is_pending());
399
400 let mapping = ActiveBufferMapping::new(
402 match wgpu_mapping.mode {
403 HostMap::Read => GPUMapModeConstants::READ,
404 HostMap::Write => GPUMapModeConstants::WRITE,
405 },
406 wgpu_mapping.range,
407 );
408
409 match mapping {
410 Err(error) => {
411 *pending_map = None;
412 p.reject_error(error.clone(), can_gc);
413 },
414 Ok(mut mapping) => {
415 mapping.data.load(&wgpu_mapping.data);
417 self.mapping.borrow_mut().replace(mapping);
419 pending_map.take();
421 p.resolve_native(&(), can_gc);
422 },
423 }
424 }
425}
426
427impl RoutedPromiseListener<Result<Mapping, BufferAccessError>> for GPUBuffer {
428 fn handle_response(
429 &self,
430 response: Result<Mapping, BufferAccessError>,
431 promise: &Rc<Promise>,
432 can_gc: CanGc,
433 ) {
434 match response {
435 Ok(mapping) => self.map_success(promise, mapping, can_gc),
436 Err(_) => self.map_failure(promise, can_gc),
437 }
438 }
439}