script/dom/webgpu/
gpuqueue.rs1use 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, GPUImageCopyTexture, GPUImageDataLayout, GPUQueueMethods, GPUSize64,
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 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(None))?
129 };
130
131 let valid = data_offset + content_size <= data_size as u64 &&
133 (content_size * sizeof_element as u64)
134 .is_multiple_of(wgpu_types::COPY_BUFFER_ALIGNMENT);
135 if !valid {
136 return Err(Error::Operation(None));
137 }
138
139 let contents = GenericSharedMemory::from_bytes(
141 &data[(data_offset as usize) * sizeof_element..
142 ((data_offset + content_size) as usize) * sizeof_element],
143 );
144 if let Err(e) = self.channel.0.send(WebGPURequest::WriteBuffer {
145 device_id: self.device.borrow().as_ref().unwrap().id().0,
146 queue_id: self.queue.0,
147 buffer_id: buffer.id().0,
148 buffer_offset,
149 data: contents,
150 }) {
151 warn!("Failed to send WriteBuffer({:?}) ({})", buffer.id(), e);
152 return Err(Error::Operation(None));
153 }
154
155 Ok(())
156 }
157
158 fn WriteTexture(
160 &self,
161 destination: &GPUImageCopyTexture,
162 data: BufferSource,
163 data_layout: &GPUImageDataLayout,
164 size: GPUExtent3D,
165 ) -> Fallible<()> {
166 let (bytes, len) = match data {
167 BufferSource::ArrayBufferView(d) => (d.to_vec(), d.len() as u64),
168 BufferSource::ArrayBuffer(d) => (d.to_vec(), d.len() as u64),
169 };
170 let valid = data_layout.offset <= len;
171
172 if !valid {
173 return Err(Error::Operation(None));
174 }
175
176 let texture_cv = destination.try_convert()?;
177 let texture_layout = data_layout.convert();
178 let write_size = (&size).try_convert()?;
179 let final_data = GenericSharedMemory::from_bytes(&bytes);
180
181 if let Err(e) = self.channel.0.send(WebGPURequest::WriteTexture {
182 device_id: self.device.borrow().as_ref().unwrap().id().0,
183 queue_id: self.queue.0,
184 texture_cv,
185 data_layout: texture_layout,
186 size: write_size,
187 data: final_data,
188 }) {
189 warn!(
190 "Failed to send WriteTexture({:?}) ({})",
191 destination.texture.id().0,
192 e
193 );
194 return Err(Error::Operation(None));
195 }
196
197 Ok(())
198 }
199
200 fn OnSubmittedWorkDone(&self, can_gc: CanGc) -> Rc<Promise> {
202 let global = self.global();
203 let promise = Promise::new(&global, can_gc);
204 let task_source = global.task_manager().dom_manipulation_task_source();
205 let callback = callback_promise(&promise, self, task_source);
206
207 if let Err(e) = self
208 .channel
209 .0
210 .send(WebGPURequest::QueueOnSubmittedWorkDone {
211 sender: callback,
212 queue_id: self.queue.0,
213 })
214 {
215 warn!("QueueOnSubmittedWorkDone failed with {e}")
216 }
217 promise
218 }
219}
220
221impl RoutedPromiseListener<()> for GPUQueue {
222 fn handle_response(
223 &self,
224 cx: &mut js::context::JSContext,
225 _response: (),
226 promise: &Rc<Promise>,
227 ) {
228 promise.resolve_native(&(), CanGc::from_cx(cx));
229 }
230}