1use std::ops::Range;
6use std::rc::Rc;
7use std::string::String;
8
9use dom_struct::dom_struct;
10use js::typedarray::HeapArrayBuffer;
11use script_bindings::cell::DomRefCell;
12use script_bindings::reflector::{Reflector, reflect_dom_object};
13use script_bindings::trace::RootedTraceableBox;
14use servo_base::generic_channel::GenericSharedMemory;
15use webgpu_traits::{Mapping, WebGPU, WebGPUBuffer, WebGPURequest};
16use wgpu_core::device::HostMap;
17use wgpu_core::resource::BufferAccessError;
18
19use crate::conversions::Convert;
20use crate::dom::bindings::buffer_source::DataBlock;
21use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
22 GPUBufferDescriptor, GPUBufferMapState, GPUBufferMethods, GPUFlagsConstant,
23 GPUMapModeConstants, GPUMapModeFlags, GPUSize64,
24};
25use crate::dom::bindings::error::{Error, Fallible};
26use crate::dom::bindings::reflector::DomGlobal;
27use crate::dom::bindings::root::{Dom, DomRoot};
28use crate::dom::bindings::str::USVString;
29use crate::dom::globalscope::GlobalScope;
30use crate::dom::promise::Promise;
31use crate::dom::webgpu::gpudevice::GPUDevice;
32use crate::realms::InRealm;
33use crate::routed_promise::{RoutedPromiseListener, callback_promise};
34use crate::script_runtime::{CanGc, JSContext};
35
36#[derive(JSTraceable, MallocSizeOf)]
37pub(crate) struct ActiveBufferMapping {
38 pub(crate) data: DataBlock,
42 mode: GPUMapModeFlags,
44 range: Range<u64>,
46}
47
48impl ActiveBufferMapping {
49 pub(crate) fn new(mode: GPUMapModeFlags, range: Range<u64>) -> Fallible<Self> {
51 let size = range.end - range.start;
53 if size > (1 << 53) - 1 {
55 return Err(Error::Range(c"Over MAX_SAFE_INTEGER".to_owned()));
56 }
57 let size: usize = size
58 .try_into()
59 .map_err(|_| Error::Range(c"Over usize".to_owned()))?;
60 Ok(Self {
61 data: DataBlock::new_zeroed(size),
62 mode,
63 range,
64 })
65 }
66}
67
68#[dom_struct]
69pub(crate) struct GPUBuffer {
70 reflector_: Reflector,
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(),
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 }
190}
191
192impl GPUBufferMethods<crate::DomTypeHolder> for GPUBuffer {
193 fn Unmap(&self) {
195 let promise = self.pending_map.borrow_mut().take();
197 if let Some(promise) = promise {
198 promise.reject_error(Error::Abort(None), CanGc::deprecated_note());
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.is_multiple_of(wgpu_types::MAP_ALIGNMENT) &&
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, 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 cx: &mut js::context::JSContext,
436 response: Result<Mapping, BufferAccessError>,
437 promise: &Rc<Promise>,
438 ) {
439 match response {
440 Ok(mapping) => self.map_success(promise, mapping, CanGc::from_cx(cx)),
441 Err(_) => self.map_failure(promise, CanGc::from_cx(cx)),
442 }
443 }
444}