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(c"Over MAX_SAFE_INTEGER".to_owned()));
55 }
56 let size: usize = size
57 .try_into()
58 .map_err(|_| Error::Range(c"Over usize".to_owned()))?;
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 #[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 #[conditional_malloc_size_of]
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 fn Unmap(&self) {
194 let promise = self.pending_map.borrow_mut().take();
196 if let Some(promise) = promise {
197 promise.reject_error(Error::Abort(None), CanGc::note());
198 *self.pending_map.borrow_mut() = Some(promise);
199 }
200 let mut mapping = self.mapping.borrow_mut().take();
202 let mapping = if let Some(mapping) = mapping.as_mut() {
203 mapping
204 } else {
205 return;
206 };
207
208 mapping.data.clear_views();
210 if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
212 buffer_id: self.id().0,
213 mapping: if mapping.mode >= GPUMapModeConstants::WRITE {
214 Some(Mapping {
215 data: GenericSharedMemory::from_bytes(mapping.data.data()),
216 range: mapping.range.clone(),
217 mode: HostMap::Write,
218 })
219 } else {
220 None
221 },
222 }) {
223 warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
224 }
225 }
226
227 fn Destroy(&self) {
229 self.Unmap();
231 if let Err(e) = self
233 .channel
234 .0
235 .send(WebGPURequest::DestroyBuffer(self.buffer.0))
236 {
237 warn!(
238 "Failed to send WebGPURequest::DestroyBuffer({:?}) ({})",
239 self.buffer.0, e
240 );
241 };
242 }
243
244 fn MapAsync(
246 &self,
247 mode: u32,
248 offset: GPUSize64,
249 size: Option<GPUSize64>,
250 comp: InRealm,
251 can_gc: CanGc,
252 ) -> Rc<Promise> {
253 let promise = Promise::new_in_current_realm(comp, can_gc);
254 if self.pending_map.borrow().is_some() {
256 promise.reject_error(Error::Operation(None), can_gc);
257 return promise;
258 }
259 *self.pending_map.borrow_mut() = Some(promise.clone());
261 let host_map = match mode {
263 GPUMapModeConstants::READ => HostMap::Read,
264 GPUMapModeConstants::WRITE => HostMap::Write,
265 _ => {
266 self.device
267 .dispatch_error(webgpu_traits::Error::Validation(String::from(
268 "Invalid MapModeFlags",
269 )));
270 self.map_failure(&promise, can_gc);
271 return promise;
272 },
273 };
274
275 let callback = callback_promise(
276 &promise,
277 self,
278 self.global().task_manager().dom_manipulation_task_source(),
279 );
280 if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync {
281 callback,
282 buffer_id: self.buffer.0,
283 device_id: self.device.id().0,
284 host_map,
285 offset,
286 size,
287 }) {
288 warn!(
289 "Failed to send BufferMapAsync ({:?}) ({})",
290 self.buffer.0, e
291 );
292 self.map_failure(&promise, can_gc);
293 return promise;
294 }
295 promise
297 }
298
299 fn GetMappedRange(
301 &self,
302 _cx: JSContext,
303 offset: GPUSize64,
304 size: Option<GPUSize64>,
305 can_gc: CanGc,
306 ) -> Fallible<RootedTraceableBox<HeapArrayBuffer>> {
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
314 .mapping
315 .borrow_mut()
316 .take()
317 .ok_or(Error::Operation(None))?;
318
319 let valid = offset % wgpu_types::MAP_ALIGNMENT == 0 &&
320 range_size % wgpu_types::COPY_BUFFER_ALIGNMENT == 0 &&
321 offset >= mapping.range.start &&
322 offset + range_size <= mapping.range.end;
323 if !valid {
324 self.mapping.borrow_mut().replace(mapping);
325 return Err(Error::Operation(None));
326 }
327
328 let rebased_offset = (offset - mapping.range.start) as usize;
332 let result = mapping
333 .data
334 .view(rebased_offset..rebased_offset + range_size as usize, can_gc)
335 .map(|view| view.array_buffer())
336 .map_err(|()| Error::Operation(None));
337
338 self.mapping.borrow_mut().replace(mapping);
339 result
340 }
341
342 fn Label(&self) -> USVString {
344 self.label.borrow().clone()
345 }
346
347 fn SetLabel(&self, value: USVString) {
349 *self.label.borrow_mut() = value;
350 }
351
352 fn Size(&self) -> GPUSize64 {
354 self.size
355 }
356
357 fn Usage(&self) -> GPUFlagsConstant {
359 self.usage
360 }
361
362 fn MapState(&self) -> GPUBufferMapState {
364 if self.mapping.borrow().is_some() {
366 GPUBufferMapState::Mapped
367 } else if self.pending_map.borrow().is_some() {
368 GPUBufferMapState::Pending
369 } else {
370 GPUBufferMapState::Unmapped
371 }
372 }
373}
374
375impl GPUBuffer {
376 fn map_failure(&self, p: &Rc<Promise>, can_gc: CanGc) {
377 if self.pending_map.borrow().as_ref() != Some(p) {
379 assert!(p.is_rejected());
380 return;
381 }
382 assert!(p.is_pending());
384 self.pending_map.borrow_mut().take();
386 let is_lost = self.device.is_lost();
388 if is_lost {
389 p.reject_error(Error::Abort(None), can_gc);
390 } else {
391 p.reject_error(Error::Operation(None), can_gc);
392 }
393 }
394
395 fn map_success(&self, p: &Rc<Promise>, wgpu_mapping: Mapping, can_gc: CanGc) {
396 if self.pending_map.borrow().as_ref() != Some(p) {
398 assert!(p.is_rejected());
399 return;
400 }
401
402 assert!(p.is_pending());
404
405 let mapping = ActiveBufferMapping::new(
407 match wgpu_mapping.mode {
408 HostMap::Read => GPUMapModeConstants::READ,
409 HostMap::Write => GPUMapModeConstants::WRITE,
410 },
411 wgpu_mapping.range,
412 );
413
414 match mapping {
415 Err(error) => {
416 *self.pending_map.borrow_mut() = None;
417 p.reject_error(error.clone(), can_gc);
418 },
419 Ok(mut mapping) => {
420 mapping.data.load(&wgpu_mapping.data);
422 self.mapping.borrow_mut().replace(mapping);
424 self.pending_map.borrow_mut().take();
426 p.resolve_native(&(), can_gc);
427 },
428 }
429 }
430}
431
432impl RoutedPromiseListener<Result<Mapping, BufferAccessError>> for GPUBuffer {
433 fn handle_response(
434 &self,
435 response: Result<Mapping, BufferAccessError>,
436 promise: &Rc<Promise>,
437 can_gc: CanGc,
438 ) {
439 match response {
440 Ok(mapping) => self.map_success(promise, mapping, can_gc),
441 Err(_) => self.map_failure(promise, can_gc),
442 }
443 }
444}