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 #[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 if let Some(promise) = self.pending_map.borrow_mut().take() {
196 promise.reject_error(Error::Abort, CanGc::note());
197 }
198 let mut mapping = self.mapping.borrow_mut().take();
200 let mapping = if let Some(mapping) = mapping.as_mut() {
201 mapping
202 } else {
203 return;
204 };
205
206 mapping.data.clear_views();
208 if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
210 buffer_id: self.id().0,
211 mapping: if mapping.mode >= GPUMapModeConstants::WRITE {
212 Some(Mapping {
213 data: IpcSharedMemory::from_bytes(mapping.data.data()),
214 range: mapping.range.clone(),
215 mode: HostMap::Write,
216 })
217 } else {
218 None
219 },
220 }) {
221 warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
222 }
223 }
224
225 fn Destroy(&self) {
227 self.Unmap();
229 if let Err(e) = self
231 .channel
232 .0
233 .send(WebGPURequest::DestroyBuffer(self.buffer.0))
234 {
235 warn!(
236 "Failed to send WebGPURequest::DestroyBuffer({:?}) ({})",
237 self.buffer.0, e
238 );
239 };
240 }
241
242 fn MapAsync(
244 &self,
245 mode: u32,
246 offset: GPUSize64,
247 size: Option<GPUSize64>,
248 comp: InRealm,
249 can_gc: CanGc,
250 ) -> Rc<Promise> {
251 let promise = Promise::new_in_current_realm(comp, can_gc);
252 if self.pending_map.borrow().is_some() {
254 promise.reject_error(Error::Operation, can_gc);
255 return promise;
256 }
257 *self.pending_map.borrow_mut() = Some(promise.clone());
259 let host_map = match mode {
261 GPUMapModeConstants::READ => HostMap::Read,
262 GPUMapModeConstants::WRITE => HostMap::Write,
263 _ => {
264 self.device
265 .dispatch_error(webgpu_traits::Error::Validation(String::from(
266 "Invalid MapModeFlags",
267 )));
268 self.map_failure(&promise, can_gc);
269 return promise;
270 },
271 };
272
273 let sender = route_promise(
274 &promise,
275 self,
276 self.global().task_manager().dom_manipulation_task_source(),
277 );
278 if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync {
279 sender,
280 buffer_id: self.buffer.0,
281 device_id: self.device.id().0,
282 host_map,
283 offset,
284 size,
285 }) {
286 warn!(
287 "Failed to send BufferMapAsync ({:?}) ({})",
288 self.buffer.0, e
289 );
290 self.map_failure(&promise, can_gc);
291 return promise;
292 }
293 promise
295 }
296
297 fn GetMappedRange(
299 &self,
300 _cx: JSContext,
301 offset: GPUSize64,
302 size: Option<GPUSize64>,
303 can_gc: CanGc,
304 ) -> Fallible<ArrayBuffer> {
305 let range_size = if let Some(s) = size {
306 s
307 } else {
308 self.size.saturating_sub(offset)
309 };
310 let mut mapping = self.mapping.borrow_mut();
312 let mapping = mapping.as_mut().ok_or(Error::Operation)?;
313
314 let valid = offset % wgpu_types::MAP_ALIGNMENT == 0 &&
315 range_size % wgpu_types::COPY_BUFFER_ALIGNMENT == 0 &&
316 offset >= mapping.range.start &&
317 offset + range_size <= mapping.range.end;
318 if !valid {
319 return Err(Error::Operation);
320 }
321
322 let rebased_offset = (offset - mapping.range.start) as usize;
326 mapping
327 .data
328 .view(rebased_offset..rebased_offset + range_size as usize, can_gc)
329 .map(|view| view.array_buffer())
330 .map_err(|()| Error::Operation)
331 }
332
333 fn Label(&self) -> USVString {
335 self.label.borrow().clone()
336 }
337
338 fn SetLabel(&self, value: USVString) {
340 *self.label.borrow_mut() = value;
341 }
342
343 fn Size(&self) -> GPUSize64 {
345 self.size
346 }
347
348 fn Usage(&self) -> GPUFlagsConstant {
350 self.usage
351 }
352
353 fn MapState(&self) -> GPUBufferMapState {
355 if self.mapping.borrow().is_some() {
357 GPUBufferMapState::Mapped
358 } else if self.pending_map.borrow().is_some() {
359 GPUBufferMapState::Pending
360 } else {
361 GPUBufferMapState::Unmapped
362 }
363 }
364}
365
366impl GPUBuffer {
367 fn map_failure(&self, p: &Rc<Promise>, can_gc: CanGc) {
368 let mut pending_map = self.pending_map.borrow_mut();
369 if pending_map.as_ref() != Some(p) {
371 assert!(p.is_rejected());
372 return;
373 }
374 assert!(p.is_pending());
376 pending_map.take();
378 if self.device.is_lost() {
380 p.reject_error(Error::Abort, can_gc);
381 } else {
382 p.reject_error(Error::Operation, can_gc);
383 }
384 }
385
386 fn map_success(&self, p: &Rc<Promise>, wgpu_mapping: Mapping, can_gc: CanGc) {
387 let mut pending_map = self.pending_map.borrow_mut();
388
389 if pending_map.as_ref() != Some(p) {
391 assert!(p.is_rejected());
392 return;
393 }
394
395 assert!(p.is_pending());
397
398 let mapping = ActiveBufferMapping::new(
400 match wgpu_mapping.mode {
401 HostMap::Read => GPUMapModeConstants::READ,
402 HostMap::Write => GPUMapModeConstants::WRITE,
403 },
404 wgpu_mapping.range,
405 );
406
407 match mapping {
408 Err(error) => {
409 *pending_map = None;
410 p.reject_error(error.clone(), can_gc);
411 },
412 Ok(mut mapping) => {
413 mapping.data.load(&wgpu_mapping.data);
415 self.mapping.borrow_mut().replace(mapping);
417 pending_map.take();
419 p.resolve_native(&(), can_gc);
420 },
421 }
422 }
423}
424
425impl RoutedPromiseListener<Result<Mapping, BufferAccessError>> for GPUBuffer {
426 fn handle_response(
427 &self,
428 response: Result<Mapping, BufferAccessError>,
429 promise: &Rc<Promise>,
430 can_gc: CanGc,
431 ) {
432 match response {
433 Ok(mapping) => self.map_success(promise, mapping, can_gc),
434 Err(_) => self.map_failure(promise, can_gc),
435 }
436 }
437}