/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; use ipc_channel::ipc::IpcSharedMemory; use webgpu::identity::WebGPUOpResult; use webgpu::{wgt, WebGPU, WebGPUQueue, WebGPURequest}; use super::bindings::codegen::Bindings::WebGPUBinding::{GPUImageCopyTexture, GPUImageDataLayout}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ GPUExtent3D, GPUQueueMethods, GPUSize64, }; use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer as BufferSource; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::USVString; use crate::dom::globalscope::GlobalScope; use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState}; use crate::dom::gpucommandbuffer::GPUCommandBuffer; use crate::dom::gpucommandencoder::{convert_ic_texture, convert_image_data_layout}; use crate::dom::gpudevice::{convert_texture_size_to_dict, convert_texture_size_to_wgt, GPUDevice}; #[dom_struct] pub struct GPUQueue { reflector_: Reflector, #[ignore_malloc_size_of = "defined in webgpu"] #[no_trace] channel: WebGPU, device: DomRefCell>>, label: DomRefCell, #[no_trace] queue: WebGPUQueue, } impl GPUQueue { fn new_inherited(channel: WebGPU, queue: WebGPUQueue) -> Self { GPUQueue { channel, reflector_: Reflector::new(), device: DomRefCell::new(None), label: DomRefCell::new(USVString::default()), queue, } } pub fn new(global: &GlobalScope, channel: WebGPU, queue: WebGPUQueue) -> DomRoot { reflect_dom_object(Box::new(GPUQueue::new_inherited(channel, queue)), global) } } impl GPUQueue { pub fn set_device(&self, device: &GPUDevice) { *self.device.borrow_mut() = Some(Dom::from_ref(device)); } } impl GPUQueueMethods for GPUQueue { /// fn Label(&self) -> USVString { self.label.borrow().clone() } /// fn SetLabel(&self, value: USVString) { *self.label.borrow_mut() = value; } /// fn Submit(&self, command_buffers: Vec>) { let valid = command_buffers.iter().all(|cb| { cb.buffers().iter().all(|b| match b.state() { GPUBufferState::Unmapped => true, _ => false, }) }); let scope_id = self.device.borrow().as_ref().unwrap().use_current_scope(); if !valid { self.device.borrow().as_ref().unwrap().handle_server_msg( scope_id, WebGPUOpResult::ValidationError(String::from( "Referenced GPUBuffer(s) are not Unmapped", )), ); return; } let command_buffers = command_buffers.iter().map(|cb| cb.id().0).collect(); self.channel .0 .send(( scope_id, WebGPURequest::Submit { queue_id: self.queue.0, command_buffers, }, )) .unwrap(); } /// #[allow(unsafe_code)] fn WriteBuffer( &self, buffer: &GPUBuffer, buffer_offset: GPUSize64, data: BufferSource, data_offset: GPUSize64, size: Option, ) -> Fallible<()> { let bytes = match data { BufferSource::ArrayBufferView(d) => d.to_vec(), BufferSource::ArrayBuffer(d) => d.to_vec(), }; let content_size = if let Some(s) = size { s } else { bytes.len() as GPUSize64 - data_offset }; let valid = data_offset + content_size <= bytes.len() as u64 && buffer.state() == GPUBufferState::Unmapped && content_size % wgt::COPY_BUFFER_ALIGNMENT == 0 && buffer_offset % wgt::COPY_BUFFER_ALIGNMENT == 0; if !valid { return Err(Error::Operation); } let final_data = IpcSharedMemory::from_bytes( &bytes[data_offset as usize..(data_offset + content_size) as usize], ); if let Err(e) = self.channel.0.send(( self.device.borrow().as_ref().unwrap().use_current_scope(), WebGPURequest::WriteBuffer { queue_id: self.queue.0, buffer_id: buffer.id().0, buffer_offset, data: final_data, }, )) { warn!("Failed to send WriteBuffer({:?}) ({})", buffer.id(), e); return Err(Error::Operation); } Ok(()) } /// fn WriteTexture( &self, destination: &GPUImageCopyTexture, data: BufferSource, data_layout: &GPUImageDataLayout, size: GPUExtent3D, ) -> Fallible<()> { let (bytes, len) = match data { BufferSource::ArrayBufferView(d) => (d.to_vec(), d.len() as u64), BufferSource::ArrayBuffer(d) => (d.to_vec(), d.len() as u64), }; let valid = data_layout.offset <= len; if !valid { return Err(Error::Operation); } let texture_cv = convert_ic_texture(destination); let texture_layout = convert_image_data_layout(data_layout); let write_size = convert_texture_size_to_wgt(&convert_texture_size_to_dict(&size)); let final_data = IpcSharedMemory::from_bytes(&bytes); if let Err(e) = self.channel.0.send(( self.device.borrow().as_ref().unwrap().use_current_scope(), WebGPURequest::WriteTexture { queue_id: self.queue.0, texture_cv, data_layout: texture_layout, size: write_size, data: final_data, }, )) { warn!( "Failed to send WriteTexture({:?}) ({})", destination.texture.id().0, e ); return Err(Error::Operation); } Ok(()) } }