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