Initial implementation of GPUBuffer for WebGPU

Added WebIDL bindings for GPUBuffer, GPUBufferDescriptor, GPUBufferUsage
Implemented the `createBuffer` and `createBufferMapped` functions of GPUDevice
This commit is contained in:
Istvan Miklos 2019-11-15 15:09:04 +01:00
parent 6ccad53937
commit ebfcd0f27f
14 changed files with 428 additions and 12 deletions

4
Cargo.lock generated
View file

@ -6443,7 +6443,7 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.1.0"
source = "git+https://github.com/gfx-rs/wgpu#4fdb54d33a38632ee111465e4c8282dc95994aa8"
source = "git+https://github.com/gfx-rs/wgpu#6b097ab77a33d012e06f128f47ce74440f692d9f"
dependencies = [
"arrayvec 0.5.1",
"bitflags",
@ -6460,7 +6460,7 @@ dependencies = [
"rendy-descriptor",
"rendy-memory",
"serde",
"smallvec 0.6.10",
"smallvec 1.0.0",
"vec_map",
]

View file

@ -37,6 +37,7 @@ use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::bindings::utils::WindowProxyHandler;
use crate::dom::document::PendingRestyle;
use crate::dom::gpubuffer::GPUBufferState;
use crate::dom::htmlimageelement::SourceSet;
use crate::dom::htmlmediaelement::{HTMLMediaElementFetchContext, MediaFrameRenderer};
use crate::dom::identityhub::Identities;
@ -147,7 +148,7 @@ use tendril::stream::LossyDecoder;
use tendril::{StrTendril, TendrilSink};
use time::{Duration, Timespec, Tm};
use uuid::Uuid;
use webgpu::{WebGPU, WebGPUAdapter, WebGPUDevice};
use webgpu::{WebGPU, WebGPUAdapter, WebGPUBuffer, WebGPUDevice};
use webrender_api::{DocumentId, ImageKey};
use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState};
use webxr_api::SwapChainId as WebXRSwapChainId;
@ -511,6 +512,8 @@ unsafe_no_jsmanaged_fields!(RefCell<Identities>);
unsafe_no_jsmanaged_fields!(WebGPU);
unsafe_no_jsmanaged_fields!(WebGPUAdapter);
unsafe_no_jsmanaged_fields!(WebGPUDevice);
unsafe_no_jsmanaged_fields!(WebGPUBuffer);
unsafe_no_jsmanaged_fields!(GPUBufferState);
unsafe_no_jsmanaged_fields!(WebXRSwapChainId);
unsafe_no_jsmanaged_fields!(MediaList);
unsafe_no_jsmanaged_fields!(WebVRGamepadData, WebVRGamepadState, WebVRGamepadHand);

View file

@ -9,8 +9,7 @@ use crate::dom::bindings::codegen::Bindings::GPUAdapterBinding::{
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;

View file

@ -0,0 +1,120 @@
/* 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::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::{
self, GPUBufferMethods, GPUBufferSize,
};
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSender;
use std::cell::Cell;
use webgpu::{WebGPUBuffer, WebGPUDevice, WebGPURequest};
#[derive(MallocSizeOf)]
pub enum GPUBufferState {
Mapped,
Unmapped,
Destroyed,
}
#[dom_struct]
pub struct GPUBuffer {
reflector_: Reflector,
#[ignore_malloc_size_of = "channels are hard"]
channel: IpcSender<WebGPURequest>,
label: DomRefCell<Option<DOMString>>,
size: GPUBufferSize,
usage: u32,
state: DomRefCell<GPUBufferState>,
buffer: WebGPUBuffer,
device: WebGPUDevice,
valid: Cell<bool>,
}
impl GPUBuffer {
fn new_inherited(
channel: IpcSender<WebGPURequest>,
buffer: WebGPUBuffer,
device: WebGPUDevice,
state: GPUBufferState,
size: GPUBufferSize,
usage: u32,
valid: bool,
) -> GPUBuffer {
Self {
reflector_: Reflector::new(),
channel,
label: DomRefCell::new(None),
state: DomRefCell::new(state),
size: size,
usage: usage,
valid: Cell::new(valid),
device,
buffer,
}
}
#[allow(unsafe_code)]
pub fn new(
global: &GlobalScope,
channel: IpcSender<WebGPURequest>,
buffer: WebGPUBuffer,
device: WebGPUDevice,
state: GPUBufferState,
size: GPUBufferSize,
usage: u32,
valid: bool,
) -> DomRoot<GPUBuffer> {
reflect_dom_object(
Box::new(GPUBuffer::new_inherited(
channel, buffer, device, state, size, usage, valid,
)),
global,
GPUBufferBinding::Wrap,
)
}
}
impl Drop for GPUBuffer {
fn drop(&mut self) {
self.Destroy()
}
}
impl GPUBufferMethods for GPUBuffer {
/// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap
fn Unmap(&self) {
self.channel
.send(WebGPURequest::UnmapBuffer(self.buffer))
.unwrap();
}
/// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy
fn Destroy(&self) {
match *self.state.borrow() {
GPUBufferState::Mapped => {
self.Unmap();
},
_ => {},
};
self.channel
.send(WebGPURequest::DestroyBuffer(self.buffer))
.unwrap();
*self.state.borrow_mut() = GPUBufferState::Destroyed;
}
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn GetLabel(&self) -> Option<DOMString> {
self.label.borrow().clone()
}
/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn SetLabel(&self, value: Option<DOMString>) {
*self.label.borrow_mut() = value;
}
}

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 GPUBufferUsage {
reflector_: Reflector,
}

View file

@ -2,19 +2,30 @@
* 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::GPUBufferBinding::GPUBufferDescriptor;
use crate::dom::bindings::codegen::Bindings::GPUDeviceBinding::{self, GPUDeviceMethods};
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::gpuadapter::GPUAdapter;
use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState};
use crate::dom::window::Window;
use crate::script_runtime::JSContext as SafeJSContext;
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
use std::ptr::NonNull;
use webgpu::WebGPUDevice;
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::typedarray::{ArrayBuffer, CreateWith};
use std::ptr::{self, NonNull};
use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage};
use webgpu::{WebGPUBuffer, WebGPUDevice, WebGPURequest};
#[dom_struct]
pub struct GPUDevice {
@ -63,6 +74,68 @@ impl GPUDevice {
}
}
impl GPUDevice {
unsafe fn resolve_create_buffer_mapped(
&self,
cx: SafeJSContext,
channel: ipc_channel::ipc::IpcSender<WebGPURequest>,
gpu_buffer: WebGPUBuffer,
array_buffer: Vec<u8>,
descriptor: BufferDescriptor,
valid: bool,
) -> Vec<JSVal> {
rooted!(in(*cx) let mut js_array_buffer = ptr::null_mut::<JSObject>());
let mut out = Vec::new();
assert!(ArrayBuffer::create(
*cx,
CreateWith::Slice(array_buffer.as_slice()),
js_array_buffer.handle_mut(),
)
.is_ok());
let buff = GPUBuffer::new(
&self.global(),
channel,
gpu_buffer,
self.device,
GPUBufferState::Mapped,
descriptor.size,
descriptor.usage.bits(),
valid,
);
out.push(ObjectValue(buff.reflector().get_jsobject().get()));
out.push(ObjectValue(js_array_buffer.get()));
out
}
fn validate_buffer_descriptor(
&self,
descriptor: &GPUBufferDescriptor,
) -> (bool, BufferDescriptor) {
// TODO: Record a validation error in the current scope if the descriptor is invalid.
let wgpu_usage = BufferUsage::from_bits(descriptor.usage);
let valid = wgpu_usage.is_some() && descriptor.size > 0;
if valid {
(
true,
BufferDescriptor {
size: descriptor.size,
usage: wgpu_usage.unwrap(),
},
)
} else {
(
false,
BufferDescriptor {
size: 0,
usage: BufferUsage::STORAGE,
},
)
}
}
}
impl GPUDeviceMethods for GPUDevice {
/// https://gpuweb.github.io/gpuweb/#dom-gpudevice-adapter
fn Adapter(&self) -> DomRoot<GPUAdapter> {
@ -88,4 +161,89 @@ impl GPUDeviceMethods for GPUDevice {
fn SetLabel(&self, value: Option<DOMString>) {
*self.label.borrow_mut() = value;
}
/// https://gpuweb.github.io/gpuweb/#dom-gpudevice-createbuffer
fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> DomRoot<GPUBuffer> {
let (valid, wgpu_descriptor) = self.validate_buffer_descriptor(descriptor);
let channel;
let (sender, receiver) = ipc::channel().unwrap();
if let Some(window) = self.global().downcast::<Window>() {
let id = window.Navigator().create_buffer_id(self.device.0.backend());
match window.webgpu_channel() {
Some(thread) => {
channel = thread.0.clone();
thread
.0
.send(WebGPURequest::CreateBuffer(
sender,
self.device,
id,
wgpu_descriptor,
))
.unwrap();
},
None => unimplemented!(),
}
} else {
unimplemented!()
};
let buffer = receiver.recv().unwrap();
GPUBuffer::new(
&self.global(),
channel,
buffer,
self.device,
GPUBufferState::Unmapped,
descriptor.size,
descriptor.usage,
valid,
)
}
/// 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);
let channel;
let (sender, receiver) = ipc::channel().unwrap();
rooted!(in(*cx) let js_val = UndefinedValue());
if let Some(window) = self.global().downcast::<Window>() {
let id = window.Navigator().create_buffer_id(self.device.0.backend());
match window.webgpu_channel() {
Some(thread) => {
channel = thread.0.clone();
thread
.0
.send(WebGPURequest::CreateBufferMapped(
sender,
self.device,
id,
wgpu_descriptor.clone(),
))
.unwrap()
},
None => return vec![js_val.get()],
}
} else {
return vec![js_val.get()];
};
let (buffer, array_buffer) = receiver.recv().unwrap();
unsafe {
self.resolve_create_buffer_mapped(
cx,
channel,
buffer,
array_buffer,
wgpu_descriptor,
valid,
)
}
}
}

View file

@ -5,7 +5,7 @@
use smallvec::SmallVec;
use webgpu::wgpu::{
hub::IdentityManager,
id::{AdapterId, DeviceId},
id::{AdapterId, BufferId, DeviceId},
Backend,
};
@ -13,6 +13,7 @@ use webgpu::wgpu::{
pub struct IdentityHub {
adapters: IdentityManager,
devices: IdentityManager,
buffers: IdentityManager,
backend: Backend,
}
@ -21,6 +22,7 @@ impl IdentityHub {
IdentityHub {
adapters: IdentityManager::default(),
devices: IdentityManager::default(),
buffers: IdentityManager::default(),
backend,
}
}
@ -32,6 +34,10 @@ impl IdentityHub {
fn create_device_id(&mut self) -> DeviceId {
self.devices.alloc(self.backend)
}
pub fn create_buffer_id(&mut self) -> BufferId {
self.buffers.alloc(self.backend)
}
}
#[derive(Debug)]
@ -99,4 +105,18 @@ impl Identities {
}
ids
}
pub fn create_buffer_id(&mut self, backend: Backend) -> BufferId {
match backend {
#[cfg(any(target_os = "linux", target_os = "windows"))]
Backend::Vulkan => self.vk_hub.create_buffer_id(),
#[cfg(target_os = "windows")]
Backend::Dx12 => self.dx12_hub.create_buffer_id(),
#[cfg(target_os = "windows")]
Backend::Dx11 => self.dx11_hub.create_buffer_id(),
#[cfg(any(target_os = "ios", target_os = "macos"))]
Backend::Metal => self.metal_hub.create_buffer_id(),
_ => self.dummy_hub.create_buffer_id(),
}
}
}

View file

@ -317,6 +317,8 @@ pub mod gamepadlist;
pub mod globalscope;
pub mod gpu;
pub mod gpuadapter;
pub mod gpubuffer;
pub mod gpubufferusage;
pub mod gpudevice;
pub mod hashchangeevent;
pub mod headers;

View file

@ -28,7 +28,7 @@ use smallvec::SmallVec;
use std::cell::RefCell;
use std::rc::Rc;
use webgpu::wgpu::{
id::{AdapterId, DeviceId},
id::{AdapterId, BufferId, DeviceId},
Backend,
};
@ -84,6 +84,10 @@ impl Navigator {
pub fn create_device_id(&self, backend: Backend) -> DeviceId {
self.gpu_id_hub.borrow_mut().create_device_id(backend)
}
pub fn create_buffer_id(&self, backend: Backend) -> BufferId {
self.gpu_id_hub.borrow_mut().create_buffer_id(backend)
}
}
impl NavigatorMethods for Navigator {

View file

@ -0,0 +1,25 @@
/* 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/#gpubuffer
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
interface GPUBuffer {
// Promise<ArrayBuffer> mapReadAsync();
// Promise<ArrayBuffer> mapWriteAsync();
void unmap();
void destroy();
};
GPUBuffer includes GPUObjectBase;
dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
required GPUBufferSize size;
required GPUBufferUsageFlags usage;
};
typedef unsigned long long GPUBufferSize;
typedef unsigned long GPUBufferUsageFlags;
typedef sequence<any> GPUMappedBuffer;

View file

@ -0,0 +1,17 @@
/* 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/#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;
};

View file

@ -9,9 +9,9 @@ interface GPUDevice : EventTarget {
readonly attribute object extensions;
readonly attribute object limits;
/*GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
/*Promise<GPUMappedBuffer> createBufferMappedAsync(GPUBufferDescriptor descriptor);
GPUTexture createTexture(GPUTextureDescriptor descriptor);
GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});

View file

@ -36,6 +36,20 @@ pub enum WebGPURequest {
wgpu::id::DeviceId,
),
Exit(IpcSender<()>),
CreateBuffer(
IpcSender<WebGPUBuffer>,
WebGPUDevice,
wgpu::id::BufferId,
wgpu::resource::BufferDescriptor,
),
CreateBufferMapped(
IpcSender<(WebGPUBuffer, Vec<u8>)>,
WebGPUDevice,
wgpu::id::BufferId,
wgpu::resource::BufferDescriptor,
),
UnmapBuffer(WebGPUBuffer),
DestroyBuffer(WebGPUBuffer),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -152,6 +166,47 @@ impl WGPU {
)
}
},
WebGPURequest::CreateBuffer(sender, device, id, descriptor) => {
let global = &self.global;
let _output =
gfx_select!(id => global.device_create_buffer(device.0, &descriptor, id));
let buffer = WebGPUBuffer(id);
if let Err(e) = sender.send(buffer) {
warn!(
"Failed to send response to WebGPURequest::CreateBuffer ({})",
e
)
}
},
WebGPURequest::CreateBufferMapped(sender, device, id, descriptor) => {
let global = &self.global;
let mut arr_buff_ptr: *mut u8 = std::ptr::null_mut();
let buffer_size = descriptor.size as usize;
let _output = gfx_select!(id =>
global.device_create_buffer_mapped(device.0, &descriptor, &mut arr_buff_ptr, id));
let buffer = WebGPUBuffer(id);
let mut array_buffer = Vec::with_capacity(buffer_size);
unsafe {
array_buffer.set_len(buffer_size);
std::ptr::copy(arr_buff_ptr, array_buffer.as_mut_ptr(), buffer_size);
};
if let Err(e) = sender.send((buffer, array_buffer)) {
warn!(
"Failed to send response to WebGPURequest::CreateBufferMapped ({})",
e
)
}
},
WebGPURequest::UnmapBuffer(buffer) => {
let global = &self.global;
let _output = gfx_select!(buffer.0 => global.buffer_unmap(buffer.0));
},
WebGPURequest::DestroyBuffer(buffer) => {
let global = &self.global;
let _output = gfx_select!(buffer.0 => global.buffer_destroy(buffer.0));
},
WebGPURequest::Exit(sender) => {
self.deinit();
if let Err(e) = sender.send(()) {
@ -181,3 +236,4 @@ macro_rules! webgpu_resource {
webgpu_resource!(WebGPUAdapter, wgpu::id::AdapterId);
webgpu_resource!(WebGPUDevice, wgpu::id::DeviceId);
webgpu_resource!(WebGPUBuffer, wgpu::id::BufferId);

View file

@ -45,6 +45,7 @@ packages = [
"proc-macro2",
"quote",
"syn",
"smallvec",
"unicode-xid",
"wayland-sys",
]