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