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 #[allow(unsafe_code)]
102 fn WriteBuffer(
103 &self,
104 buffer: &GPUBuffer,
105 buffer_offset: GPUSize64,
106 data: BufferSource,
107 data_offset: GPUSize64,
108 size: Option<GPUSize64>,
109 ) -> Fallible<()> {
110 let sizeof_element: usize = match data {
112 BufferSource::ArrayBufferView(ref d) => d.get_array_type().byte_size().unwrap_or(1),
113 BufferSource::ArrayBuffer(_) => 1,
114 };
115 let data = match data {
116 BufferSource::ArrayBufferView(d) => d.to_vec(),
117 BufferSource::ArrayBuffer(d) => d.to_vec(),
118 };
119 let data_size: usize = data.len() / sizeof_element;
121 debug_assert_eq!(data.len() % sizeof_element, 0);
122 let content_size = if let Some(s) = size {
124 s
125 } else {
126 (data_size as GPUSize64)
127 .checked_sub(data_offset)
128 .ok_or(Error::Operation)?
129 };
130
131 let valid = data_offset + content_size <= data_size as u64 &&
133 content_size * sizeof_element as u64 % wgpu_types::COPY_BUFFER_ALIGNMENT == 0;
134 if !valid {
135 return Err(Error::Operation);
136 }
137
138 let contents = IpcSharedMemory::from_bytes(
140 &data[(data_offset as usize) * sizeof_element..
141 ((data_offset + content_size) as usize) * sizeof_element],
142 );
143 if let Err(e) = self.channel.0.send(WebGPURequest::WriteBuffer {
144 device_id: self.device.borrow().as_ref().unwrap().id().0,
145 queue_id: self.queue.0,
146 buffer_id: buffer.id().0,
147 buffer_offset,
148 data: contents,
149 }) {
150 warn!("Failed to send WriteBuffer({:?}) ({})", buffer.id(), e);
151 return Err(Error::Operation);
152 }
153
154 Ok(())
155 }
156
157 fn WriteTexture(
159 &self,
160 destination: &GPUImageCopyTexture,
161 data: BufferSource,
162 data_layout: &GPUImageDataLayout,
163 size: GPUExtent3D,
164 ) -> Fallible<()> {
165 let (bytes, len) = match data {
166 BufferSource::ArrayBufferView(d) => (d.to_vec(), d.len() as u64),
167 BufferSource::ArrayBuffer(d) => (d.to_vec(), d.len() as u64),
168 };
169 let valid = data_layout.offset <= len;
170
171 if !valid {
172 return Err(Error::Operation);
173 }
174
175 let texture_cv = destination.try_convert()?;
176 let texture_layout = data_layout.convert();
177 let write_size = (&size).try_convert()?;
178 let final_data = IpcSharedMemory::from_bytes(&bytes);
179
180 if let Err(e) = self.channel.0.send(WebGPURequest::WriteTexture {
181 device_id: self.device.borrow().as_ref().unwrap().id().0,
182 queue_id: self.queue.0,
183 texture_cv,
184 data_layout: texture_layout,
185 size: write_size,
186 data: final_data,
187 }) {
188 warn!(
189 "Failed to send WriteTexture({:?}) ({})",
190 destination.texture.id().0,
191 e
192 );
193 return Err(Error::Operation);
194 }
195
196 Ok(())
197 }
198
199 fn OnSubmittedWorkDone(&self, can_gc: CanGc) -> Rc<Promise> {
201 let global = self.global();
202 let promise = Promise::new(&global, can_gc);
203 let task_source = global.task_manager().dom_manipulation_task_source();
204 let sender = route_promise(&promise, self, task_source);
205
206 if let Err(e) = self
207 .channel
208 .0
209 .send(WebGPURequest::QueueOnSubmittedWorkDone {
210 sender,
211 queue_id: self.queue.0,
212 })
213 {
214 warn!("QueueOnSubmittedWorkDone failed with {e}")
215 }
216 promise
217 }
218}
219
220impl RoutedPromiseListener<()> for GPUQueue {
221 fn handle_response(&self, _response: (), promise: &Rc<Promise>, can_gc: CanGc) {
222 promise.resolve_native(&(), can_gc);
223 }
224}