diff --git a/Cargo.lock b/Cargo.lock index 88783797a98..bba68728820 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1865,8 +1865,7 @@ dependencies = [ [[package]] name = "gfx-descriptor" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf35f5d66d1bc56e63e68d7528441453f25992bd954b84309d23c659df2c5da" +source = "git+https://github.com/gfx-rs/gfx-extras?rev=0f7dac1b05813948fa0e5999c4fe6473f0c98f9b#0f7dac1b05813948fa0e5999c4fe6473f0c98f9b" dependencies = [ "fxhash", "gfx-hal", @@ -1886,7 +1885,7 @@ dependencies = [ [[package]] name = "gfx-memory" version = "0.1.3" -source = "git+https://github.com/gfx-rs/gfx-extras?rev=438353c3f75368c12024ad2fc03cbeb15f351fd9#438353c3f75368c12024ad2fc03cbeb15f351fd9" +source = "git+https://github.com/gfx-rs/gfx-extras?rev=0f7dac1b05813948fa0e5999c4fe6473f0c98f9b#0f7dac1b05813948fa0e5999c4fe6473f0c98f9b" dependencies = [ "fxhash", "gfx-hal", @@ -3527,7 +3526,7 @@ checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "naga" version = "0.1.0" -source = "git+https://github.com/gfx-rs/naga?rev=bce6358eb1026c13d2f1c6d365af37afe8869a86#bce6358eb1026c13d2f1c6d365af37afe8869a86" +source = "git+https://github.com/gfx-rs/naga?rev=a9228d2aed38c71388489a95817238ff98198fa3#a9228d2aed38c71388489a95817238ff98198fa3" dependencies = [ "bitflags", "fxhash", @@ -3955,16 +3954,7 @@ version = "0.2.0" source = "git+https://github.com/servo/webrender#1175acad2d4f49fa712e105c84149ac7f394261d" dependencies = [ "euclid", - "peek-poke-derive 0.2.1 (git+https://github.com/servo/webrender)", -] - -[[package]] -name = "peek-poke" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93fd6a575ebf1ac2668d08443c97a22872cfb463fd8b7ddd141e9f6be59af2f" -dependencies = [ - "peek-poke-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "peek-poke-derive", ] [[package]] @@ -3979,19 +3969,6 @@ dependencies = [ "unicode-xid 0.2.0", ] -[[package]] -name = "peek-poke-derive" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb44a25c5bba983be0fc8592dfaf3e6d0935ce8be0c6b15b2a39507af34a926" -dependencies = [ - "proc-macro2 1.0.17", - "quote 1.0.2", - "syn", - "synstructure", - "unicode-xid 0.2.0", -] - [[package]] name = "peeking_take_while" version = "0.1.2" @@ -6014,6 +5991,25 @@ dependencies = [ "serde", ] +[[package]] +name = "tracing" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41f40ed0e162c911ac6fcb53ecdc8134c46905fdbbae8c50add462a538b495f" +dependencies = [ + "cfg-if", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83a9a47081cd522c09c81b31aec2c9273424976f922ad61c053b58350b715" +dependencies = [ + "lazy_static", +] + [[package]] name = "tracy-rs" version = "0.1.1" @@ -6493,7 +6489,7 @@ dependencies = [ "derive_more", "euclid", "malloc_size_of_derive", - "peek-poke 0.2.0 (git+https://github.com/servo/webrender)", + "peek-poke", "serde", "serde_bytes", "serde_derive", @@ -6566,7 +6562,7 @@ dependencies = [ [[package]] name = "wgpu-core" version = "0.5.0" -source = "git+https://github.com/gfx-rs/wgpu#fc2dd481b2713cd0eda6ffa540faeaf7418fd051" +source = "git+https://github.com/gfx-rs/wgpu#177a0b39acde46f86fc46e3e63be16283a2da399" dependencies = [ "arrayvec 0.5.1", "bitflags", @@ -6583,11 +6579,11 @@ dependencies = [ "log", "naga", "parking_lot 0.10.2", - "peek-poke 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ron", "serde", "smallvec 1.3.0", "spirv_headers", + "tracing", "vec_map", "wgpu-types", ] @@ -6595,10 +6591,9 @@ dependencies = [ [[package]] name = "wgpu-types" version = "0.5.0" -source = "git+https://github.com/gfx-rs/wgpu#fc2dd481b2713cd0eda6ffa540faeaf7418fd051" +source = "git+https://github.com/gfx-rs/wgpu#177a0b39acde46f86fc46e3e63be16283a2da399" dependencies = [ "bitflags", - "peek-poke 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde", ] diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index 7aa8ffa4c8b..e4d4c234262 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -153,7 +153,7 @@ DOMInterfaces = { }, 'GPUBuffer': { - 'inRealms': ['MapReadAsync'], + 'inRealms': ['MapAsync'], } } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 6208a7bc1ba..9c536ff0994 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -165,10 +165,11 @@ use tendril::{StrTendril, TendrilSink}; use time::{Duration, Timespec, Tm}; use uuid::Uuid; use webgpu::{ - wgpu::command::RawPass, wgpu::id, WebGPU, WebGPUAdapter, WebGPUBindGroup, - WebGPUBindGroupLayout, WebGPUBuffer, WebGPUCommandBuffer, WebGPUCommandEncoder, - WebGPUComputePipeline, WebGPUDevice, WebGPUPipelineLayout, WebGPUQueue, WebGPURenderPipeline, - WebGPUSampler, WebGPUShaderModule, WebGPUTexture, WebGPUTextureView, + wgpu::command::{ComputePass, RenderPass}, + WebGPU, WebGPUAdapter, WebGPUBindGroup, WebGPUBindGroupLayout, WebGPUBuffer, + WebGPUCommandBuffer, WebGPUCommandEncoder, WebGPUComputePipeline, WebGPUDevice, + WebGPUPipelineLayout, WebGPUQueue, WebGPURenderPipeline, WebGPUSampler, WebGPUShaderModule, + WebGPUTexture, WebGPUTextureView, }; use webrender_api::{DocumentId, ExternalImageId, ImageKey}; use webxr_api::SwapChainId as WebXRSwapChainId; @@ -581,7 +582,8 @@ unsafe_no_jsmanaged_fields!(WebGPUContextId); unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer); unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder); unsafe_no_jsmanaged_fields!(WebGPUDevice); -unsafe_no_jsmanaged_fields!(Option>); +unsafe_no_jsmanaged_fields!(Option); +unsafe_no_jsmanaged_fields!(Option); unsafe_no_jsmanaged_fields!(GPUBufferState); unsafe_no_jsmanaged_fields!(GPUCommandEncoderState); unsafe_no_jsmanaged_fields!(WebXRSwapChainId); diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index a23a30a4b11..016f5d8bd1d 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -2,8 +2,9 @@ * 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 crate::dom::bindings::cell::{DomRefCell, Ref}; -use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::{GPUBufferMethods, GPUSize64}; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferMethods; +use crate::dom::bindings::codegen::Bindings::GPUMapModeBinding::GPUMapModeConstants; use crate::dom::bindings::error::Error; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; @@ -11,22 +12,28 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::globalscope::GlobalScope; +use crate::dom::gpu::{response_async, AsyncWGPUListener}; +use crate::dom::promise::Promise; +use crate::realms::InRealm; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; use js::jsval::UndefinedValue; use js::rust::jsapi_wrapped::{DetachArrayBuffer, IsPromiseObject, RejectPromise}; -use js::typedarray::ArrayBuffer; +use js::rust::MutableHandle; +use js::typedarray::{ArrayBuffer, CreateWith}; use std::cell::Cell; use std::ptr; -use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; +use std::rc::Rc; +use webgpu::{ + wgpu::device::HostMap, WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest, WebGPUResponse, +}; // https://gpuweb.github.io/gpuweb/#buffer-state -#[derive(Clone, MallocSizeOf)] +#[derive(Clone, Copy, MallocSizeOf, PartialEq)] pub enum GPUBufferState { - MappedForReading, - MappedForWriting, - MappedPendingForReading, - MappedPendingForWriting, + Mapped, + MappedAtCreation, + MappingPending, Unmapped, Destroyed, } @@ -37,14 +44,15 @@ pub struct GPUBuffer { #[ignore_malloc_size_of = "defined in webgpu"] channel: WebGPU, label: DomRefCell>, - size: GPUSize64, - usage: u32, - state: DomRefCell, + state: Cell, buffer: WebGPUBuffer, device: WebGPUDevice, valid: Cell, #[ignore_malloc_size_of = "defined in mozjs"] mapping: RootedTraceableBox>, + mapping_range: (u64, u64), + size: u64, + map_mode: Cell>, } impl GPUBuffer { @@ -53,22 +61,23 @@ impl GPUBuffer { buffer: WebGPUBuffer, device: WebGPUDevice, state: GPUBufferState, - size: GPUSize64, - usage: u32, + size: u64, valid: bool, mapping: RootedTraceableBox>, + mapping_range: (u64, u64), ) -> Self { Self { reflector_: Reflector::new(), channel, label: DomRefCell::new(None), - state: DomRefCell::new(state), - size: size, - usage: usage, + state: Cell::new(state), valid: Cell::new(valid), device, buffer, mapping, + size, + mapping_range, + map_mode: Cell::new(None), } } @@ -79,14 +88,21 @@ impl GPUBuffer { buffer: WebGPUBuffer, device: WebGPUDevice, state: GPUBufferState, - size: GPUSize64, - usage: u32, + size: u64, valid: bool, mapping: RootedTraceableBox>, + mapping_range: (u64, u64), ) -> DomRoot { reflect_dom_object( Box::new(GPUBuffer::new_inherited( - channel, buffer, device, state, size, usage, valid, mapping, + channel, + buffer, + device, + state, + size, + valid, + mapping, + mapping_range, )), global, ) @@ -98,16 +114,8 @@ impl GPUBuffer { self.buffer } - pub fn size(&self) -> GPUSize64 { - self.size - } - - pub fn usage(&self) -> u32 { - self.usage - } - - pub fn state(&self) -> Ref { - self.state.borrow() + pub fn state(&self) -> GPUBufferState { + self.state.get() } pub fn is_valid(&self) -> bool { @@ -127,54 +135,64 @@ impl GPUBufferMethods for GPUBuffer { fn Unmap(&self) { let cx = self.global().get_cx(); // Step 1 - match *self.state.borrow() { + match self.state.get() { GPUBufferState::Unmapped | GPUBufferState::Destroyed => { // TODO: Record validation error on the current scope return; }, - GPUBufferState::MappedForWriting => { - // Step 3.1 + // Step 3 + GPUBufferState::Mapped | GPUBufferState::MappedAtCreation => { match ArrayBuffer::from(self.mapping.get()) { Ok(array_buffer) => { - self.channel - .0 - .send(WebGPURequest::UnmapBuffer { - device_id: self.device.0, - buffer_id: self.id().0, - array_buffer: array_buffer.to_vec(), - }) - .unwrap(); // Step 3.2 + if Some(GPUMapModeConstants::READ) != self.map_mode.get() { + self.channel + .0 + .send(WebGPURequest::UnmapBuffer { + device_id: self.device.0, + buffer_id: self.id().0, + array_buffer: array_buffer.to_vec(), + mapped_at_creation: self.map_mode.get() == None, + }) + .unwrap(); + } + // Step 3.3 unsafe { DetachArrayBuffer(*cx, self.mapping.handle()); } }, - _ => { - // Step 2 - unsafe { - if IsPromiseObject(self.mapping.handle()) { - let err = Error::Abort; - rooted!(in(*cx) let mut undef = UndefinedValue()); - err.to_jsval(*cx, &self.global(), undef.handle_mut()); - RejectPromise(*cx, self.mapping.handle(), undef.handle()); - }; - } + Err(_) => { + warn!( + "Could not find ArrayBuffer of Mapped buffer ({:?})", + self.buffer.0 + ); }, }; }, - _ => {}, + // Step 2 + GPUBufferState::MappingPending => unsafe { + if IsPromiseObject(self.mapping.handle()) { + let err = Error::Operation; + rooted!(in(*cx) let mut undef = UndefinedValue()); + err.to_jsval(*cx, &self.global(), undef.handle_mut()); + RejectPromise(*cx, self.mapping.handle(), undef.handle()); + } else { + warn!("No promise object for pending mapping found"); + } + }, }; // Step 3.3 self.mapping.set(ptr::null_mut()); // Step 4 - *self.state.borrow_mut() = GPUBufferState::Unmapped; + self.state.set(GPUBufferState::Unmapped); + self.map_mode.set(None); } /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy fn Destroy(&self) { - let state = self.state.borrow().clone(); + let state = self.state.get(); match state { - GPUBufferState::MappedForReading | GPUBufferState::MappedForWriting => { + GPUBufferState::Mapped | GPUBufferState::MappedAtCreation => { self.Unmap(); }, _ => {}, @@ -189,7 +207,56 @@ impl GPUBufferMethods for GPUBuffer { self.buffer.0, e ); }; - *self.state.borrow_mut() = GPUBufferState::Destroyed; + self.state.set(GPUBufferState::Destroyed); + } + + #[allow(unsafe_code)] + /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapasync-offset-size + fn MapAsync(&self, mode: u32, offset: u64, size: u64, comp: InRealm) -> Rc { + let promise = Promise::new_in_current_realm(&self.global(), comp); + let map_range = if size == 0 { + offset..self.size + } else { + offset..size + }; + let host_map = match mode { + GPUMapModeConstants::READ => { + self.map_mode.set(Some(mode)); + HostMap::Read + }, + GPUMapModeConstants::WRITE => { + self.map_mode.set(Some(mode)); + HostMap::Write + }, + _ => { + promise.reject_error(Error::Abort); + return promise; + }, + }; + if self.state.get() != GPUBufferState::Unmapped { + promise.reject_error(Error::Abort); + return promise; + } + self.mapping.set(*promise.promise_obj()); + self.state.set(GPUBufferState::MappingPending); + + let sender = response_async(&promise, self); + + if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync { + sender, + buffer_id: self.buffer.0, + host_map, + map_range, + }) { + warn!( + "Failed to send BufferMapAsync ({:?}) ({})", + self.buffer.0, e + ); + promise.reject_error(Error::Operation); + return promise; + } + + promise } /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label @@ -202,3 +269,40 @@ impl GPUBufferMethods for GPUBuffer { *self.label.borrow_mut() = value; } } + +impl AsyncWGPUListener for GPUBuffer { + #[allow(unsafe_code)] + fn handle_response(&self, response: WebGPUResponse, promise: &Rc) { + match response { + WebGPUResponse::BufferMapAsync(bytes) => { + if unsafe { + ArrayBuffer::create( + *self.global().get_cx(), + CreateWith::Slice(&bytes), + MutableHandle::from_raw(self.mapping.handle_mut()), + ) + } + .is_err() + { + promise.reject_error(Error::Operation); + } + self.state.set(GPUBufferState::Mapped); + promise.resolve_native(&()); + }, + _ => { + warn!("Wrong WebGPUResponse received"); + promise.reject_error(Error::Operation); + }, + } + if let Err(e) = self + .channel + .0 + .send(WebGPURequest::BufferMapComplete(self.buffer.0)) + { + warn!( + "Failed to send BufferMapComplete({:?}) ({})", + self.buffer.0, e + ); + } + } +} diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs index 0615492dec6..4f41daccfa2 100644 --- a/components/script/dom/gpucommandencoder.rs +++ b/components/script/dom/gpucommandencoder.rs @@ -24,13 +24,10 @@ use dom_struct::dom_struct; use std::cell::Cell; use std::collections::HashSet; use webgpu::wgpu::command::{ - RawPass, RenderPassColorAttachmentDescriptor, RenderPassDepthStencilAttachmentDescriptor, - RenderPassDescriptor, + ColorAttachmentDescriptor, DepthStencilAttachmentDescriptor, RenderPass, RenderPassDescriptor, }; use webgpu::{self, wgt, WebGPU, WebGPUDevice, WebGPURequest}; -const BUFFER_COPY_ALIGN_MASK: u64 = 3; - // https://gpuweb.github.io/gpuweb/#enumdef-encoder-state #[derive(MallocSizeOf, PartialEq)] pub enum GPUCommandEncoderState { @@ -126,7 +123,6 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { GPUComputePassEncoder::new(&self.global(), self.channel.clone(), &self) } - #[allow(unsafe_code)] /// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-beginrenderpass fn BeginRenderPass( &self, @@ -141,7 +137,7 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { .colorAttachments .iter() .map(|color| { - let (load_op, clear_color) = match color.loadValue { + let (load_op, clear_value) = match color.loadValue { GPUColorLoad::GPULoadOp(_) => (wgt::LoadOp::Load, wgt::Color::TRANSPARENT), GPUColorLoad::DoubleSequence(ref s) => ( wgt::LoadOp::Clear, @@ -162,15 +158,19 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { }, ), }; - RenderPassColorAttachmentDescriptor { - attachment: color.attachment.id().0, - resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0), + let channel = wgt::PassChannel { load_op, store_op: match color.storeOp { GPUStoreOp::Store => wgt::StoreOp::Store, GPUStoreOp::Clear => wgt::StoreOp::Clear, }, - clear_color, + clear_value, + read_only: false, + }; + ColorAttachmentDescriptor { + attachment: color.attachment.id().0, + resolve_target: color.resolveTarget.as_ref().map(|t| t.id().0), + channel, } }) .collect::>(); @@ -184,34 +184,39 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { GPUStencilLoadValue::GPULoadOp(_) => (wgt::LoadOp::Load, 0u32), GPUStencilLoadValue::RangeEnforcedUnsignedLong(l) => (wgt::LoadOp::Clear, l), }; - RenderPassDepthStencilAttachmentDescriptor { + let depth_channel = wgt::PassChannel { + load_op: depth_load_op, + store_op: match depth.depthStoreOp { + GPUStoreOp::Store => wgt::StoreOp::Store, + GPUStoreOp::Clear => wgt::StoreOp::Clear, + }, + clear_value: clear_depth, + read_only: depth.depthReadOnly, + }; + let stencil_channel = wgt::PassChannel { + load_op: stencil_load_op, + store_op: match depth.stencilStoreOp { + GPUStoreOp::Store => wgt::StoreOp::Store, + GPUStoreOp::Clear => wgt::StoreOp::Clear, + }, + clear_value: clear_stencil, + read_only: depth.stencilReadOnly, + }; + DepthStencilAttachmentDescriptor { attachment: depth.attachment.id().0, - depth_load_op, - depth_store_op: match depth.depthStoreOp { - GPUStoreOp::Store => wgt::StoreOp::Store, - GPUStoreOp::Clear => wgt::StoreOp::Clear, - }, - clear_depth, - depth_read_only: depth.depthReadOnly, - stencil_load_op, - stencil_store_op: match depth.stencilStoreOp { - GPUStoreOp::Store => wgt::StoreOp::Store, - GPUStoreOp::Clear => wgt::StoreOp::Clear, - }, - clear_stencil, - stencil_read_only: depth.stencilReadOnly, + depth: depth_channel, + stencil: stencil_channel, } }); let desc = RenderPassDescriptor { - color_attachments: colors.as_ptr(), - color_attachments_length: colors.len(), + color_attachments: colors.as_slice(), depth_stencil_attachment: depth_stencil.as_ref(), }; - let raw_pass = unsafe { RawPass::new_render(self.id().0, &desc) }; + let render_pass = RenderPass::new(self.encoder.0, desc); - GPURenderPassEncoder::new(&self.global(), self.channel.clone(), raw_pass, &self) + GPURenderPassEncoder::new(&self.global(), self.channel.clone(), render_pass, &self) } /// https://gpuweb.github.io/gpuweb/#dom-gpucommandencoder-copybuffertobuffer @@ -223,36 +228,9 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { destination_offset: GPUSize64, size: GPUSize64, ) { - let mut valid = match source_offset.checked_add(size) { - Some(_) => true, - None => false, - }; - valid &= match destination_offset.checked_add(size) { - Some(_) => true, - None => false, - }; - valid &= match wgt::BufferUsage::from_bits(source.usage()) { - Some(usage) => usage.contains(wgt::BufferUsage::COPY_SRC), - None => false, - }; - valid &= match wgt::BufferUsage::from_bits(destination.usage()) { - Some(usage) => usage.contains(wgt::BufferUsage::COPY_DST), - None => false, - }; - valid &= (*self.state.borrow() == GPUCommandEncoderState::Open) && - source.is_valid() && - destination.is_valid() & - !(size & BUFFER_COPY_ALIGN_MASK == 0) & - !(source_offset & BUFFER_COPY_ALIGN_MASK == 0) & - !(destination_offset & BUFFER_COPY_ALIGN_MASK == 0) & - (source.size() >= source_offset + size) & - (destination.size() >= destination_offset + size); - - if source.id().0 == destination.id().0 { - //TODO: maybe forbid this case based on https://github.com/gpuweb/gpuweb/issues/783 - valid &= source_offset > destination_offset + size || - source_offset + size < destination_offset; - } + let valid = source.is_valid() && + destination.is_valid() && + *self.state.borrow() == GPUCommandEncoderState::Open; if !valid { // TODO: Record an error in the current scope. diff --git a/components/script/dom/gpucomputepassencoder.rs b/components/script/dom/gpucomputepassencoder.rs index 68fb641bd25..ac2a5b0dc90 100644 --- a/components/script/dom/gpucomputepassencoder.rs +++ b/components/script/dom/gpucomputepassencoder.rs @@ -2,8 +2,6 @@ * 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/. */ -#![allow(unsafe_code)] - use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::GPUComputePassEncoderBinding::GPUComputePassEncoderMethods; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; @@ -11,18 +9,12 @@ use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; use crate::dom::gpubindgroup::GPUBindGroup; +use crate::dom::gpubuffer::GPUBuffer; use crate::dom::gpucommandencoder::{GPUCommandEncoder, GPUCommandEncoderState}; use crate::dom::gpucomputepipeline::GPUComputePipeline; use dom_struct::dom_struct; use webgpu::{ - wgpu::command::{ - compute_ffi::{ - wgpu_compute_pass_dispatch, wgpu_compute_pass_set_bind_group, - wgpu_compute_pass_set_pipeline, - }, - RawPass, - }, - wgpu::id, + wgpu::command::{compute_ffi as wgpu_comp, ComputePass}, WebGPU, WebGPURequest, }; @@ -33,7 +25,7 @@ pub struct GPUComputePassEncoder { channel: WebGPU, label: DomRefCell>, #[ignore_malloc_size_of = "defined in wgpu-core"] - raw_pass: DomRefCell>>, + compute_pass: DomRefCell>, command_encoder: Dom, } @@ -43,7 +35,7 @@ impl GPUComputePassEncoder { channel, reflector_: Reflector::new(), label: DomRefCell::new(None), - raw_pass: DomRefCell::new(Some(unsafe { RawPass::new_compute(parent.id().0) })), + compute_pass: DomRefCell::new(Some(ComputePass::new(parent.id().0))), command_encoder: Dom::from_ref(parent), } } @@ -69,21 +61,30 @@ impl GPUComputePassEncoderMethods for GPUComputePassEncoder { /// https://gpuweb.github.io/gpuweb/#dom-gpucomputepassencoder-dispatch fn Dispatch(&self, x: u32, y: u32, z: u32) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { wgpu_compute_pass_dispatch(raw_pass, x, y, z) }; + if let Some(compute_pass) = self.compute_pass.borrow_mut().as_mut() { + wgpu_comp::wgpu_compute_pass_dispatch(compute_pass, x, y, z); + } + } + + /// https://gpuweb.github.io/gpuweb/#dom-gpucomputepassencoder-dispatchindirect + fn DispatchIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) { + if let Some(compute_pass) = self.compute_pass.borrow_mut().as_mut() { + wgpu_comp::wgpu_compute_pass_dispatch_indirect( + compute_pass, + indirect_buffer.id().0, + indirect_offset, + ); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-endpass fn EndPass(&self) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().take() { - let (pass_data, command_encoder_id) = unsafe { raw_pass.finish_compute() }; - + if let Some(compute_pass) = self.compute_pass.borrow_mut().take() { self.channel .0 .send(WebGPURequest::RunComputePass { - command_encoder_id, - pass_data, + command_encoder_id: self.command_encoder.id().0, + compute_pass, }) .unwrap(); @@ -95,11 +96,12 @@ impl GPUComputePassEncoderMethods for GPUComputePassEncoder { } /// https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup + #[allow(unsafe_code)] fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup, dynamic_offsets: Vec) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { + if let Some(compute_pass) = self.compute_pass.borrow_mut().as_mut() { unsafe { - wgpu_compute_pass_set_bind_group( - raw_pass, + wgpu_comp::wgpu_compute_pass_set_bind_group( + compute_pass, index, bind_group.id().0, dynamic_offsets.as_ptr(), @@ -111,8 +113,8 @@ impl GPUComputePassEncoderMethods for GPUComputePassEncoder { /// https://gpuweb.github.io/gpuweb/#dom-gpucomputepassencoder-setpipeline fn SetPipeline(&self, pipeline: &GPUComputePipeline) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { wgpu_compute_pass_set_pipeline(raw_pass, pipeline.id().0) }; + if let Some(compute_pass) = self.compute_pass.borrow_mut().as_mut() { + wgpu_comp::wgpu_compute_pass_set_pipeline(compute_pass, pipeline.id().0); } } } diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index b67c6e696e3..f522cd17d33 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -54,10 +54,9 @@ use crate::script_runtime::JSContext as SafeJSContext; use arrayvec::ArrayVec; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; -use js::jsval::{JSVal, ObjectValue}; +use js::rust::MutableHandle; use js::typedarray::{ArrayBuffer, CreateWith}; -use std::num::NonZeroU64; -use std::ptr::{self, NonNull}; +use std::ptr::NonNull; use webgpu::wgpu::binding_model::BufferBinding; use webgpu::{self, wgt, WebGPU, WebGPUBindings, WebGPURequest}; @@ -120,38 +119,6 @@ impl GPUDevice { pub fn id(&self) -> webgpu::WebGPUDevice { self.device } - - fn validate_buffer_descriptor( - &self, - descriptor: &GPUBufferDescriptor, - mapped_at_creation: bool, - ) -> (bool, wgt::BufferDescriptor) { - // TODO: Record a validation error in the current scope if the descriptor is invalid. - let wgpu_usage = wgt::BufferUsage::from_bits(descriptor.usage); - let valid = wgpu_usage.is_some() && descriptor.size > 0; - - if valid { - ( - true, - wgt::BufferDescriptor { - size: descriptor.size, - usage: wgpu_usage.unwrap(), - mapped_at_creation, - label: Default::default(), - }, - ) - } else { - ( - false, - wgt::BufferDescriptor { - size: 0, - usage: wgt::BufferUsage::empty(), - mapped_at_creation, - label: Default::default(), - }, - ) - } - } } impl GPUDeviceMethods for GPUDevice { @@ -187,7 +154,15 @@ impl GPUDeviceMethods for GPUDevice { /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> DomRoot { - let (valid, wgpu_descriptor) = self.validate_buffer_descriptor(descriptor, false); + let wgpu_descriptor = wgt::BufferDescriptor { + label: Default::default(), + size: descriptor.size, + usage: match wgt::BufferUsage::from_bits(descriptor.usage) { + Some(u) => u, + None => wgt::BufferUsage::empty(), + }, + mapped_at_creation: descriptor.mappedAtCreation, + }; let id = self .global() .wgpu_id_hub() @@ -203,70 +178,38 @@ impl GPUDeviceMethods for GPUDevice { .expect("Failed to create WebGPU buffer"); let buffer = webgpu::WebGPUBuffer(id); + let mapping = RootedTraceableBox::new(Heap::default()); + let state; + let mapping_range; + if descriptor.mappedAtCreation { + unsafe { + assert!(ArrayBuffer::create( + *self.global().get_cx(), + CreateWith::Length(descriptor.size as u32), + MutableHandle::from_raw(mapping.handle_mut()), + ) + .is_ok()); + } + state = GPUBufferState::MappedAtCreation; + mapping_range = (0, descriptor.size); + } else { + state = GPUBufferState::Unmapped; + mapping_range = (0, 0); + } GPUBuffer::new( &self.global(), self.channel.clone(), buffer, self.device, - GPUBufferState::Unmapped, + state, descriptor.size, - descriptor.usage, - valid, - RootedTraceableBox::new(Heap::default()), + true, + mapping, + mapping_range, ) } - /// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffermapped - fn CreateBufferMapped( - &self, - cx: SafeJSContext, - descriptor: &GPUBufferDescriptor, - ) -> Vec { - let (valid, wgpu_descriptor) = self.validate_buffer_descriptor(descriptor, true); - let buffer_id = self - .global() - .wgpu_id_hub() - .lock() - .create_buffer_id(self.device.0.backend()); - self.channel - .0 - .send(WebGPURequest::CreateBuffer { - device_id: self.device.0, - buffer_id, - descriptor: wgpu_descriptor.clone(), - }) - .expect("Failed to create WebGPU buffer"); - - rooted!(in(*cx) let mut js_array_buffer = ptr::null_mut::()); - unsafe { - assert!(ArrayBuffer::create( - *cx, - CreateWith::Length(descriptor.size as u32), - js_array_buffer.handle_mut(), - ) - .is_ok()); - } - - let buffer = webgpu::WebGPUBuffer(buffer_id); - let buff = GPUBuffer::new( - &self.global(), - self.channel.clone(), - buffer, - self.device, - GPUBufferState::MappedForWriting, - wgpu_descriptor.size, - wgpu_descriptor.usage.bits(), - valid, - RootedTraceableBox::from_box(Heap::boxed(js_array_buffer.get())), - ); - - vec![ - ObjectValue(buff.reflector().get_jsobject().get()), - ObjectValue(js_array_buffer.get()), - ] - } - /// https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout #[allow(non_snake_case)] fn CreateBindGroupLayout( @@ -284,28 +227,16 @@ impl GPUDeviceMethods for GPUDevice { let ty = match bind.type_ { GPUBindingType::Uniform_buffer => wgt::BindingType::UniformBuffer { dynamic: bind.hasDynamicOffset, - min_binding_size: if bind.minBufferBindingSize == 0 { - None - } else { - Some(NonZeroU64::new(bind.minBufferBindingSize).unwrap()) - }, + min_binding_size: wgt::BufferSize::new(bind.minBufferBindingSize), }, GPUBindingType::Storage_buffer => wgt::BindingType::StorageBuffer { dynamic: bind.hasDynamicOffset, - min_binding_size: if bind.minBufferBindingSize == 0 { - None - } else { - Some(NonZeroU64::new(bind.minBufferBindingSize).unwrap()) - }, + min_binding_size: wgt::BufferSize::new(bind.minBufferBindingSize), readonly: false, }, GPUBindingType::Readonly_storage_buffer => wgt::BindingType::StorageBuffer { dynamic: bind.hasDynamicOffset, - min_binding_size: if bind.minBufferBindingSize == 0 { - None - } else { - Some(NonZeroU64::new(bind.minBufferBindingSize).unwrap()) - }, + min_binding_size: wgt::BufferSize::new(bind.minBufferBindingSize), readonly: true, }, GPUBindingType::Sampled_texture => wgt::BindingType::SampledTexture { @@ -433,11 +364,7 @@ impl GPUDeviceMethods for GPUDevice { WebGPUBindings::Buffer(BufferBinding { buffer_id: b.buffer.id().0, offset: b.offset, - size: if let Some(s) = b.size { - wgt::BufferSize(s) - } else { - wgt::BufferSize::WHOLE - }, + size: wgt::BufferSize::new(b.size.unwrap_or(0)), }) }, }, diff --git a/components/script/dom/gpumapmode.rs b/components/script/dom/gpumapmode.rs new file mode 100644 index 00000000000..b216d3f839d --- /dev/null +++ b/components/script/dom/gpumapmode.rs @@ -0,0 +1,11 @@ +/* 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 crate::dom::bindings::reflector::Reflector; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct GPUMapMode { + reflector_: Reflector, +} diff --git a/components/script/dom/gpuqueue.rs b/components/script/dom/gpuqueue.rs index 5b533a8d32c..fa79dc6fd44 100644 --- a/components/script/dom/gpuqueue.rs +++ b/components/script/dom/gpuqueue.rs @@ -51,7 +51,7 @@ impl GPUQueueMethods for GPUQueue { /// https://gpuweb.github.io/gpuweb/#dom-gpuqueue-submit fn Submit(&self, command_buffers: Vec>) { let valid = command_buffers.iter().all(|cb| { - cb.buffers().iter().all(|b| match *b.state() { + cb.buffers().iter().all(|b| match b.state() { GPUBufferState::Unmapped => true, _ => false, }) diff --git a/components/script/dom/gpurenderpassencoder.rs b/components/script/dom/gpurenderpassencoder.rs index a2d223b6f5d..cf25734eeda 100644 --- a/components/script/dom/gpurenderpassencoder.rs +++ b/components/script/dom/gpurenderpassencoder.rs @@ -2,8 +2,6 @@ * 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/. */ -#![allow(unsafe_code)] - use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::GPUCommandEncoderBinding::GPUColor; use crate::dom::bindings::codegen::Bindings::GPURenderPassEncoderBinding::GPURenderPassEncoderMethods; @@ -18,8 +16,7 @@ use crate::dom::gpucommandencoder::{GPUCommandEncoder, GPUCommandEncoderState}; use crate::dom::gpurenderpipeline::GPURenderPipeline; use dom_struct::dom_struct; use webgpu::{ - wgpu::command::{render_ffi as wgpu_render, RawPass}, - wgpu::id, + wgpu::command::{render_ffi as wgpu_render, RenderPass}, wgt, WebGPU, WebGPURequest, }; @@ -30,21 +27,17 @@ pub struct GPURenderPassEncoder { channel: WebGPU, label: DomRefCell>, #[ignore_malloc_size_of = "defined in wgpu-core"] - raw_pass: DomRefCell>>, + render_pass: DomRefCell>, command_encoder: Dom, } impl GPURenderPassEncoder { - fn new_inherited( - channel: WebGPU, - raw_pass: RawPass, - parent: &GPUCommandEncoder, - ) -> Self { + fn new_inherited(channel: WebGPU, render_pass: RenderPass, parent: &GPUCommandEncoder) -> Self { Self { channel, reflector_: Reflector::new(), label: DomRefCell::new(None), - raw_pass: DomRefCell::new(Some(raw_pass)), + render_pass: DomRefCell::new(Some(render_pass)), command_encoder: Dom::from_ref(parent), } } @@ -52,12 +45,14 @@ impl GPURenderPassEncoder { pub fn new( global: &GlobalScope, channel: WebGPU, - raw_pass: RawPass, + render_pass: RenderPass, parent: &GPUCommandEncoder, ) -> DomRoot { reflect_dom_object( Box::new(GPURenderPassEncoder::new_inherited( - channel, raw_pass, parent, + channel, + render_pass, + parent, )), global, ) @@ -76,11 +71,12 @@ impl GPURenderPassEncoderMethods for GPURenderPassEncoder { } /// https://gpuweb.github.io/gpuweb/#dom-gpuprogrammablepassencoder-setbindgroup + #[allow(unsafe_code)] fn SetBindGroup(&self, index: u32, bind_group: &GPUBindGroup, dynamic_offsets: Vec) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { unsafe { wgpu_render::wgpu_render_pass_set_bind_group( - raw_pass, + render_pass, index, bind_group.id().0, dynamic_offsets.as_ptr(), @@ -100,24 +96,23 @@ impl GPURenderPassEncoderMethods for GPURenderPassEncoder { min_depth: Finite, max_depth: Finite, ) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_set_viewport( - raw_pass, *x, *y, *width, *height, *min_depth, *max_depth, - ) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_set_viewport( + render_pass, + *x, + *y, + *width, + *height, + *min_depth, + *max_depth, + ); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setscissorrect fn SetScissorRect(&self, x: u32, y: u32, width: u32, height: u32) { - if width <= 0 || height <= 0 { - return warn!("Cannot set scissor rect- width and height must greater than 0"); - } - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_set_scissor_rect(raw_pass, x, y, width, height) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_set_scissor_rect(render_pass, x, y, width, height); } } @@ -137,28 +132,26 @@ impl GPURenderPassEncoderMethods for GPURenderPassEncoder { a: *s[3], }, }; - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { wgpu_render::wgpu_render_pass_set_blend_color(raw_pass, &colors) }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_set_blend_color(render_pass, &colors); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-setstencilreference fn SetStencilReference(&self, reference: u32) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { wgpu_render::wgpu_render_pass_set_stencil_reference(raw_pass, reference) }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_set_stencil_reference(render_pass, reference); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-endpass fn EndPass(&self) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().take() { - let (pass_data, command_encoder_id) = unsafe { raw_pass.finish_render() }; - + if let Some(render_pass) = self.render_pass.borrow_mut().take() { self.channel .0 .send(WebGPURequest::RunRenderPass { - command_encoder_id, - pass_data, + command_encoder_id: self.command_encoder.id().0, + render_pass, }) .unwrap(); @@ -171,59 +164,46 @@ impl GPURenderPassEncoderMethods for GPURenderPassEncoder { /// https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setpipeline fn SetPipeline(&self, pipeline: &GPURenderPipeline) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { wgpu_render::wgpu_render_pass_set_pipeline(raw_pass, pipeline.id().0) }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_set_pipeline(render_pass, pipeline.id().0); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setindexbuffer fn SetIndexBuffer(&self, buffer: &GPUBuffer, offset: u64, size: u64) { - let s = if size == 0 { - wgt::BufferSize::WHOLE - } else { - wgt::BufferSize(size) - }; - - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_set_index_buffer(raw_pass, buffer.id().0, offset, s) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_set_index_buffer( + render_pass, + buffer.id().0, + offset, + wgt::BufferSize::new(size), + ); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-setvertexbuffer fn SetVertexBuffer(&self, slot: u32, buffer: &GPUBuffer, offset: u64, size: u64) { - let s = if size == 0 { - wgt::BufferSize::WHOLE - } else { - wgt::BufferSize(size) - }; - - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_set_vertex_buffer( - raw_pass, - slot, - buffer.id().0, - offset, - s, - ) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_set_vertex_buffer( + render_pass, + slot, + buffer.id().0, + offset, + wgt::BufferSize::new(size), + ); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-draw fn Draw(&self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_draw( - raw_pass, - vertex_count, - instance_count, - first_vertex, - first_instance, - ) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_draw( + render_pass, + vertex_count, + instance_count, + first_vertex, + first_instance, + ); } } @@ -236,43 +216,37 @@ impl GPURenderPassEncoderMethods for GPURenderPassEncoder { base_vertex: i32, first_instance: u32, ) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_draw_indexed( - raw_pass, - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_draw_indexed( + render_pass, + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindirect fn DrawIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_draw_indirect( - raw_pass, - indirect_buffer.id().0, - indirect_offset, - ) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_draw_indirect( + render_pass, + indirect_buffer.id().0, + indirect_offset, + ); } } /// https://gpuweb.github.io/gpuweb/#dom-gpurenderencoderbase-drawindexedindirect fn DrawIndexedIndirect(&self, indirect_buffer: &GPUBuffer, indirect_offset: u64) { - if let Some(raw_pass) = self.raw_pass.borrow_mut().as_mut() { - unsafe { - wgpu_render::wgpu_render_pass_draw_indexed_indirect( - raw_pass, - indirect_buffer.id().0, - indirect_offset, - ) - }; + if let Some(render_pass) = self.render_pass.borrow_mut().as_mut() { + wgpu_render::wgpu_render_pass_draw_indexed_indirect( + render_pass, + indirect_buffer.id().0, + indirect_offset, + ); } } } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 2ee17f3e74b..0265c6b9bcd 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -332,6 +332,7 @@ pub mod gpucommandencoder; pub mod gpucomputepassencoder; pub mod gpucomputepipeline; pub mod gpudevice; +pub mod gpumapmode; pub mod gpupipelinelayout; pub mod gpuqueue; pub mod gpurenderpassencoder; diff --git a/components/script/dom/webidls/GPUBuffer.webidl b/components/script/dom/webidls/GPUBuffer.webidl index 4896caf46c1..f3e9e29a577 100644 --- a/components/script/dom/webidls/GPUBuffer.webidl +++ b/components/script/dom/webidls/GPUBuffer.webidl @@ -5,8 +5,8 @@ // https://gpuweb.github.io/gpuweb/#gpubuffer [Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] interface GPUBuffer { - //Promise mapReadAsync(); - // Promise mapWriteAsync(); + Promise mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size = 0); + //ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size = 0); void unmap(); void destroy(); @@ -16,10 +16,9 @@ GPUBuffer includes GPUObjectBase; dictionary GPUBufferDescriptor : GPUObjectDescriptorBase { required GPUSize64 size; required GPUBufferUsageFlags usage; + boolean mappedAtCreation = false; }; typedef unsigned long long GPUSize64; -typedef unsigned long GPUBufferUsageFlags; - typedef sequence GPUMappedBuffer; diff --git a/components/script/dom/webidls/GPUBufferUsage.webidl b/components/script/dom/webidls/GPUBufferUsage.webidl index 549f6652d1f..6db1d029f18 100644 --- a/components/script/dom/webidls/GPUBufferUsage.webidl +++ b/components/script/dom/webidls/GPUBufferUsage.webidl @@ -5,13 +5,16 @@ // https://gpuweb.github.io/gpuweb/#buffer-usage [Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"] interface GPUBufferUsage { - const GPUBufferUsageFlags MAP_READ = 0x0001; - const GPUBufferUsageFlags MAP_WRITE = 0x0002; - const GPUBufferUsageFlags COPY_SRC = 0x0004; - const GPUBufferUsageFlags COPY_DST = 0x0008; - const GPUBufferUsageFlags INDEX = 0x0010; - const GPUBufferUsageFlags VERTEX = 0x0020; - const GPUBufferUsageFlags UNIFORM = 0x0040; - const GPUBufferUsageFlags STORAGE = 0x0080; - const GPUBufferUsageFlags INDIRECT = 0x0100; + const GPUBufferUsageFlags MAP_READ = 0x0001; + const GPUBufferUsageFlags MAP_WRITE = 0x0002; + const GPUBufferUsageFlags COPY_SRC = 0x0004; + const GPUBufferUsageFlags COPY_DST = 0x0008; + const GPUBufferUsageFlags INDEX = 0x0010; + const GPUBufferUsageFlags VERTEX = 0x0020; + const GPUBufferUsageFlags UNIFORM = 0x0040; + const GPUBufferUsageFlags STORAGE = 0x0080; + const GPUBufferUsageFlags INDIRECT = 0x0100; + const GPUBufferUsageFlags QUERY_RESOLVE = 0x0200; }; + +typedef [EnforceRange] unsigned long GPUBufferUsageFlags; diff --git a/components/script/dom/webidls/GPUComputePassEncoder.webidl b/components/script/dom/webidls/GPUComputePassEncoder.webidl index 0b19cf98134..2aecf685f2e 100644 --- a/components/script/dom/webidls/GPUComputePassEncoder.webidl +++ b/components/script/dom/webidls/GPUComputePassEncoder.webidl @@ -7,7 +7,7 @@ interface GPUComputePassEncoder { void setPipeline(GPUComputePipeline pipeline); void dispatch(GPUSize32 x, optional GPUSize32 y = 1, optional GPUSize32 z = 1); - // void dispatchIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); + void dispatchIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); void endPass(); }; diff --git a/components/script/dom/webidls/GPUDevice.webidl b/components/script/dom/webidls/GPUDevice.webidl index 738c8f076fd..24ee59b54a9 100644 --- a/components/script/dom/webidls/GPUDevice.webidl +++ b/components/script/dom/webidls/GPUDevice.webidl @@ -12,7 +12,6 @@ interface GPUDevice : EventTarget { [SameObject] readonly attribute GPUQueue defaultQueue; GPUBuffer createBuffer(GPUBufferDescriptor descriptor); - GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor); GPUTexture createTexture(GPUTextureDescriptor descriptor); GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {}); diff --git a/components/script/dom/webidls/GPUMapMode.webidl b/components/script/dom/webidls/GPUMapMode.webidl new file mode 100644 index 00000000000..0dc52c4b448 --- /dev/null +++ b/components/script/dom/webidls/GPUMapMode.webidl @@ -0,0 +1,12 @@ +/* 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/. */ + +// https://gpuweb.github.io/gpuweb/#gpumapmode +[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"] +interface GPUMapMode { + const GPUMapModeFlags READ = 0x0001; + const GPUMapModeFlags WRITE = 0x0002; +}; + +typedef [EnforceRange] unsigned long GPUMapModeFlags; diff --git a/components/webgpu/Cargo.toml b/components/webgpu/Cargo.toml index 8654f67c1fc..3f04af88a3f 100644 --- a/components/webgpu/Cargo.toml +++ b/components/webgpu/Cargo.toml @@ -21,5 +21,5 @@ servo_config = { path = "../config" } smallvec = { version = "0.6", features = ["serde"] } webrender_api = { git = "https://github.com/servo/webrender" } webrender_traits = { path = "../webrender_traits" } -wgpu-core = { version = "0.5.0", git = "https://github.com/gfx-rs/wgpu", features = ["replay", "trace"] } +wgpu-core = { version = "0.5.0", git = "https://github.com/gfx-rs/wgpu", features = ["replay", "trace", "serial-pass"] } wgpu-types = { version = "0.5.0", git = "https://github.com/gfx-rs/wgpu", features = ["replay", "trace"] } diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index 800a248a4a6..4b342e117f3 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -29,7 +29,7 @@ use webrender_traits::{ }; use wgpu::{ binding_model::{BindGroupDescriptor, BindGroupEntry, BindingResource, BufferBinding}, - command::{BufferCopyView, TextureCopyView}, + command::{BufferCopyView, ComputePass, RenderPass, TextureCopyView}, device::HostMap, id, instance::RequestAdapterOptions, @@ -48,12 +48,20 @@ pub enum WebGPUResponse { queue_id: WebGPUQueue, _descriptor: wgt::DeviceDescriptor, }, + BufferMapAsync(Vec), } pub type WebGPUResponseResult = Result; #[derive(Debug, Deserialize, Serialize)] pub enum WebGPURequest { + BufferMapAsync { + sender: IpcSender, + buffer_id: id::BufferId, + host_map: HostMap, + map_range: std::ops::Range, + }, + BufferMapComplete(id::BufferId), CommandEncoderFinish { command_encoder_id: id::CommandEncoderId, // TODO(zakorgy): Serialize CommandBufferDescriptor in wgpu-core @@ -170,11 +178,11 @@ pub enum WebGPURequest { }, RunComputePass { command_encoder_id: id::CommandEncoderId, - pass_data: Vec, + compute_pass: ComputePass, }, RunRenderPass { command_encoder_id: id::CommandEncoderId, - pass_data: Vec, + render_pass: RenderPass, }, Submit { queue_id: id::QueueId, @@ -190,6 +198,7 @@ pub enum WebGPURequest { device_id: id::DeviceId, buffer_id: id::BufferId, array_buffer: Vec, + mapped_at_creation: bool, }, } @@ -200,6 +209,13 @@ pub enum WebGPUBindings { TextureView(id::TextureViewId), } +struct BufferMapInfo<'a> { + buffer_id: id::BufferId, + sender: IpcSender, + global: &'a wgpu::hub::Global, + size: usize, +} + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WebGPU(pub IpcSender); @@ -264,7 +280,7 @@ impl WebGPU { } } -struct WGPU { +struct WGPU<'a> { receiver: IpcReceiver, sender: IpcSender, script_sender: IpcSender, @@ -273,13 +289,15 @@ struct WGPU { devices: Vec, // Track invalid adapters https://gpuweb.github.io/gpuweb/#invalid _invalid_adapters: Vec, + // Buffers with pending mapping + buffer_maps: HashMap>, webrender_api: webrender_api::RenderApi, webrender_document: webrender_api::DocumentId, external_images: Arc>, wgpu_image_map: Arc>>, } -impl WGPU { +impl<'a> WGPU<'a> { fn new( receiver: IpcReceiver, sender: IpcSender, @@ -296,10 +314,11 @@ impl WGPU { receiver, sender, script_sender, - global: wgpu::hub::Global::new("wgpu-core", factory), + global: wgpu::hub::Global::new("wgpu-core", factory, wgt::BackendBit::PRIMARY), adapters: Vec::new(), devices: Vec::new(), _invalid_adapters: Vec::new(), + buffer_maps: HashMap::new(), webrender_api: webrender_api_sender.create_api(), webrender_document, external_images, @@ -307,9 +326,57 @@ impl WGPU { } } - fn run(mut self) { + fn run(&'a mut self) { while let Ok(msg) = self.receiver.recv() { + self.devices.iter().for_each(|device| { + let global = &self.global; + let device_id = device.0; + gfx_select!(device_id => global.device_poll(device_id, false)); + }); match msg { + WebGPURequest::BufferMapAsync { + sender, + buffer_id, + host_map, + map_range, + } => { + let map_info = BufferMapInfo { + buffer_id, + sender, + global: &self.global, + size: (map_range.end - map_range.start) as usize, + }; + self.buffer_maps.insert(buffer_id, map_info); + + unsafe extern "C" fn callback(status: BufferMapAsyncStatus, userdata: *mut u8) { + let info = (userdata as *mut BufferMapInfo).as_ref().unwrap(); + match status { + BufferMapAsyncStatus::Success => { + let global = info.global; + let data_pt = gfx_select!(info.buffer_id => + global.buffer_get_mapped_range(info.buffer_id, 0, None)); + let data = slice::from_raw_parts(data_pt, info.size).to_vec(); + if let Err(e) = + info.sender.send(Ok(WebGPUResponse::BufferMapAsync(data))) + { + warn!("Could not send BufferMapAsync Response ({})", e); + } + }, + _ => warn!("Could not map buffer({:?})", info.buffer_id), + } + } + + let operation = BufferMapOperation { + host: host_map, + callback, + user_data: convert_to_pointer(self.buffer_maps.get(&buffer_id).unwrap()), + }; + let global = &self.global; + gfx_select!(buffer_id => global.buffer_map_async(buffer_id, map_range, operation)); + }, + WebGPURequest::BufferMapComplete(buffer_id) => { + self.buffer_maps.remove(&buffer_id); + }, WebGPURequest::CommandEncoderFinish { command_encoder_id } => { let global = &self.global; let _ = gfx_select!(command_encoder_id => global.command_encoder_finish( @@ -526,14 +593,9 @@ impl WGPU { program, } => { let global = &self.global; - let descriptor = wgpu_core::pipeline::ShaderModuleDescriptor { - code: wgpu_core::U32Array { - bytes: program.as_ptr(), - length: program.len(), - }, - }; + let source = wgpu_core::pipeline::ShaderModuleSource::SpirV(&program); let _ = gfx_select!(program_id => - global.device_create_shader_module(device_id, &descriptor, program_id)); + global.device_create_shader_module(device_id, source, program_id)); }, WebGPURequest::CreateSwapChain { device_id, @@ -636,7 +698,6 @@ impl WGPU { if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) { warn!("Failed to send WebGPUMsg::Exit to script ({})", e); } - drop(self.global); if let Err(e) = sender.send(()) { warn!("Failed to send response to WebGPURequest::Exit ({})", e) } @@ -712,22 +773,22 @@ impl WGPU { }, WebGPURequest::RunComputePass { command_encoder_id, - pass_data, + compute_pass, } => { let global = &self.global; gfx_select!(command_encoder_id => global.command_encoder_run_compute_pass( command_encoder_id, - &pass_data + &compute_pass )); }, WebGPURequest::RunRenderPass { command_encoder_id, - pass_data, + render_pass, } => { let global = &self.global; gfx_select!(command_encoder_id => global.command_encoder_run_render_pass( command_encoder_id, - &pass_data + &render_pass )); }, WebGPURequest::Submit { @@ -824,7 +885,7 @@ impl WGPU { // TODO: Remove the blocking behaviour gfx_select!(device_id => global.device_poll(device_id, true)); let buf_data = gfx_select!(buffer_id => - global.buffer_get_mapped_range(buffer_id, 0, wgt::BufferSize::WHOLE)); + global.buffer_get_mapped_range(buffer_id, 0, None)); if let Some(present_data) = self.wgpu_image_map.lock().unwrap().get_mut(&external_id) { @@ -849,21 +910,34 @@ impl WGPU { device_id, buffer_id, array_buffer, + mapped_at_creation, } => { let global = &self.global; - - gfx_select!(buffer_id => global.device_set_buffer_sub_data( - device_id, - buffer_id, - 0, - array_buffer.as_slice() - )); + if mapped_at_creation { + gfx_select!(device_id => global.queue_write_buffer( + device_id, + buffer_id, + 0, + array_buffer.as_slice() + )); + } else { + gfx_select!(buffer_id => global.device_set_buffer_sub_data( + device_id, + buffer_id, + 0, + array_buffer.as_slice() + )); + } }, } } } } +fn convert_to_pointer(obj: &T) -> *mut u8 { + (obj as *const T) as *mut u8 +} + macro_rules! webgpu_resource { ($name:ident, $id:ty) => { #[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)]