1use dom_struct::dom_struct;
6use webgpu_traits::{
7 WebGPU, WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePass, WebGPUDevice,
8 WebGPURenderPass, WebGPURequest,
9};
10use wgpu_core::command as wgpu_com;
11
12use crate::conversions::{Convert, TryConvert};
13use crate::dom::bindings::cell::DomRefCell;
14use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
15 GPUCommandBufferDescriptor, GPUCommandEncoderDescriptor, GPUCommandEncoderMethods,
16 GPUComputePassDescriptor, GPUExtent3D, GPUImageCopyBuffer, GPUImageCopyTexture,
17 GPURenderPassDescriptor, GPUSize64,
18};
19use crate::dom::bindings::error::Fallible;
20use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
21use crate::dom::bindings::root::{Dom, DomRoot};
22use crate::dom::bindings::str::USVString;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::gpuconvert::convert_load_op;
25use crate::dom::webgpu::gpubuffer::GPUBuffer;
26use crate::dom::webgpu::gpucommandbuffer::GPUCommandBuffer;
27use crate::dom::webgpu::gpucomputepassencoder::GPUComputePassEncoder;
28use crate::dom::webgpu::gpudevice::GPUDevice;
29use crate::dom::webgpu::gpurenderpassencoder::GPURenderPassEncoder;
30use crate::script_runtime::CanGc;
31
32#[dom_struct]
33pub(crate) struct GPUCommandEncoder {
34 reflector_: Reflector,
35 #[no_trace]
36 channel: WebGPU,
37 label: DomRefCell<USVString>,
38 #[no_trace]
39 encoder: WebGPUCommandEncoder,
40 device: Dom<GPUDevice>,
41}
42
43impl GPUCommandEncoder {
44 pub(crate) fn new_inherited(
45 channel: WebGPU,
46 device: &GPUDevice,
47 encoder: WebGPUCommandEncoder,
48 label: USVString,
49 ) -> Self {
50 Self {
51 channel,
52 reflector_: Reflector::new(),
53 label: DomRefCell::new(label),
54 device: Dom::from_ref(device),
55 encoder,
56 }
57 }
58
59 pub(crate) fn new(
60 global: &GlobalScope,
61 channel: WebGPU,
62 device: &GPUDevice,
63 encoder: WebGPUCommandEncoder,
64 label: USVString,
65 can_gc: CanGc,
66 ) -> DomRoot<Self> {
67 reflect_dom_object(
68 Box::new(GPUCommandEncoder::new_inherited(
69 channel, device, encoder, label,
70 )),
71 global,
72 can_gc,
73 )
74 }
75}
76
77impl GPUCommandEncoder {
78 pub(crate) fn id(&self) -> WebGPUCommandEncoder {
79 self.encoder
80 }
81
82 pub(crate) fn device_id(&self) -> WebGPUDevice {
83 self.device.id()
84 }
85
86 pub(crate) fn create(
88 device: &GPUDevice,
89 descriptor: &GPUCommandEncoderDescriptor,
90 can_gc: CanGc,
91 ) -> DomRoot<GPUCommandEncoder> {
92 let command_encoder_id = device.global().wgpu_id_hub().create_command_encoder_id();
93 device
94 .channel()
95 .0
96 .send(WebGPURequest::CreateCommandEncoder {
97 device_id: device.id().0,
98 command_encoder_id,
99 desc: wgpu_types::CommandEncoderDescriptor {
100 label: (&descriptor.parent).convert(),
101 },
102 })
103 .expect("Failed to create WebGPU command encoder");
104
105 let encoder = WebGPUCommandEncoder(command_encoder_id);
106
107 GPUCommandEncoder::new(
108 &device.global(),
109 device.channel().clone(),
110 device,
111 encoder,
112 descriptor.parent.label.clone(),
113 can_gc,
114 )
115 }
116}
117
118impl GPUCommandEncoderMethods<crate::DomTypeHolder> for GPUCommandEncoder {
119 fn Label(&self) -> USVString {
121 self.label.borrow().clone()
122 }
123
124 fn SetLabel(&self, value: USVString) {
126 *self.label.borrow_mut() = value;
127 }
128
129 fn BeginComputePass(
131 &self,
132 descriptor: &GPUComputePassDescriptor,
133 ) -> DomRoot<GPUComputePassEncoder> {
134 let compute_pass_id = self.global().wgpu_id_hub().create_compute_pass_id();
135
136 if let Err(e) = self.channel.0.send(WebGPURequest::BeginComputePass {
137 command_encoder_id: self.id().0,
138 compute_pass_id,
139 label: (&descriptor.parent).convert(),
140 device_id: self.device.id().0,
141 }) {
142 warn!("Failed to send WebGPURequest::BeginComputePass {e:?}");
143 }
144
145 GPUComputePassEncoder::new(
146 &self.global(),
147 self.channel.clone(),
148 self,
149 WebGPUComputePass(compute_pass_id),
150 descriptor.parent.label.clone(),
151 CanGc::note(),
152 )
153 }
154
155 fn BeginRenderPass(
157 &self,
158 descriptor: &GPURenderPassDescriptor,
159 ) -> Fallible<DomRoot<GPURenderPassEncoder>> {
160 let depth_stencil_attachment = descriptor.depthStencilAttachment.as_ref().map(|ds| {
161 wgpu_com::RenderPassDepthStencilAttachment {
162 depth: wgpu_com::PassChannel {
163 load_op: ds
164 .depthLoadOp
165 .as_ref()
166 .map(|l| convert_load_op(l, ds.depthClearValue.map(|v| *v))),
167 store_op: ds.depthStoreOp.as_ref().map(Convert::convert),
168 read_only: ds.depthReadOnly,
169 },
170 stencil: wgpu_com::PassChannel {
171 load_op: ds
172 .stencilLoadOp
173 .as_ref()
174 .map(|l| convert_load_op(l, Some(ds.stencilClearValue))),
175 store_op: ds.stencilStoreOp.as_ref().map(Convert::convert),
176 read_only: ds.stencilReadOnly,
177 },
178 view: ds.view.id().0,
179 }
180 });
181
182 let color_attachments = descriptor
183 .colorAttachments
184 .iter()
185 .map(|color| -> Fallible<_> {
186 Ok(Some(wgpu_com::RenderPassColorAttachment {
187 resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0),
188 load_op: convert_load_op(
189 &color.loadOp,
190 color
191 .clearValue
192 .as_ref()
193 .map(|color| (color).try_convert())
194 .transpose()?
195 .unwrap_or_default(),
196 ),
197 store_op: color.storeOp.convert(),
198 view: color.view.id().0,
199 depth_slice: None,
200 }))
201 })
202 .collect::<Fallible<Vec<_>>>()?;
203 let render_pass_id = self.global().wgpu_id_hub().create_render_pass_id();
204
205 if let Err(e) = self.channel.0.send(WebGPURequest::BeginRenderPass {
206 command_encoder_id: self.id().0,
207 render_pass_id,
208 label: (&descriptor.parent).convert(),
209 depth_stencil_attachment,
210 color_attachments,
211 device_id: self.device.id().0,
212 }) {
213 warn!("Failed to send WebGPURequest::BeginRenderPass {e:?}");
214 }
215
216 Ok(GPURenderPassEncoder::new(
217 &self.global(),
218 self.channel.clone(),
219 WebGPURenderPass(render_pass_id),
220 self,
221 descriptor.parent.label.clone(),
222 CanGc::note(),
223 ))
224 }
225
226 fn CopyBufferToBuffer(
228 &self,
229 source: &GPUBuffer,
230 source_offset: GPUSize64,
231 destination: &GPUBuffer,
232 destination_offset: GPUSize64,
233 size: GPUSize64,
234 ) {
235 self.channel
236 .0
237 .send(WebGPURequest::CopyBufferToBuffer {
238 command_encoder_id: self.encoder.0,
239 source_id: source.id().0,
240 source_offset,
241 destination_id: destination.id().0,
242 destination_offset,
243 size,
244 })
245 .expect("Failed to send CopyBufferToBuffer");
246 }
247
248 fn CopyBufferToTexture(
250 &self,
251 source: &GPUImageCopyBuffer,
252 destination: &GPUImageCopyTexture,
253 copy_size: GPUExtent3D,
254 ) -> Fallible<()> {
255 self.channel
256 .0
257 .send(WebGPURequest::CopyBufferToTexture {
258 command_encoder_id: self.encoder.0,
259 source: source.convert(),
260 destination: destination.try_convert()?,
261 copy_size: (©_size).try_convert()?,
262 })
263 .expect("Failed to send CopyBufferToTexture");
264
265 Ok(())
266 }
267
268 fn CopyTextureToBuffer(
270 &self,
271 source: &GPUImageCopyTexture,
272 destination: &GPUImageCopyBuffer,
273 copy_size: GPUExtent3D,
274 ) -> Fallible<()> {
275 self.channel
276 .0
277 .send(WebGPURequest::CopyTextureToBuffer {
278 command_encoder_id: self.encoder.0,
279 source: source.try_convert()?,
280 destination: destination.convert(),
281 copy_size: (©_size).try_convert()?,
282 })
283 .expect("Failed to send CopyTextureToBuffer");
284
285 Ok(())
286 }
287
288 fn CopyTextureToTexture(
290 &self,
291 source: &GPUImageCopyTexture,
292 destination: &GPUImageCopyTexture,
293 copy_size: GPUExtent3D,
294 ) -> Fallible<()> {
295 self.channel
296 .0
297 .send(WebGPURequest::CopyTextureToTexture {
298 command_encoder_id: self.encoder.0,
299 source: source.try_convert()?,
300 destination: destination.try_convert()?,
301 copy_size: (©_size).try_convert()?,
302 })
303 .expect("Failed to send CopyTextureToTexture");
304
305 Ok(())
306 }
307
308 fn Finish(&self, descriptor: &GPUCommandBufferDescriptor) -> DomRoot<GPUCommandBuffer> {
310 self.channel
311 .0
312 .send(WebGPURequest::CommandEncoderFinish {
313 command_encoder_id: self.encoder.0,
314 device_id: self.device.id().0,
315 desc: wgpu_types::CommandBufferDescriptor {
316 label: (&descriptor.parent).convert(),
317 },
318 })
319 .expect("Failed to send Finish");
320
321 let buffer = WebGPUCommandBuffer(self.encoder.0.into_command_buffer_id());
322 GPUCommandBuffer::new(
323 &self.global(),
324 self.channel.clone(),
325 buffer,
326 descriptor.parent.label.clone(),
327 CanGc::note(),
328 )
329 }
330}