Implement GPUBuffer.mapAsync and update wgpu-core

This commit is contained in:
Kunal Mohan 2020-06-22 19:52:02 +05:30
parent 0afe412d63
commit b74cea3a46
18 changed files with 514 additions and 433 deletions

59
Cargo.lock generated
View file

@ -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",
]

View file

@ -153,7 +153,7 @@ DOMInterfaces = {
},
'GPUBuffer': {
'inRealms': ['MapReadAsync'],
'inRealms': ['MapAsync'],
}
}

View file

@ -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<RawPass<id::CommandEncoderId>>);
unsafe_no_jsmanaged_fields!(Option<RenderPass>);
unsafe_no_jsmanaged_fields!(Option<ComputePass>);
unsafe_no_jsmanaged_fields!(GPUBufferState);
unsafe_no_jsmanaged_fields!(GPUCommandEncoderState);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);

View file

@ -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<Option<DOMString>>,
size: GPUSize64,
usage: u32,
state: DomRefCell<GPUBufferState>,
state: Cell<GPUBufferState>,
buffer: WebGPUBuffer,
device: WebGPUDevice,
valid: Cell<bool>,
#[ignore_malloc_size_of = "defined in mozjs"]
mapping: RootedTraceableBox<Heap<*mut JSObject>>,
mapping_range: (u64, u64),
size: u64,
map_mode: Cell<Option<u32>>,
}
impl GPUBuffer {
@ -53,22 +61,23 @@ impl GPUBuffer {
buffer: WebGPUBuffer,
device: WebGPUDevice,
state: GPUBufferState,
size: GPUSize64,
usage: u32,
size: u64,
valid: bool,
mapping: RootedTraceableBox<Heap<*mut JSObject>>,
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<Heap<*mut JSObject>>,
mapping_range: (u64, u64),
) -> DomRoot<Self> {
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<GPUBufferState> {
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<Promise> {
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<Promise>) {
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
);
}
}
}

View file

@ -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::<Vec<_>>();
@ -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.

View file

@ -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<Option<DOMString>>,
#[ignore_malloc_size_of = "defined in wgpu-core"]
raw_pass: DomRefCell<Option<RawPass<id::CommandEncoderId>>>,
compute_pass: DomRefCell<Option<ComputePass>>,
command_encoder: Dom<GPUCommandEncoder>,
}
@ -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<u32>) {
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);
}
}
}

View file

@ -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<std::string::String>) {
// 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<GPUBuffer> {
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<JSVal> {
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::<JSObject>());
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)),
})
},
},

View file

@ -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,
}

View file

@ -51,7 +51,7 @@ impl GPUQueueMethods for GPUQueue {
/// https://gpuweb.github.io/gpuweb/#dom-gpuqueue-submit
fn Submit(&self, command_buffers: Vec<DomRoot<GPUCommandBuffer>>) {
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,
})

View file

@ -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<Option<DOMString>>,
#[ignore_malloc_size_of = "defined in wgpu-core"]
raw_pass: DomRefCell<Option<RawPass<id::CommandEncoderId>>>,
render_pass: DomRefCell<Option<RenderPass>>,
command_encoder: Dom<GPUCommandEncoder>,
}
impl GPURenderPassEncoder {
fn new_inherited(
channel: WebGPU,
raw_pass: RawPass<id::CommandEncoderId>,
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<id::CommandEncoderId>,
render_pass: RenderPass,
parent: &GPUCommandEncoder,
) -> DomRoot<Self> {
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<u32>) {
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<f32>,
max_depth: Finite<f32>,
) {
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,
);
}
}
}

View file

@ -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;

View file

@ -5,8 +5,8 @@
// https://gpuweb.github.io/gpuweb/#gpubuffer
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUBuffer {
//Promise<ArrayBuffer> mapReadAsync();
// Promise<ArrayBuffer> mapWriteAsync();
Promise<void> 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<any> GPUMappedBuffer;

View file

@ -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;

View file

@ -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();
};

View file

@ -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 = {});

View file

@ -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;

View file

@ -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"] }

View file

@ -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<u8>),
}
pub type WebGPUResponseResult = Result<WebGPUResponse, String>;
#[derive(Debug, Deserialize, Serialize)]
pub enum WebGPURequest {
BufferMapAsync {
sender: IpcSender<WebGPUResponseResult>,
buffer_id: id::BufferId,
host_map: HostMap,
map_range: std::ops::Range<u64>,
},
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<u8>,
compute_pass: ComputePass,
},
RunRenderPass {
command_encoder_id: id::CommandEncoderId,
pass_data: Vec<u8>,
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<u8>,
mapped_at_creation: bool,
},
}
@ -200,6 +209,13 @@ pub enum WebGPUBindings {
TextureView(id::TextureViewId),
}
struct BufferMapInfo<'a> {
buffer_id: id::BufferId,
sender: IpcSender<WebGPUResponseResult>,
global: &'a wgpu::hub::Global<IdentityRecyclerFactory>,
size: usize,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct WebGPU(pub IpcSender<WebGPURequest>);
@ -264,7 +280,7 @@ impl WebGPU {
}
}
struct WGPU {
struct WGPU<'a> {
receiver: IpcReceiver<WebGPURequest>,
sender: IpcSender<WebGPURequest>,
script_sender: IpcSender<WebGPUMsg>,
@ -273,13 +289,15 @@ struct WGPU {
devices: Vec<WebGPUDevice>,
// Track invalid adapters https://gpuweb.github.io/gpuweb/#invalid
_invalid_adapters: Vec<WebGPUAdapter>,
// Buffers with pending mapping
buffer_maps: HashMap<id::BufferId, BufferMapInfo<'a>>,
webrender_api: webrender_api::RenderApi,
webrender_document: webrender_api::DocumentId,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>,
}
impl WGPU {
impl<'a> WGPU<'a> {
fn new(
receiver: IpcReceiver<WebGPURequest>,
sender: IpcSender<WebGPURequest>,
@ -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<T: Sized>(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)]