From b74cea3a4698ca7e35a801b37b8b61479a46ede5 Mon Sep 17 00:00:00 2001 From: Kunal Mohan Date: Mon, 22 Jun 2020 19:52:02 +0530 Subject: [PATCH 1/5] Implement GPUBuffer.mapAsync and update wgpu-core --- Cargo.lock | 59 +++-- .../script/dom/bindings/codegen/Bindings.conf | 2 +- components/script/dom/bindings/trace.rs | 12 +- components/script/dom/gpubuffer.rs | 216 +++++++++++++----- components/script/dom/gpucommandencoder.rs | 96 +++----- .../script/dom/gpucomputepassencoder.rs | 50 ++-- components/script/dom/gpudevice.rs | 147 +++--------- components/script/dom/gpumapmode.rs | 11 + components/script/dom/gpuqueue.rs | 2 +- components/script/dom/gpurenderpassencoder.rs | 178 ++++++--------- components/script/dom/mod.rs | 1 + .../script/dom/webidls/GPUBuffer.webidl | 7 +- .../script/dom/webidls/GPUBufferUsage.webidl | 21 +- .../dom/webidls/GPUComputePassEncoder.webidl | 2 +- .../script/dom/webidls/GPUDevice.webidl | 1 - .../script/dom/webidls/GPUMapMode.webidl | 12 + components/webgpu/Cargo.toml | 2 +- components/webgpu/lib.rs | 128 ++++++++--- 18 files changed, 514 insertions(+), 433 deletions(-) create mode 100644 components/script/dom/gpumapmode.rs create mode 100644 components/script/dom/webidls/GPUMapMode.webidl 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)] From e6f0d12f97edec3b98164fe56a35b7a791d829eb Mon Sep 17 00:00:00 2001 From: Kunal Mohan Date: Thu, 25 Jun 2020 20:21:38 +0530 Subject: [PATCH 2/5] Remove peek-poke and peek-poke-derive from ignored packages --- servo-tidy.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/servo-tidy.toml b/servo-tidy.toml index 7fb6c99845f..c60f1d28b29 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -32,8 +32,6 @@ packages = [ "cocoa", "gleam", "libloading", - "peek-poke", - "peek-poke-derive", "wayland-sys", "parking_lot", "parking_lot_core", From ef3b1414065f91b81a9d5980b6c954640515d206 Mon Sep 17 00:00:00 2001 From: Kunal Mohan Date: Fri, 26 Jun 2020 13:20:29 +0530 Subject: [PATCH 3/5] address review comments --- components/script/dom/bindings/trace.rs | 3 +- components/script/dom/gpubuffer.rs | 58 +- components/script/dom/gpudevice.rs | 6 +- components/webgpu/lib.rs | 1165 ++++++++++++----------- 4 files changed, 632 insertions(+), 600 deletions(-) diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 9c536ff0994..d6216cdad48 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -135,7 +135,7 @@ use std::borrow::Cow; use std::cell::{Cell, RefCell, UnsafeCell}; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::hash::{BuildHasher, Hash}; -use std::ops::{Deref, DerefMut}; +use std::ops::{Deref, DerefMut, Range}; use std::path::PathBuf; use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicUsize}; @@ -586,6 +586,7 @@ unsafe_no_jsmanaged_fields!(Option); unsafe_no_jsmanaged_fields!(Option); unsafe_no_jsmanaged_fields!(GPUBufferState); unsafe_no_jsmanaged_fields!(GPUCommandEncoderState); +unsafe_no_jsmanaged_fields!(Range); unsafe_no_jsmanaged_fields!(WebXRSwapChainId); unsafe_no_jsmanaged_fields!(MediaList); unsafe_no_jsmanaged_fields!( diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index 016f5d8bd1d..8b1b80e6365 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::GPUBufferMethods; +use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::{GPUBufferMethods, GPUSize64}; use crate::dom::bindings::codegen::Bindings::GPUMapModeBinding::GPUMapModeConstants; use crate::dom::bindings::error::Error; use crate::dom::bindings::reflector::DomObject; @@ -22,6 +22,7 @@ use js::rust::jsapi_wrapped::{DetachArrayBuffer, IsPromiseObject, RejectPromise} use js::rust::MutableHandle; use js::typedarray::{ArrayBuffer, CreateWith}; use std::cell::Cell; +use std::ops::Range; use std::ptr; use std::rc::Rc; use webgpu::{ @@ -50,8 +51,8 @@ pub struct GPUBuffer { valid: Cell, #[ignore_malloc_size_of = "defined in mozjs"] mapping: RootedTraceableBox>, - mapping_range: (u64, u64), - size: u64, + mapping_range: DomRefCell>, + size: GPUSize64, map_mode: Cell>, } @@ -61,10 +62,10 @@ impl GPUBuffer { buffer: WebGPUBuffer, device: WebGPUDevice, state: GPUBufferState, - size: u64, + size: GPUSize64, valid: bool, mapping: RootedTraceableBox>, - mapping_range: (u64, u64), + mapping_range: Range, ) -> Self { Self { reflector_: Reflector::new(), @@ -76,7 +77,7 @@ impl GPUBuffer { buffer, mapping, size, - mapping_range, + mapping_range: DomRefCell::new(mapping_range), map_mode: Cell::new(None), } } @@ -88,10 +89,10 @@ impl GPUBuffer { buffer: WebGPUBuffer, device: WebGPUDevice, state: GPUBufferState, - size: u64, + size: GPUSize64, valid: bool, mapping: RootedTraceableBox>, - mapping_range: (u64, u64), + mapping_range: Range, ) -> DomRoot { reflect_dom_object( Box::new(GPUBuffer::new_inherited( @@ -186,6 +187,7 @@ impl GPUBufferMethods for GPUBuffer { // Step 4 self.state.set(GPUBufferState::Unmapped); self.map_mode.set(None); + *self.mapping_range.borrow_mut() = 0..0; } /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy @@ -217,17 +219,16 @@ impl GPUBufferMethods for GPUBuffer { let map_range = if size == 0 { offset..self.size } else { - offset..size + if offset + size > self.size { + warn!("Requested mapping size is greated than buffer size"); + promise.reject_error(Error::Abort); + return promise; + } + offset..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 - }, + GPUMapModeConstants::READ => HostMap::Read, + GPUMapModeConstants::WRITE => HostMap::Write, _ => { promise.reject_error(Error::Abort); return promise; @@ -238,15 +239,13 @@ impl GPUBufferMethods for GPUBuffer { 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, + map_range: map_range.clone(), }) { warn!( "Failed to send BufferMapAsync ({:?}) ({})", @@ -256,6 +255,9 @@ impl GPUBufferMethods for GPUBuffer { return promise; } + self.state.set(GPUBufferState::MappingPending); + self.map_mode.set(Some(mode)); + *self.mapping_range.borrow_mut() = map_range; promise } @@ -275,19 +277,23 @@ impl AsyncWGPUListener for GPUBuffer { fn handle_response(&self, response: WebGPUResponse, promise: &Rc) { match response { WebGPUResponse::BufferMapAsync(bytes) => { - if unsafe { + match unsafe { ArrayBuffer::create( *self.global().get_cx(), CreateWith::Slice(&bytes), MutableHandle::from_raw(self.mapping.handle_mut()), ) - } - .is_err() - { - promise.reject_error(Error::Operation); + } { + Ok(_) => promise.resolve_native(&()), + Err(()) => { + warn!( + "Failed to create ArrayBuffer for buffer({:?})", + self.buffer.0 + ); + promise.reject_error(Error::Operation); + }, } self.state.set(GPUBufferState::Mapped); - promise.resolve_native(&()); }, _ => { warn!("Wrong WebGPUResponse received"); diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index f522cd17d33..08bc4f05744 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -191,10 +191,10 @@ impl GPUDeviceMethods for GPUDevice { .is_ok()); } state = GPUBufferState::MappedAtCreation; - mapping_range = (0, descriptor.size); + mapping_range = 0..descriptor.size; } else { state = GPUBufferState::Unmapped; - mapping_range = (0, 0); + mapping_range = 0..0; } GPUBuffer::new( @@ -364,7 +364,7 @@ impl GPUDeviceMethods for GPUDevice { WebGPUBindings::Buffer(BufferBinding { buffer_id: b.buffer.id().0, offset: b.offset, - size: wgt::BufferSize::new(b.size.unwrap_or(0)), + size: b.size.and_then(wgt::BufferSize::new), }) }, }, diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index 4b342e117f3..6eeb0824286 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -20,9 +20,10 @@ use servo_config::pref; use smallvec::SmallVec; use std::collections::HashMap; use std::ffi::CString; -use std::ptr; +use std::ptr::{self, NonNull}; use std::slice; use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; use webrender_traits::{ WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageHandlerType, WebrenderImageSource, @@ -36,6 +37,8 @@ use wgpu::{ resource::{BufferMapAsyncStatus, BufferMapOperation}, }; +const DEVICE_POLL_INTERVAL: u64 = 100; + #[derive(Debug, Deserialize, Serialize)] pub enum WebGPUResponse { RequestAdapter { @@ -295,6 +298,7 @@ struct WGPU<'a> { webrender_document: webrender_api::DocumentId, external_images: Arc>, wgpu_image_map: Arc>>, + last_poll: Instant, } impl<'a> WGPU<'a> { @@ -323,612 +327,633 @@ impl<'a> WGPU<'a> { webrender_document, external_images, wgpu_image_map, + last_poll: Instant::now(), } } 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, + loop { + if self.last_poll.elapsed() >= Duration::from_millis(DEVICE_POLL_INTERVAL) { + self.global.poll_all_devices(false); + self.last_poll = Instant::now(); + } + if let Ok(msg) = self.receiver.try_recv() { + match msg { + WebGPURequest::BufferMapAsync { sender, - global: &self.global, - size: (map_range.end - map_range.start) as usize, - }; - self.buffer_maps.insert(buffer_id, map_info); + 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), + unsafe extern "C" fn callback( + status: BufferMapAsyncStatus, + userdata: *mut u8, + ) { + let nonnull_info = + NonNull::new(userdata as *mut BufferMapInfo).unwrap(); + let info = nonnull_info.as_ref(); + 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); + } + }, + _ => error!("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( - command_encoder_id, - &wgt::CommandBufferDescriptor::default() - )); - }, - WebGPURequest::CopyBufferToBuffer { - command_encoder_id, - source_id, - source_offset, - destination_id, - destination_offset, - size, - } => { - let global = &self.global; - let _ = gfx_select!(command_encoder_id => global.command_encoder_copy_buffer_to_buffer( + 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( + command_encoder_id, + &wgt::CommandBufferDescriptor::default() + )); + }, + WebGPURequest::CopyBufferToBuffer { command_encoder_id, source_id, source_offset, destination_id, destination_offset, - size - )); - }, - WebGPURequest::CreateBindGroup { - device_id, - bind_group_id, - bind_group_layout_id, - mut entries, - } => { - let global = &self.global; - let bindings = entries - .drain(..) - .map(|(bind, res)| { - let resource = match res { - WebGPUBindings::Sampler(s) => BindingResource::Sampler(s), - WebGPUBindings::TextureView(t) => BindingResource::TextureView(t), - WebGPUBindings::Buffer(b) => BindingResource::Buffer(b), - }; - BindGroupEntry { - binding: bind, - resource, - } - }) - .collect::>(); - let descriptor = BindGroupDescriptor { - label: None, - layout: bind_group_layout_id, - bindings: bindings.as_slice(), - }; - let _ = gfx_select!(bind_group_id => - global.device_create_bind_group(device_id, &descriptor, bind_group_id)); - }, - WebGPURequest::CreateBindGroupLayout { - device_id, - bind_group_layout_id, - entries, - } => { - let global = &self.global; - let descriptor = wgt::BindGroupLayoutDescriptor { - bindings: entries.as_slice(), - label: None, - }; - let _ = gfx_select!(bind_group_layout_id => - global.device_create_bind_group_layout(device_id, &descriptor, bind_group_layout_id)); - }, - WebGPURequest::CreateBuffer { - device_id, - buffer_id, - descriptor, - } => { - let global = &self.global; - let st = CString::new(descriptor.label.as_bytes()).unwrap(); - let _ = gfx_select!(buffer_id => - global.device_create_buffer(device_id, &descriptor.map_label(|_| st.as_ptr()), buffer_id)); - }, - WebGPURequest::CreateCommandEncoder { - device_id, - command_encoder_id, - } => { - let global = &self.global; - let desc = wgt::CommandEncoderDescriptor { label: ptr::null() }; - let _ = gfx_select!(command_encoder_id => - global.device_create_command_encoder(device_id, &desc, command_encoder_id)); - }, - WebGPURequest::CreateComputePipeline { - device_id, - compute_pipeline_id, - pipeline_layout_id, - program_id, - entry_point, - } => { - let global = &self.global; - let entry_point = std::ffi::CString::new(entry_point).unwrap(); - let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor { - layout: pipeline_layout_id, - compute_stage: wgpu_core::pipeline::ProgrammableStageDescriptor { - module: program_id, - entry_point: entry_point.as_ptr(), - }, - }; - let _ = gfx_select!(compute_pipeline_id => - global.device_create_compute_pipeline(device_id, &descriptor, compute_pipeline_id)); - }, - WebGPURequest::CreateContext(sender) => { - let id = self - .external_images - .lock() - .expect("Lock poisoned?") - .next_id(WebrenderImageHandlerType::WebGPU); - if let Err(e) = sender.send(id) { - warn!("Failed to send ExternalImageId to new context ({})", e); - }; - }, - WebGPURequest::CreatePipelineLayout { - device_id, - pipeline_layout_id, - bind_group_layouts, - } => { - let global = &self.global; - let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { - bind_group_layouts: bind_group_layouts.as_ptr(), - bind_group_layouts_length: bind_group_layouts.len(), - }; - let _ = gfx_select!(pipeline_layout_id => - global.device_create_pipeline_layout(device_id, &descriptor, pipeline_layout_id)); - }, - //TODO: consider https://github.com/gfx-rs/wgpu/issues/684 - WebGPURequest::CreateRenderPipeline { - device_id, - render_pipeline_id, - pipeline_layout_id, - vertex_module, - vertex_entry_point, - fragment_module, - fragment_entry_point, - primitive_topology, - rasterization_state, - color_states, - depth_stencil_state, - vertex_state, - sample_count, - sample_mask, - alpha_to_coverage_enabled, - } => { - let global = &self.global; - let vertex_ep = std::ffi::CString::new(vertex_entry_point).unwrap(); - let frag_ep; - let frag_stage = match fragment_module { - Some(frag) => { - frag_ep = - std::ffi::CString::new(fragment_entry_point.unwrap()).unwrap(); - let frag_module = wgpu_core::pipeline::ProgrammableStageDescriptor { - module: frag, - entry_point: frag_ep.as_ptr(), - }; - Some(frag_module) - }, - None => None, - }; - let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { - layout: pipeline_layout_id, - vertex_stage: wgpu_core::pipeline::ProgrammableStageDescriptor { - module: vertex_module, - entry_point: vertex_ep.as_ptr(), - }, - fragment_stage: frag_stage - .as_ref() - .map_or(ptr::null(), |fs| fs as *const _), + size, + } => { + let global = &self.global; + let _ = gfx_select!(command_encoder_id => global.command_encoder_copy_buffer_to_buffer( + command_encoder_id, + source_id, + source_offset, + destination_id, + destination_offset, + size + )); + }, + WebGPURequest::CreateBindGroup { + device_id, + bind_group_id, + bind_group_layout_id, + mut entries, + } => { + let global = &self.global; + let bindings = entries + .drain(..) + .map(|(bind, res)| { + let resource = match res { + WebGPUBindings::Sampler(s) => BindingResource::Sampler(s), + WebGPUBindings::TextureView(t) => { + BindingResource::TextureView(t) + }, + WebGPUBindings::Buffer(b) => BindingResource::Buffer(b), + }; + BindGroupEntry { + binding: bind, + resource, + } + }) + .collect::>(); + let descriptor = BindGroupDescriptor { + label: None, + layout: bind_group_layout_id, + bindings: bindings.as_slice(), + }; + let _ = gfx_select!(bind_group_id => + global.device_create_bind_group(device_id, &descriptor, bind_group_id)); + }, + WebGPURequest::CreateBindGroupLayout { + device_id, + bind_group_layout_id, + entries, + } => { + let global = &self.global; + let descriptor = wgt::BindGroupLayoutDescriptor { + bindings: entries.as_slice(), + label: None, + }; + let _ = gfx_select!(bind_group_layout_id => + global.device_create_bind_group_layout(device_id, &descriptor, bind_group_layout_id)); + }, + WebGPURequest::CreateBuffer { + device_id, + buffer_id, + descriptor, + } => { + let global = &self.global; + let st = CString::new(descriptor.label.as_bytes()).unwrap(); + let _ = gfx_select!(buffer_id => + global.device_create_buffer(device_id, &descriptor.map_label(|_| st.as_ptr()), buffer_id)); + }, + WebGPURequest::CreateCommandEncoder { + device_id, + command_encoder_id, + } => { + let global = &self.global; + let desc = wgt::CommandEncoderDescriptor { label: ptr::null() }; + let _ = gfx_select!(command_encoder_id => + global.device_create_command_encoder(device_id, &desc, command_encoder_id)); + }, + WebGPURequest::CreateComputePipeline { + device_id, + compute_pipeline_id, + pipeline_layout_id, + program_id, + entry_point, + } => { + let global = &self.global; + let entry_point = std::ffi::CString::new(entry_point).unwrap(); + let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor { + layout: pipeline_layout_id, + compute_stage: wgpu_core::pipeline::ProgrammableStageDescriptor { + module: program_id, + entry_point: entry_point.as_ptr(), + }, + }; + let _ = gfx_select!(compute_pipeline_id => + global.device_create_compute_pipeline(device_id, &descriptor, compute_pipeline_id)); + }, + WebGPURequest::CreateContext(sender) => { + let id = self + .external_images + .lock() + .expect("Lock poisoned?") + .next_id(WebrenderImageHandlerType::WebGPU); + if let Err(e) = sender.send(id) { + warn!("Failed to send ExternalImageId to new context ({})", e); + }; + }, + WebGPURequest::CreatePipelineLayout { + device_id, + pipeline_layout_id, + bind_group_layouts, + } => { + let global = &self.global; + let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { + bind_group_layouts: bind_group_layouts.as_ptr(), + bind_group_layouts_length: bind_group_layouts.len(), + }; + let _ = gfx_select!(pipeline_layout_id => + global.device_create_pipeline_layout(device_id, &descriptor, pipeline_layout_id)); + }, + //TODO: consider https://github.com/gfx-rs/wgpu/issues/684 + WebGPURequest::CreateRenderPipeline { + device_id, + render_pipeline_id, + pipeline_layout_id, + vertex_module, + vertex_entry_point, + fragment_module, + fragment_entry_point, primitive_topology, - rasterization_state: &rasterization_state as *const _, - color_states: color_states.as_ptr(), - color_states_length: color_states.len(), - depth_stencil_state: depth_stencil_state - .as_ref() - .map_or(ptr::null(), |dss| dss as *const _), - vertex_state: wgpu_core::pipeline::VertexStateDescriptor { - index_format: vertex_state.0, - vertex_buffers_length: vertex_state.1.len(), - vertex_buffers: vertex_state - .1 - .iter() - .map(|buffer| wgpu_core::pipeline::VertexBufferLayoutDescriptor { - array_stride: buffer.0, - step_mode: buffer.1, - attributes_length: buffer.2.len(), - attributes: buffer.2.as_ptr(), - }) - .collect::>() - .as_ptr(), - }, + rasterization_state, + color_states, + depth_stencil_state, + vertex_state, sample_count, sample_mask, alpha_to_coverage_enabled, - }; + } => { + let global = &self.global; + let vertex_ep = std::ffi::CString::new(vertex_entry_point).unwrap(); + let frag_ep; + let frag_stage = match fragment_module { + Some(frag) => { + frag_ep = + std::ffi::CString::new(fragment_entry_point.unwrap()).unwrap(); + let frag_module = + wgpu_core::pipeline::ProgrammableStageDescriptor { + module: frag, + entry_point: frag_ep.as_ptr(), + }; + Some(frag_module) + }, + None => None, + }; + let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { + layout: pipeline_layout_id, + vertex_stage: wgpu_core::pipeline::ProgrammableStageDescriptor { + module: vertex_module, + entry_point: vertex_ep.as_ptr(), + }, + fragment_stage: frag_stage + .as_ref() + .map_or(ptr::null(), |fs| fs as *const _), + primitive_topology, + rasterization_state: &rasterization_state as *const _, + color_states: color_states.as_ptr(), + color_states_length: color_states.len(), + depth_stencil_state: depth_stencil_state + .as_ref() + .map_or(ptr::null(), |dss| dss as *const _), + vertex_state: wgpu_core::pipeline::VertexStateDescriptor { + index_format: vertex_state.0, + vertex_buffers_length: vertex_state.1.len(), + vertex_buffers: vertex_state + .1 + .iter() + .map(|buffer| { + wgpu_core::pipeline::VertexBufferLayoutDescriptor { + array_stride: buffer.0, + step_mode: buffer.1, + attributes_length: buffer.2.len(), + attributes: buffer.2.as_ptr(), + } + }) + .collect::>() + .as_ptr(), + }, + sample_count, + sample_mask, + alpha_to_coverage_enabled, + }; - let _ = gfx_select!(render_pipeline_id => - global.device_create_render_pipeline(device_id, &descriptor, render_pipeline_id)); - }, - WebGPURequest::CreateSampler { - device_id, - sampler_id, - descriptor, - } => { - let global = &self.global; - let st = CString::new(descriptor.label.as_bytes()).unwrap(); - let _ = gfx_select!(sampler_id => - global.device_create_sampler(device_id, &descriptor.map_label(|_| st.as_ptr()), sampler_id)); - }, - WebGPURequest::CreateShaderModule { - device_id, - program_id, - program, - } => { - let global = &self.global; - let source = wgpu_core::pipeline::ShaderModuleSource::SpirV(&program); - let _ = gfx_select!(program_id => - global.device_create_shader_module(device_id, source, program_id)); - }, - WebGPURequest::CreateSwapChain { - device_id, - buffer_id, - external_id, - sender, - image_desc, - image_data, - } => { - let height = image_desc.size.height; - let width = image_desc.size.width; - let buffer_stride = - ((width * 4) as u32 | (wgt::COPY_BYTES_PER_ROW_ALIGNMENT - 1)) + 1; - let _ = self.wgpu_image_map.lock().unwrap().insert( - external_id, - PresentationData { - device_id, - queue_id: device_id, - data: vec![255; (buffer_stride * height as u32) as usize], - size: Size2D::new(width, height), - buffer_id, - buffer_stride, - image_desc, - image_data: image_data.clone(), - }, - ); - let buffer_size = (buffer_stride * height as u32) as wgt::BufferAddress; - let global = &self.global; - let buffer_desc = wgt::BufferDescriptor { - label: ptr::null(), - size: buffer_size, - usage: wgt::BufferUsage::MAP_READ | wgt::BufferUsage::COPY_DST, - mapped_at_creation: false, - }; - let _ = gfx_select!(buffer_id => global.device_create_buffer( + let _ = gfx_select!(render_pipeline_id => + global.device_create_render_pipeline(device_id, &descriptor, render_pipeline_id)); + }, + WebGPURequest::CreateSampler { device_id, - &buffer_desc, - buffer_id - )); + sampler_id, + descriptor, + } => { + let global = &self.global; + let st = CString::new(descriptor.label.as_bytes()).unwrap(); + let _ = gfx_select!(sampler_id => global.device_create_sampler( + device_id, + &descriptor.map_label(|_| st.as_ptr()), + sampler_id + )); + }, + WebGPURequest::CreateShaderModule { + device_id, + program_id, + program, + } => { + let global = &self.global; + let source = wgpu_core::pipeline::ShaderModuleSource::SpirV(&program); + let _ = gfx_select!(program_id => + global.device_create_shader_module(device_id, source, program_id)); + }, + WebGPURequest::CreateSwapChain { + device_id, + buffer_id, + external_id, + sender, + image_desc, + image_data, + } => { + let height = image_desc.size.height; + let width = image_desc.size.width; + let buffer_stride = + ((width * 4) as u32 | (wgt::COPY_BYTES_PER_ROW_ALIGNMENT - 1)) + 1; + let _ = self.wgpu_image_map.lock().unwrap().insert( + external_id, + PresentationData { + device_id, + queue_id: device_id, + data: vec![255; (buffer_stride * height as u32) as usize], + size: Size2D::new(width, height), + buffer_id, + buffer_stride, + image_desc, + image_data: image_data.clone(), + }, + ); + let buffer_size = (buffer_stride * height as u32) as wgt::BufferAddress; + let global = &self.global; + let buffer_desc = wgt::BufferDescriptor { + label: ptr::null(), + size: buffer_size, + usage: wgt::BufferUsage::MAP_READ | wgt::BufferUsage::COPY_DST, + mapped_at_creation: false, + }; + let _ = gfx_select!(buffer_id => global.device_create_buffer( + device_id, + &buffer_desc, + buffer_id + )); - let image_key = self.webrender_api.generate_image_key(); - if let Err(e) = sender.send(image_key) { - warn!("Failed to send ImageKey ({})", e); - } + let image_key = self.webrender_api.generate_image_key(); + if let Err(e) = sender.send(image_key) { + warn!("Failed to send ImageKey ({})", e); + } - let mut txn = webrender_api::Transaction::new(); - txn.add_image(image_key, image_desc, image_data, None); - self.webrender_api - .send_transaction(self.webrender_document, txn); - }, - WebGPURequest::CreateTexture { - device_id, - texture_id, - descriptor, - } => { - let global = &self.global; - let st = CString::new(descriptor.label.as_bytes()).unwrap(); - let _ = gfx_select!(texture_id => - global.device_create_texture(device_id, &descriptor.map_label(|_| st.as_ptr()), texture_id)); - }, - WebGPURequest::CreateTextureView { - texture_id, - texture_view_id, - descriptor, - } => { - let global = &self.global; - let st = CString::new(descriptor.label.as_bytes()).unwrap(); - let _ = gfx_select!(texture_view_id => global.texture_create_view( + let mut txn = webrender_api::Transaction::new(); + txn.add_image(image_key, image_desc, image_data, None); + self.webrender_api + .send_transaction(self.webrender_document, txn); + }, + WebGPURequest::CreateTexture { + device_id, texture_id, - Some(&descriptor.map_label(|_| st.as_ptr())), - texture_view_id - )); - }, - WebGPURequest::DestroyBuffer(buffer) => { - let global = &self.global; - gfx_select!(buffer => global.buffer_destroy(buffer)); - }, - WebGPURequest::DestroySwapChain { - external_id, - image_key, - } => { - let data = self - .wgpu_image_map - .lock() - .unwrap() - .remove(&external_id) - .unwrap(); - let global = &self.global; - gfx_select!(data.buffer_id => global.buffer_destroy(data.buffer_id)); - let mut txn = webrender_api::Transaction::new(); - txn.delete_image(image_key); - self.webrender_api - .send_transaction(self.webrender_document, txn); - }, - WebGPURequest::DestroyTexture(texture) => { - let global = &self.global; - gfx_select!(texture => global.texture_destroy(texture)); - }, - WebGPURequest::Exit(sender) => { - if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) { - warn!("Failed to send WebGPUMsg::Exit to script ({})", e); - } - if let Err(e) = sender.send(()) { - warn!("Failed to send response to WebGPURequest::Exit ({})", e) - } - return; - }, - WebGPURequest::RequestAdapter { - sender, - options, - ids, - } => { - let adapter_id = match self.global.pick_adapter( - &options, - wgt::UnsafeExtensions::disallow(), - wgpu::instance::AdapterInputs::IdSet(&ids, |id| id.backend()), - ) { - Some(id) => id, - None => { - if let Err(e) = - sender.send(Err("Failed to get webgpu adapter".to_string())) - { - warn!( + descriptor, + } => { + let global = &self.global; + let st = CString::new(descriptor.label.as_bytes()).unwrap(); + let _ = gfx_select!(texture_id => global.device_create_texture( + device_id, + &descriptor.map_label(|_| st.as_ptr()), + texture_id + )); + }, + WebGPURequest::CreateTextureView { + texture_id, + texture_view_id, + descriptor, + } => { + let global = &self.global; + let st = CString::new(descriptor.label.as_bytes()).unwrap(); + let _ = gfx_select!(texture_view_id => global.texture_create_view( + texture_id, + Some(&descriptor.map_label(|_| st.as_ptr())), + texture_view_id + )); + }, + WebGPURequest::DestroyBuffer(buffer) => { + let global = &self.global; + gfx_select!(buffer => global.buffer_destroy(buffer)); + }, + WebGPURequest::DestroySwapChain { + external_id, + image_key, + } => { + let data = self + .wgpu_image_map + .lock() + .unwrap() + .remove(&external_id) + .unwrap(); + let global = &self.global; + gfx_select!(data.buffer_id => global.buffer_destroy(data.buffer_id)); + let mut txn = webrender_api::Transaction::new(); + txn.delete_image(image_key); + self.webrender_api + .send_transaction(self.webrender_document, txn); + }, + WebGPURequest::DestroyTexture(texture) => { + let global = &self.global; + gfx_select!(texture => global.texture_destroy(texture)); + }, + WebGPURequest::Exit(sender) => { + if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) { + warn!("Failed to send WebGPUMsg::Exit to script ({})", e); + } + if let Err(e) = sender.send(()) { + warn!("Failed to send response to WebGPURequest::Exit ({})", e) + } + return; + }, + WebGPURequest::RequestAdapter { + sender, + options, + ids, + } => { + let adapter_id = match self.global.pick_adapter( + &options, + wgt::UnsafeExtensions::disallow(), + wgpu::instance::AdapterInputs::IdSet(&ids, |id| id.backend()), + ) { + Some(id) => id, + None => { + if let Err(e) = + sender.send(Err("Failed to get webgpu adapter".to_string())) + { + warn!( "Failed to send response to WebGPURequest::RequestAdapter ({})", e ) - } - return; - }, - }; - let adapter = WebGPUAdapter(adapter_id); - self.adapters.push(adapter); - let global = &self.global; - let info = gfx_select!(adapter_id => global.adapter_get_info(adapter_id)); - if let Err(e) = sender.send(Ok(WebGPUResponse::RequestAdapter { - adapter_name: info.name, - adapter_id: adapter, - channel: WebGPU(self.sender.clone()), - })) { - warn!( - "Failed to send response to WebGPURequest::RequestAdapter ({})", - e - ) - } - }, - WebGPURequest::RequestDevice { - sender, - adapter_id, - descriptor, - device_id, - } => { - let global = &self.global; - let id = gfx_select!(device_id => global.adapter_request_device( - adapter_id.0, - &descriptor, - None, - device_id - )); + } + return; + }, + }; + let adapter = WebGPUAdapter(adapter_id); + self.adapters.push(adapter); + let global = &self.global; + let info = gfx_select!(adapter_id => global.adapter_get_info(adapter_id)); + if let Err(e) = sender.send(Ok(WebGPUResponse::RequestAdapter { + adapter_name: info.name, + adapter_id: adapter, + channel: WebGPU(self.sender.clone()), + })) { + warn!( + "Failed to send response to WebGPURequest::RequestAdapter ({})", + e + ) + } + }, + WebGPURequest::RequestDevice { + sender, + adapter_id, + descriptor, + device_id, + } => { + let global = &self.global; + let id = gfx_select!(device_id => global.adapter_request_device( + adapter_id.0, + &descriptor, + None, + device_id + )); - let device = WebGPUDevice(id); - // Note: (zakorgy) Note sure if sending the queue is needed at all, - // since wgpu-core uses the same id for the device and the queue - let queue = WebGPUQueue(id); - self.devices.push(device); - if let Err(e) = sender.send(Ok(WebGPUResponse::RequestDevice { - device_id: device, - queue_id: queue, - _descriptor: descriptor, - })) { - warn!( - "Failed to send response to WebGPURequest::RequestDevice ({})", - e - ) - } - }, - WebGPURequest::RunComputePass { - command_encoder_id, - compute_pass, - } => { - let global = &self.global; - gfx_select!(command_encoder_id => global.command_encoder_run_compute_pass( + let device = WebGPUDevice(id); + // Note: (zakorgy) Note sure if sending the queue is needed at all, + // since wgpu-core uses the same id for the device and the queue + let queue = WebGPUQueue(id); + self.devices.push(device); + if let Err(e) = sender.send(Ok(WebGPUResponse::RequestDevice { + device_id: device, + queue_id: queue, + _descriptor: descriptor, + })) { + warn!( + "Failed to send response to WebGPURequest::RequestDevice ({})", + e + ) + } + }, + WebGPURequest::RunComputePass { command_encoder_id, - &compute_pass - )); - }, - WebGPURequest::RunRenderPass { - command_encoder_id, - render_pass, - } => { - let global = &self.global; - gfx_select!(command_encoder_id => global.command_encoder_run_render_pass( + compute_pass, + } => { + let global = &self.global; + gfx_select!(command_encoder_id => global.command_encoder_run_compute_pass( + command_encoder_id, + &compute_pass + )); + }, + WebGPURequest::RunRenderPass { command_encoder_id, - &render_pass - )); - }, - WebGPURequest::Submit { - queue_id, - command_buffers, - } => { - let global = &self.global; - let _ = gfx_select!(queue_id => global.queue_submit( + render_pass, + } => { + let global = &self.global; + gfx_select!(command_encoder_id => global.command_encoder_run_render_pass( + command_encoder_id, + &render_pass + )); + }, + WebGPURequest::Submit { queue_id, - &command_buffers - )); - }, - WebGPURequest::SwapChainPresent { - external_id, - texture_id, - encoder_id, - image_key, - } => { - let global = &self.global; - let device_id; - let queue_id; - let size; - let buffer_id; - let buffer_stride; - { + command_buffers, + } => { + let global = &self.global; + let _ = gfx_select!(queue_id => global.queue_submit( + queue_id, + &command_buffers + )); + }, + WebGPURequest::SwapChainPresent { + external_id, + texture_id, + encoder_id, + image_key, + } => { + let global = &self.global; + let device_id; + let queue_id; + let size; + let buffer_id; + let buffer_stride; + { + if let Some(present_data) = + self.wgpu_image_map.lock().unwrap().get_mut(&external_id) + { + size = present_data.size; + device_id = present_data.device_id; + queue_id = present_data.queue_id; + buffer_id = present_data.buffer_id; + buffer_stride = present_data.buffer_stride; + } else { + warn!("Data not found for ExternalImageId({:?})", external_id); + continue; + } + } + + let buffer_size = + (size.height as u32 * buffer_stride) as wgt::BufferAddress; + let comm_desc = wgt::CommandEncoderDescriptor { label: ptr::null() }; + let _ = gfx_select!(encoder_id => global.device_create_command_encoder( + device_id, + &comm_desc, + encoder_id + )); + + let buffer_cv = BufferCopyView { + buffer: buffer_id, + layout: wgt::TextureDataLayout { + offset: 0, + bytes_per_row: buffer_stride, + rows_per_image: 0, + }, + }; + let texture_cv = TextureCopyView { + texture: texture_id, + mip_level: 0, + origin: wgt::Origin3d::ZERO, + }; + let copy_size = wgt::Extent3d { + width: size.width as u32, + height: size.height as u32, + depth: 1, + }; + gfx_select!(encoder_id => global.command_encoder_copy_texture_to_buffer( + encoder_id, + &texture_cv, + &buffer_cv, + ©_size + )); + let _ = gfx_select!(encoder_id => global.command_encoder_finish( + encoder_id, + &wgt::CommandBufferDescriptor::default() + )); + gfx_select!(queue_id => global.queue_submit( + queue_id, + &[encoder_id] + )); + extern "C" fn callback(status: BufferMapAsyncStatus, _user_data: *mut u8) { + match status { + BufferMapAsyncStatus::Success => { + debug!("Buffer Mapped"); + }, + _ => warn!("Could not map buffer"), + } + } + let map_op = BufferMapOperation { + host: HostMap::Read, + callback, + user_data: ptr::null_mut(), + }; + gfx_select!(buffer_id => global.buffer_map_async(buffer_id, 0..buffer_size, map_op)); + // 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, None)); if let Some(present_data) = self.wgpu_image_map.lock().unwrap().get_mut(&external_id) { - size = present_data.size; - device_id = present_data.device_id; - queue_id = present_data.queue_id; - buffer_id = present_data.buffer_id; - buffer_stride = present_data.buffer_stride; + present_data.data = unsafe { + slice::from_raw_parts(buf_data, buffer_size as usize).to_vec() + }; + let mut txn = webrender_api::Transaction::new(); + txn.update_image( + image_key, + present_data.image_desc, + present_data.image_data.clone(), + &webrender_api::DirtyRect::All, + ); + self.webrender_api + .send_transaction(self.webrender_document, txn); } else { warn!("Data not found for ExternalImageId({:?})", external_id); - continue; } - } - - let buffer_size = (size.height as u32 * buffer_stride) as wgt::BufferAddress; - let comm_desc = wgt::CommandEncoderDescriptor { label: ptr::null() }; - let _ = gfx_select!(encoder_id => global.device_create_command_encoder( + gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); + }, + WebGPURequest::UnmapBuffer { device_id, - &comm_desc, - encoder_id - )); - - let buffer_cv = BufferCopyView { - buffer: buffer_id, - layout: wgt::TextureDataLayout { - offset: 0, - bytes_per_row: buffer_stride, - rows_per_image: 0, - }, - }; - let texture_cv = TextureCopyView { - texture: texture_id, - mip_level: 0, - origin: wgt::Origin3d::ZERO, - }; - let copy_size = wgt::Extent3d { - width: size.width as u32, - height: size.height as u32, - depth: 1, - }; - gfx_select!(encoder_id => global.command_encoder_copy_texture_to_buffer( - encoder_id, - &texture_cv, - &buffer_cv, - ©_size - )); - let _ = gfx_select!(encoder_id => global.command_encoder_finish( - encoder_id, - &wgt::CommandBufferDescriptor::default() - )); - gfx_select!(queue_id => global.queue_submit( - queue_id, - &[encoder_id] - )); - extern "C" fn callback(status: BufferMapAsyncStatus, _user_data: *mut u8) { - match status { - BufferMapAsyncStatus::Success => { - debug!("Buffer Mapped"); - }, - _ => warn!("Could not map buffer"), + buffer_id, + array_buffer, + mapped_at_creation, + } => { + let global = &self.global; + 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() + )); } - } - let map_op = BufferMapOperation { - host: HostMap::Read, - callback, - user_data: ptr::null_mut(), - }; - gfx_select!(buffer_id => global.buffer_map_async(buffer_id, 0..buffer_size, map_op)); - // 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, None)); - if let Some(present_data) = - self.wgpu_image_map.lock().unwrap().get_mut(&external_id) - { - present_data.data = unsafe { - slice::from_raw_parts(buf_data, buffer_size as usize).to_vec() - }; - let mut txn = webrender_api::Transaction::new(); - txn.update_image( - image_key, - present_data.image_desc, - present_data.image_data.clone(), - &webrender_api::DirtyRect::All, - ); - self.webrender_api - .send_transaction(self.webrender_document, txn); - } else { - warn!("Data not found for ExternalImageId({:?})", external_id); - } - gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); - }, - WebGPURequest::UnmapBuffer { - device_id, - buffer_id, - array_buffer, - mapped_at_creation, - } => { - let global = &self.global; - 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() - )); - } - }, + }, + } } } } From b484836dbc66424a6abda11693eb4e31929e0d40 Mon Sep 17 00:00:00 2001 From: Kunal Mohan Date: Sat, 27 Jun 2020 12:03:59 +0530 Subject: [PATCH 4/5] Ensure proper unmap of buffer --- components/script/dom/gpubuffer.rs | 22 +++++++++++----------- components/webgpu/lib.rs | 29 +++++++++++++---------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index 8b1b80e6365..07c1c525359 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -146,17 +146,17 @@ impl GPUBufferMethods for GPUBuffer { match ArrayBuffer::from(self.mapping.get()) { Ok(array_buffer) => { // 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(); - } + self.channel + .0 + .send(WebGPURequest::UnmapBuffer { + buffer_id: self.id().0, + array_buffer: array_buffer.to_vec(), + is_map_read: self.map_mode.get() == Some(GPUMapModeConstants::READ), + offset: self.mapping_range.borrow().start, + size: self.mapping_range.borrow().end - + self.mapping_range.borrow().start, + }) + .unwrap(); // Step 3.3 unsafe { DetachArrayBuffer(*cx, self.mapping.handle()); diff --git a/components/webgpu/lib.rs b/components/webgpu/lib.rs index 6eeb0824286..71f5e633e6b 100644 --- a/components/webgpu/lib.rs +++ b/components/webgpu/lib.rs @@ -198,10 +198,11 @@ pub enum WebGPURequest { image_key: webrender_api::ImageKey, }, UnmapBuffer { - device_id: id::DeviceId, buffer_id: id::BufferId, array_buffer: Vec, - mapped_at_creation: bool, + is_map_read: bool, + offset: u64, + size: u64, }, } @@ -931,27 +932,23 @@ impl<'a> WGPU<'a> { gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); }, WebGPURequest::UnmapBuffer { - device_id, buffer_id, array_buffer, - mapped_at_creation, + is_map_read, + offset, + size, } => { let global = &self.global; - if mapped_at_creation { - gfx_select!(device_id => global.queue_write_buffer( - device_id, + if !is_map_read { + let map_ptr = gfx_select!(buffer_id => global.buffer_get_mapped_range( 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() + offset, + wgt::BufferSize::new(size) )); + unsafe { slice::from_raw_parts_mut(map_ptr, size as usize) } + .copy_from_slice(&array_buffer); } + gfx_select!(buffer_id => global.buffer_unmap(buffer_id)); }, } } From db2d313a1b76886c656a90280bfe4f44b909d977 Mon Sep 17 00:00:00 2001 From: Kunal Mohan Date: Sat, 27 Jun 2020 21:20:50 +0530 Subject: [PATCH 5/5] Fix ArrayBuffer creation in buffer mapping --- components/script/dom/gpubuffer.rs | 10 ++++------ components/script/dom/gpudevice.rs | 10 ++++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index 07c1c525359..474c30dba0f 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -19,7 +19,6 @@ use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; use js::jsval::UndefinedValue; use js::rust::jsapi_wrapped::{DetachArrayBuffer, IsPromiseObject, RejectPromise}; -use js::rust::MutableHandle; use js::typedarray::{ArrayBuffer, CreateWith}; use std::cell::Cell; use std::ops::Range; @@ -277,12 +276,10 @@ impl AsyncWGPUListener for GPUBuffer { fn handle_response(&self, response: WebGPUResponse, promise: &Rc) { match response { WebGPUResponse::BufferMapAsync(bytes) => { + let cx = self.global().get_cx(); + rooted!(in(*cx) let mut array_buffer = ptr::null_mut::()); match unsafe { - ArrayBuffer::create( - *self.global().get_cx(), - CreateWith::Slice(&bytes), - MutableHandle::from_raw(self.mapping.handle_mut()), - ) + ArrayBuffer::create(*cx, CreateWith::Slice(&bytes), array_buffer.handle_mut()) } { Ok(_) => promise.resolve_native(&()), Err(()) => { @@ -293,6 +290,7 @@ impl AsyncWGPUListener for GPUBuffer { promise.reject_error(Error::Operation); }, } + self.mapping.set(array_buffer.get()); self.state.set(GPUBufferState::Mapped); }, _ => { diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 08bc4f05744..0eb5771baac 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -54,9 +54,8 @@ use crate::script_runtime::JSContext as SafeJSContext; use arrayvec::ArrayVec; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; -use js::rust::MutableHandle; use js::typedarray::{ArrayBuffer, CreateWith}; -use std::ptr::NonNull; +use std::ptr::{self, NonNull}; use webgpu::wgpu::binding_model::BufferBinding; use webgpu::{self, wgt, WebGPU, WebGPUBindings, WebGPURequest}; @@ -182,14 +181,17 @@ impl GPUDeviceMethods for GPUDevice { let state; let mapping_range; if descriptor.mappedAtCreation { + let cx = self.global().get_cx(); + rooted!(in(*cx) let mut array_buffer = ptr::null_mut::()); unsafe { assert!(ArrayBuffer::create( - *self.global().get_cx(), + *cx, CreateWith::Length(descriptor.size as u32), - MutableHandle::from_raw(mapping.handle_mut()), + array_buffer.handle_mut(), ) .is_ok()); } + mapping.set(array_buffer.get()); state = GPUBufferState::MappedAtCreation; mapping_range = 0..descriptor.size; } else {