Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement mapReadAsync function of GPUBuffer #25831

Merged
merged 1 commit into from Feb 25, 2020
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -150,6 +150,10 @@ DOMInterfaces = {

'GPUAdapter': {
'inRealms': ['RequestDevice'],
},

'GPUBuffer': {
'inRealms': ['MapReadAsync'],
}

}
@@ -6,25 +6,44 @@ use crate::dom::bindings::cell::{DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::{
self, GPUBufferMethods, GPUBufferSize,
};
use crate::dom::bindings::error::Error;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
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::rust::MutableHandle;
use js::typedarray::{ArrayBuffer, CreateWith};
use std::cell::Cell;
use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest};
use std::ptr;
use std::rc::Rc;
use webgpu::{
wgpu::resource::BufferUsage, WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest, WebGPUResponse,
};

#[derive(MallocSizeOf)]
// https://gpuweb.github.io/gpuweb/#buffer-state
#[derive(Clone, MallocSizeOf)]
pub enum GPUBufferState {
Mapped,
MappedForReading,
MappedForWriting,
MappedPendingForReading,
MappedPendingForWriting,
Unmapped,
Destroyed,
}

#[dom_struct]
pub struct GPUBuffer {
reflector_: Reflector,
#[ignore_malloc_size_of = "channels are hard"]
#[ignore_malloc_size_of = "defined in webgpu"]
channel: WebGPU,
label: DomRefCell<Option<DOMString>>,
size: GPUBufferSize,
@@ -33,6 +52,8 @@ pub struct GPUBuffer {
buffer: WebGPUBuffer,
device: WebGPUDevice,
valid: Cell<bool>,
#[ignore_malloc_size_of = "defined in mozjs"]
mapping: RootedTraceableBox<Heap<*mut JSObject>>,
}

impl GPUBuffer {
@@ -44,6 +65,7 @@ impl GPUBuffer {
size: GPUBufferSize,
usage: u32,
valid: bool,
mapping: RootedTraceableBox<Heap<*mut JSObject>>,
) -> GPUBuffer {
Self {
reflector_: Reflector::new(),
@@ -55,6 +77,7 @@ impl GPUBuffer {
valid: Cell::new(valid),
device,
buffer,
mapping,
}
}

@@ -68,10 +91,11 @@ impl GPUBuffer {
size: GPUBufferSize,
usage: u32,
valid: bool,
mapping: RootedTraceableBox<Heap<*mut JSObject>>,
) -> DomRoot<GPUBuffer> {
reflect_dom_object(
Box::new(GPUBuffer::new_inherited(
channel, buffer, device, state, size, usage, valid,
channel, buffer, device, state, size, usage, valid, mapping,
)),
global,
GPUBufferBinding::Wrap,
@@ -104,19 +128,59 @@ impl Drop for GPUBuffer {
}

impl GPUBufferMethods for GPUBuffer {
#[allow(unsafe_code)]
/// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap
fn Unmap(&self) {
self.channel
.0
.send(WebGPURequest::UnmapBuffer(self.buffer))
.unwrap();
let cx = self.global().get_cx();
// Step 1
match *self.state.borrow() {
GPUBufferState::Unmapped | GPUBufferState::Destroyed => {
// TODO: Record validation error on the current scope
return;
},
GPUBufferState::MappedForWriting => {
// Step 3.1
match ArrayBuffer::from(self.mapping.get()) {
Ok(array_buffer) => {
self.channel
.0
.send(WebGPURequest::UnmapBuffer(
self.device.0,
self.id(),
array_buffer.to_vec(),
))
.unwrap();
// Step 3.2
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());
};
}
},
};
},
_ => {},
};
// Step 3.3
self.mapping.set(ptr::null_mut());
// Step 4
*self.state.borrow_mut() = GPUBufferState::Unmapped;
}

/// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy
fn Destroy(&self) {
match *self.state.borrow() {
GPUBufferState::Mapped => {
let state = self.state.borrow().clone();
match state {
GPUBufferState::MappedForReading | GPUBufferState::MappedForWriting => {
self.Unmap();
},
_ => {},
@@ -128,6 +192,72 @@ impl GPUBufferMethods for GPUBuffer {
*self.state.borrow_mut() = GPUBufferState::Destroyed;
}

#[allow(unsafe_code)]
/// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapreadasync
fn MapReadAsync(&self, comp: InRealm) -> Rc<Promise> {
// Step 1 & 2
let promise = Promise::new_in_current_realm(&self.global(), comp);
match *self.state.borrow() {
GPUBufferState::Unmapped => {
match BufferUsage::from_bits(self.usage) {
Some(usage) => {
if !usage.contains(BufferUsage::MAP_READ) {
// TODO: Record validation error on the current scope
promise.reject_error(Error::Abort);
return promise;
};
},
None => {
promise.reject_error(Error::Abort);
return promise;
},
}
},
_ => {
promise.reject_error(Error::Abort);
return promise;
},
}
// Step 3
self.mapping.set(*promise.promise_obj());
// Step 4
*self.state.borrow_mut() = GPUBufferState::MappedPendingForReading;

// Step 5.1
if unsafe {
ArrayBuffer::create(
*self.global().get_cx(),
CreateWith::Length(self.size as u32),
MutableHandle::from_raw(self.mapping.handle_mut()),
)
}
.is_err()
{
promise.reject_error(Error::Operation);
return promise;
}

let sender = response_async(&promise, self);
if self
.channel
.0
.send(WebGPURequest::MapReadAsync(
sender,
self.buffer.0,
self.device.0,
self.usage,
self.size,
))
.is_err()
{
promise.reject_error(Error::Operation);
return promise;
}

// Step 6
promise
}

/// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label
fn GetLabel(&self) -> Option<DOMString> {
self.label.borrow().clone()
@@ -138,3 +268,25 @@ 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::MapReadAsync(bytes) => unsafe {
match ArrayBuffer::from(self.mapping.get()) {
Ok(mut array_buffer) => {
// Step 5.2
array_buffer.update(&bytes);
// Step 5.3
*self.state.borrow_mut() = GPUBufferState::MappedForReading;
// Step 5.4
promise.resolve_native(&array_buffer);
},
_ => promise.reject_error(Error::Operation),
};
},
_ => promise.reject_error(Error::Operation),
}
}
}
@@ -46,7 +46,7 @@ use webgpu::wgpu::binding_model::{
ShaderStage,
};
use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage};
use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPUQueue, WebGPURequest};
use webgpu::{WebGPU, WebGPUDevice, WebGPUQueue, WebGPURequest};

#[dom_struct]
pub struct GPUDevice {
@@ -106,38 +106,6 @@ impl GPUDevice {
}

impl GPUDevice {
unsafe fn resolve_create_buffer_mapped(
&self,
cx: SafeJSContext,
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(),
self.channel.clone(),
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,
@@ -223,6 +191,7 @@ impl GPUDeviceMethods for GPUDevice {
descriptor.size,
descriptor.usage,
valid,
RootedTraceableBox::new(Heap::default()),
)
}

@@ -245,11 +214,33 @@ impl GPUDeviceMethods for GPUDevice {
))
.expect("Failed to create WebGPU buffer");

let (buffer, array_buffer) = receiver.recv().unwrap();

rooted!(in(*cx) let mut js_array_buffer = ptr::null_mut::<JSObject>());
unsafe {
self.resolve_create_buffer_mapped(cx, buffer, array_buffer, wgpu_descriptor, valid)
assert!(ArrayBuffer::create(
*cx,
CreateWith::Length(descriptor.size as u32),
js_array_buffer.handle_mut(),
)
.is_ok());
}

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

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.