mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Implement mapReadAsync function of GPUBuffer
Implemented the `mapReadAsync` and fixed the `unmap` functions of `GPUBuffer`. Added `mapped` internal slot for tracking the ArrayBuffer/Promise. Added more states to the `GPUBufferState` enum.
This commit is contained in:
parent
92f5b36f49
commit
2df4d9fce4
5 changed files with 253 additions and 62 deletions
|
@ -150,6 +150,10 @@ DOMInterfaces = {
|
|||
|
||||
'GPUAdapter': {
|
||||
'inRealms': ['RequestDevice'],
|
||||
},
|
||||
|
||||
'GPUBuffer': {
|
||||
'inRealms': ['MapReadAsync'],
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,17 +6,36 @@ 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,
|
||||
}
|
||||
|
@ -24,7 +43,7 @@ pub enum GPUBufferState {
|
|||
#[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();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue