1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use script_bindings::cell::DomRefCell;
9use script_bindings::reflector::{Reflector, reflect_dom_object};
10use servo_base::generic_channel::GenericSharedMemory;
11use webgpu_traits::{WebGPU, WebGPUQueue, WebGPURequest};
12
13use crate::conversions::{Convert, TryConvert};
14use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
15 GPUExtent3D, GPUQueueMethods, GPUSize64, GPUTexelCopyBufferLayout, GPUTexelCopyTextureInfo,
16};
17use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer as BufferSource;
18use crate::dom::bindings::error::{Error, Fallible};
19use crate::dom::bindings::reflector::DomGlobal;
20use crate::dom::bindings::root::{Dom, DomRoot};
21use crate::dom::bindings::str::USVString;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::promise::Promise;
24use crate::dom::webgpu::gpubuffer::GPUBuffer;
25use crate::dom::webgpu::gpucommandbuffer::GPUCommandBuffer;
26use crate::dom::webgpu::gpudevice::GPUDevice;
27use crate::routed_promise::{RoutedPromiseListener, callback_promise};
28use crate::script_runtime::CanGc;
29
30#[dom_struct]
31pub(crate) struct GPUQueue {
32 reflector_: Reflector,
33 #[ignore_malloc_size_of = "defined in webgpu"]
34 #[no_trace]
35 channel: WebGPU,
36 device: DomRefCell<Option<Dom<GPUDevice>>>,
37 label: DomRefCell<USVString>,
38 #[no_trace]
39 queue: WebGPUQueue,
40}
41
42impl GPUQueue {
43 fn new_inherited(channel: WebGPU, queue: WebGPUQueue) -> Self {
44 GPUQueue {
45 channel,
46 reflector_: Reflector::new(),
47 device: DomRefCell::new(None),
48 label: DomRefCell::new(USVString::default()),
49 queue,
50 }
51 }
52
53 pub(crate) fn new(
54 global: &GlobalScope,
55 channel: WebGPU,
56 queue: WebGPUQueue,
57 can_gc: CanGc,
58 ) -> DomRoot<Self> {
59 reflect_dom_object(
60 Box::new(GPUQueue::new_inherited(channel, queue)),
61 global,
62 can_gc,
63 )
64 }
65}
66
67impl GPUQueue {
68 pub(crate) fn set_device(&self, device: &GPUDevice) {
69 *self.device.borrow_mut() = Some(Dom::from_ref(device));
70 }
71
72 pub(crate) fn id(&self) -> WebGPUQueue {
73 self.queue
74 }
75}
76
77impl GPUQueueMethods<crate::DomTypeHolder> for GPUQueue {
78 fn Label(&self) -> USVString {
80 self.label.borrow().clone()
81 }
82
83 fn SetLabel(&self, value: USVString) {
85 *self.label.borrow_mut() = value;
86 }
87
88 fn Submit(&self, command_buffers: Vec<DomRoot<GPUCommandBuffer>>) {
90 let command_buffers = command_buffers.iter().map(|cb| cb.id().0).collect();
91 self.channel
92 .0
93 .send(WebGPURequest::Submit {
94 device_id: self.device.borrow().as_ref().unwrap().id().0,
95 queue_id: self.queue.0,
96 command_buffers,
97 })
98 .unwrap();
99 }
100
101 #[expect(unsafe_code)]
103 fn WriteBuffer(
104 &self,
105 buffer: &GPUBuffer,
106 buffer_offset: GPUSize64,
107 data: BufferSource,
108 data_offset: GPUSize64,
109 size: Option<GPUSize64>,
110 ) -> Fallible<()> {
111 let (sizeof_element, data_len): (usize, usize) = match &data {
113 BufferSource::ArrayBufferView(d) => {
114 (d.get_array_type().byte_size().unwrap_or(1), d.len())
115 },
116 BufferSource::ArrayBuffer(d) => (1, d.len()),
117 };
118 let data_size: usize = data_len / sizeof_element;
120 debug_assert_eq!(data_len % sizeof_element, 0);
121 let content_size = if let Some(s) = size {
123 s
124 } else {
125 (data_size as GPUSize64)
126 .checked_sub(data_offset)
127 .ok_or(Error::Operation(None))?
128 };
129
130 let valid = data_offset + content_size <= data_size as u64 &&
132 (content_size * sizeof_element as u64)
133 .is_multiple_of(wgpu_types::COPY_BUFFER_ALIGNMENT);
134 if !valid {
135 return Err(Error::Operation(None));
136 }
137
138 let byte_start = (data_offset as usize) * sizeof_element;
140 let byte_end = ((data_offset + content_size) as usize) * sizeof_element;
141 let contents = match &data {
142 BufferSource::ArrayBufferView(data) => {
143 GenericSharedMemory::from_bytes(unsafe { &data.as_slice()[byte_start..byte_end] })
146 },
147 BufferSource::ArrayBuffer(data) => {
148 GenericSharedMemory::from_bytes(unsafe { &data.as_slice()[byte_start..byte_end] })
151 },
152 };
153 if let Err(e) = self.channel.0.send(WebGPURequest::WriteBuffer {
154 device_id: self.device.borrow().as_ref().unwrap().id().0,
155 queue_id: self.queue.0,
156 buffer_id: buffer.id().0,
157 buffer_offset,
158 data: contents,
159 }) {
160 warn!("Failed to send WriteBuffer({:?}) ({})", buffer.id(), e);
161 return Err(Error::Operation(None));
162 }
163
164 Ok(())
165 }
166
167 fn WriteTexture(
169 &self,
170 destination: &GPUTexelCopyTextureInfo,
171 data: BufferSource,
172 data_layout: &GPUTexelCopyBufferLayout,
173 size: GPUExtent3D,
174 ) -> Fallible<()> {
175 let (bytes, len) = match data {
176 BufferSource::ArrayBufferView(d) => (d.to_vec(), d.len() as u64),
177 BufferSource::ArrayBuffer(d) => (d.to_vec(), d.len() as u64),
178 };
179 let valid = data_layout.offset <= len;
180
181 if !valid {
182 return Err(Error::Operation(None));
183 }
184
185 let texture_cv = destination.try_convert()?;
186 let texture_layout = data_layout.convert();
187 let write_size = (&size).try_convert()?;
188 let final_data = GenericSharedMemory::from_bytes(&bytes);
189
190 if let Err(e) = self.channel.0.send(WebGPURequest::WriteTexture {
191 device_id: self.device.borrow().as_ref().unwrap().id().0,
192 queue_id: self.queue.0,
193 texture_cv,
194 data_layout: texture_layout,
195 size: write_size,
196 data: final_data,
197 }) {
198 warn!(
199 "Failed to send WriteTexture({:?}) ({})",
200 destination.texture.id().0,
201 e
202 );
203 return Err(Error::Operation(None));
204 }
205
206 Ok(())
207 }
208
209 fn OnSubmittedWorkDone(&self, can_gc: CanGc) -> Rc<Promise> {
211 let global = self.global();
212 let promise = Promise::new(&global, can_gc);
213 let task_source = global.task_manager().dom_manipulation_task_source();
214 let callback = callback_promise(&promise, self, task_source);
215
216 if let Err(e) = self
217 .channel
218 .0
219 .send(WebGPURequest::QueueOnSubmittedWorkDone {
220 sender: callback,
221 queue_id: self.queue.0,
222 })
223 {
224 warn!("QueueOnSubmittedWorkDone failed with {e}")
225 }
226 promise
227 }
228}
229
230impl RoutedPromiseListener<()> for GPUQueue {
231 fn handle_response(
232 &self,
233 cx: &mut js::context::JSContext,
234 _response: (),
235 promise: &Rc<Promise>,
236 ) {
237 promise.resolve_native_with_cx(cx, &());
238 }
239}