mirror of
https://github.com/servo/servo.git
synced 2025-07-29 10:10:34 +01:00
webgpu: Sync GPUBuffer
(#33154)
* More helpers on `Promise` Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * Sync `GPUBuffer` Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * Set some good expectations Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * Some bad expect also on firefox Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * Extract DataBlock, DataView impl from GPUBuffer Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> * Fix size check to work on 32bit platforms Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
bb5926b329
commit
7fce24f9d5
12 changed files with 690 additions and 1163 deletions
|
@ -4,18 +4,23 @@
|
||||||
|
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use std::borrow::BorrowMut;
|
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::Range;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use js::jsapi::glue::NewExternalArrayBuffer;
|
use js::jsapi::{
|
||||||
use js::jsapi::{Heap, JSObject, JS_GetArrayBufferViewBuffer, JS_IsArrayBufferViewObject};
|
Heap, JSObject, JS_GetArrayBufferViewBuffer, JS_IsArrayBufferViewObject, NewExternalArrayBuffer,
|
||||||
|
};
|
||||||
use js::rust::wrappers::DetachArrayBuffer;
|
use js::rust::wrappers::DetachArrayBuffer;
|
||||||
use js::rust::{CustomAutoRooterGuard, Handle, MutableHandleObject};
|
use js::rust::{CustomAutoRooterGuard, Handle, MutableHandleObject};
|
||||||
use js::typedarray::{CreateWith, TypedArray, TypedArrayElement, TypedArrayElementCreator};
|
use js::typedarray::{
|
||||||
|
ArrayBuffer, CreateWith, HeapArrayBuffer, TypedArray, TypedArrayElement,
|
||||||
|
TypedArrayElementCreator,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::script_runtime::JSContext;
|
use crate::script_runtime::JSContext;
|
||||||
|
|
||||||
/// <https://webidl.spec.whatwg.org/#BufferSource>
|
/// <https://webidl.spec.whatwg.org/#BufferSource>
|
||||||
|
@ -402,44 +407,103 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_new_external_array_buffer<T>(
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
cx: JSContext,
|
pub struct DataBlock {
|
||||||
mapping: Arc<Mutex<Vec<T::Element>>>,
|
#[ignore_malloc_size_of = "Arc"]
|
||||||
offset: usize,
|
data: Arc<Box<[u8]>>,
|
||||||
range_size: usize,
|
/// Data views (mutable subslices of data)
|
||||||
m_end: usize,
|
data_views: Vec<DataView>,
|
||||||
) -> HeapBufferSource<T>
|
}
|
||||||
where
|
|
||||||
T: TypedArrayElement + TypedArrayElementCreator,
|
/// Returns true if two non-inclusive ranges overlap
|
||||||
T::Element: Clone + Copy,
|
// https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-if-two-ranges-overlap
|
||||||
{
|
fn range_overlap<T: std::cmp::PartialOrd>(range1: &Range<T>, range2: &Range<T>) -> bool {
|
||||||
|
range1.start < range2.end && range2.start < range1.end
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataBlock {
|
||||||
|
pub fn new_zeroed(size: usize) -> Self {
|
||||||
|
let data = vec![0; size];
|
||||||
|
Self {
|
||||||
|
data: Arc::new(data.into_boxed_slice()),
|
||||||
|
data_views: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panics if there is any active view or src data is not same length
|
||||||
|
pub fn load(&mut self, src: &[u8]) {
|
||||||
|
// `Arc::get_mut` ensures there are no views
|
||||||
|
Arc::get_mut(&mut self.data).unwrap().clone_from_slice(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panics if there is any active view
|
||||||
|
pub fn data(&mut self) -> &mut [u8] {
|
||||||
|
// `Arc::get_mut` ensures there are no views
|
||||||
|
Arc::get_mut(&mut self.data).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_views(&mut self) {
|
||||||
|
self.data_views.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns error if requested range is already mapped
|
||||||
|
pub fn view(&mut self, range: Range<usize>) -> Result<&DataView, ()> {
|
||||||
|
if self
|
||||||
|
.data_views
|
||||||
|
.iter()
|
||||||
|
.any(|view| range_overlap(&view.range, &range))
|
||||||
|
{
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
let cx = GlobalScope::get_cx();
|
||||||
/// `freeFunc()` must be threadsafe, should be safely callable from any thread
|
/// `freeFunc()` must be threadsafe, should be safely callable from any thread
|
||||||
/// without causing conflicts , unexpected behavior.
|
/// without causing conflicts, unexpected behavior.
|
||||||
/// <https://github.com/servo/mozjs/blob/main/mozjs-sys/mozjs/js/public/ArrayBuffer.h#L89>
|
|
||||||
unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
|
unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
|
||||||
// Clippy warns about "creating a `Arc` from a void raw pointer" here, but suggests
|
// Clippy warns about "creating a `Arc` from a void raw pointer" here, but suggests
|
||||||
// the exact same line to fix it. Doing the cast is tricky because of the use of
|
// the exact same line to fix it. Doing the cast is tricky because of the use of
|
||||||
// a generic type in this parameter.
|
// a generic type in this parameter.
|
||||||
#[allow(clippy::from_raw_with_void_ptr)]
|
#[allow(clippy::from_raw_with_void_ptr)]
|
||||||
let _ = Arc::from_raw(free_user_data as *const _);
|
drop(Arc::from_raw(free_user_data as *const _));
|
||||||
}
|
}
|
||||||
|
let raw: *mut Box<[u8]> = Arc::into_raw(Arc::clone(&self.data)) as _;
|
||||||
unsafe {
|
rooted!(in(*cx) let object = unsafe {
|
||||||
let mapping_slice_ptr = mapping.lock().unwrap().borrow_mut()[offset..m_end].as_mut_ptr();
|
NewExternalArrayBuffer(
|
||||||
|
|
||||||
// rooted! is needed to ensure memory safety and prevent potential garbage collection issues.
|
|
||||||
// https://github.com/mozilla-spidermonkey/spidermonkey-embedding-examples/blob/esr78/docs/GC%20Rooting%20Guide.md#performance-tweaking
|
|
||||||
rooted!(in(*cx) let array_buffer = NewExternalArrayBuffer(
|
|
||||||
*cx,
|
*cx,
|
||||||
range_size,
|
range.end - range.start,
|
||||||
mapping_slice_ptr as _,
|
// SAFETY: This is safe because we have checked there is no overlapping view
|
||||||
|
(*raw)[range.clone()].as_mut_ptr() as _,
|
||||||
Some(free_func),
|
Some(free_func),
|
||||||
Arc::into_raw(mapping) as _,
|
raw as _,
|
||||||
));
|
)
|
||||||
|
});
|
||||||
HeapBufferSource {
|
self.data_views.push(DataView {
|
||||||
buffer_source: BufferSource::ArrayBuffer(Heap::boxed(*array_buffer)),
|
range,
|
||||||
phantom: PhantomData,
|
buffer: HeapArrayBuffer::from(*object).unwrap(),
|
||||||
}
|
});
|
||||||
|
Ok(self.data_views.last().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
pub struct DataView {
|
||||||
|
#[no_trace]
|
||||||
|
range: Range<usize>,
|
||||||
|
#[ignore_malloc_size_of = "defined in mozjs"]
|
||||||
|
buffer: HeapArrayBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataView {
|
||||||
|
pub fn array_buffer(&self) -> ArrayBuffer {
|
||||||
|
unsafe { ArrayBuffer::from(self.buffer.underlying_object().get()).unwrap() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DataView {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let cx = GlobalScope::get_cx();
|
||||||
|
assert!(unsafe {
|
||||||
|
js::jsapi::DetachArrayBuffer(*cx, self.buffer.underlying_object().handle())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,6 @@ use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||||
use crate::dom::bindings::reflector::{DomObject, Reflector};
|
use crate::dom::bindings::reflector::{DomObject, Reflector};
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
use crate::dom::bindings::str::{DOMString, USVString};
|
use crate::dom::bindings::str::{DOMString, USVString};
|
||||||
use crate::dom::gpubuffer::GPUBufferState;
|
|
||||||
use crate::dom::gpucanvascontext::WebGPUContextId;
|
use crate::dom::gpucanvascontext::WebGPUContextId;
|
||||||
use crate::dom::htmlimageelement::SourceSet;
|
use crate::dom::htmlimageelement::SourceSet;
|
||||||
use crate::dom::htmlmediaelement::HTMLMediaElementFetchContext;
|
use crate::dom::htmlmediaelement::HTMLMediaElementFetchContext;
|
||||||
|
@ -366,7 +365,6 @@ unsafe_no_jsmanaged_fields!(WindowProxyHandler);
|
||||||
unsafe_no_jsmanaged_fields!(DOMString);
|
unsafe_no_jsmanaged_fields!(DOMString);
|
||||||
unsafe_no_jsmanaged_fields!(USVString);
|
unsafe_no_jsmanaged_fields!(USVString);
|
||||||
unsafe_no_jsmanaged_fields!(WebGPUContextId);
|
unsafe_no_jsmanaged_fields!(WebGPUContextId);
|
||||||
unsafe_no_jsmanaged_fields!(GPUBufferState);
|
|
||||||
unsafe_no_jsmanaged_fields!(SourceSet);
|
unsafe_no_jsmanaged_fields!(SourceSet);
|
||||||
unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext);
|
unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext);
|
||||||
unsafe_no_jsmanaged_fields!(StreamConsumer);
|
unsafe_no_jsmanaged_fields!(StreamConsumer);
|
||||||
|
|
|
@ -2,19 +2,20 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::Cell;
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use ipc_channel::ipc::IpcSharedMemory;
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
use js::typedarray::{ArrayBuffer, ArrayBufferU8};
|
use js::typedarray::ArrayBuffer;
|
||||||
use webgpu::wgc::device::HostMap;
|
use webgpu::wgc::device::HostMap;
|
||||||
use webgpu::{WebGPU, WebGPUBuffer, WebGPURequest, WebGPUResponse};
|
use webgpu::{wgt, Mapping, WebGPU, WebGPUBuffer, WebGPURequest, WebGPUResponse};
|
||||||
|
|
||||||
use super::bindings::buffer_source::{create_new_external_array_buffer, HeapBufferSource};
|
use super::bindings::buffer_source::DataBlock;
|
||||||
|
use super::bindings::codegen::Bindings::WebGPUBinding::{
|
||||||
|
GPUBufferMapState, GPUFlagsConstant, GPUMapModeFlags,
|
||||||
|
};
|
||||||
use crate::dom::bindings::cell::DomRefCell;
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
|
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
|
||||||
GPUBufferMethods, GPUMapModeConstants, GPUSize64,
|
GPUBufferMethods, GPUMapModeConstants, GPUSize64,
|
||||||
|
@ -30,31 +31,36 @@ use crate::dom::promise::Promise;
|
||||||
use crate::realms::InRealm;
|
use crate::realms::InRealm;
|
||||||
use crate::script_runtime::JSContext;
|
use crate::script_runtime::JSContext;
|
||||||
|
|
||||||
const RANGE_OFFSET_ALIGN_MASK: u64 = 8;
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
const RANGE_SIZE_ALIGN_MASK: u64 = 4;
|
pub struct ActiveBufferMapping {
|
||||||
|
// TODO(sagudev): Use IpcSharedMemory when https://github.com/servo/ipc-channel/pull/356 lands
|
||||||
// https://gpuweb.github.io/gpuweb/#buffer-state
|
/// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-data>
|
||||||
#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
|
/// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-views>
|
||||||
pub enum GPUBufferState {
|
pub data: DataBlock,
|
||||||
Mapped,
|
/// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-mode>
|
||||||
MappedAtCreation,
|
mode: GPUMapModeFlags,
|
||||||
MappingPending,
|
/// <https://gpuweb.github.io/gpuweb/#active-buffer-mapping-range>
|
||||||
Unmapped,
|
range: Range<u64>,
|
||||||
Destroyed,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
impl ActiveBufferMapping {
|
||||||
pub struct GPUBufferMapInfo {
|
/// <https://gpuweb.github.io/gpuweb/#abstract-opdef-initialize-an-active-buffer-mapping>
|
||||||
#[ignore_malloc_size_of = "Arc"]
|
pub fn new(mode: GPUMapModeFlags, range: Range<u64>) -> Fallible<Self> {
|
||||||
#[no_trace]
|
// Step 1
|
||||||
/// The `mapping` is wrapped in an `Arc` to ensure thread safety.
|
let size = range.end - range.start;
|
||||||
/// This is necessary for integration with the SpiderMonkey engine,
|
// Step 2
|
||||||
pub mapping: Arc<Mutex<Vec<u8>>>,
|
if size > (1 << 53) - 1 {
|
||||||
pub mapping_range: Range<u64>,
|
return Err(Error::Range("Over MAX_SAFE_INTEGER".to_string()));
|
||||||
pub mapped_ranges: Vec<Range<u64>>,
|
}
|
||||||
#[ignore_malloc_size_of = "defined in mozjs"]
|
let size: usize = size
|
||||||
pub js_buffers: Vec<HeapBufferSource<ArrayBufferU8>>,
|
.try_into()
|
||||||
pub map_mode: Option<u32>,
|
.map_err(|_| Error::Range("Over usize".to_string()))?;
|
||||||
|
Ok(Self {
|
||||||
|
data: DataBlock::new_zeroed(size),
|
||||||
|
mode,
|
||||||
|
range,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
|
@ -64,14 +70,18 @@ pub struct GPUBuffer {
|
||||||
#[no_trace]
|
#[no_trace]
|
||||||
channel: WebGPU,
|
channel: WebGPU,
|
||||||
label: DomRefCell<USVString>,
|
label: DomRefCell<USVString>,
|
||||||
state: Cell<GPUBufferState>,
|
|
||||||
#[no_trace]
|
#[no_trace]
|
||||||
buffer: WebGPUBuffer,
|
buffer: WebGPUBuffer,
|
||||||
device: Dom<GPUDevice>,
|
device: Dom<GPUDevice>,
|
||||||
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-size>
|
||||||
size: GPUSize64,
|
size: GPUSize64,
|
||||||
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-usage>
|
||||||
|
usage: GPUFlagsConstant,
|
||||||
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-pending_map-slot>
|
||||||
#[ignore_malloc_size_of = "promises are hard"]
|
#[ignore_malloc_size_of = "promises are hard"]
|
||||||
map_promise: DomRefCell<Option<Rc<Promise>>>,
|
pending_map: DomRefCell<Option<Rc<Promise>>>,
|
||||||
map_info: DomRefCell<Option<GPUBufferMapInfo>>,
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapping-slot>
|
||||||
|
mapping: DomRefCell<Option<ActiveBufferMapping>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUBuffer {
|
impl GPUBuffer {
|
||||||
|
@ -79,38 +89,37 @@ impl GPUBuffer {
|
||||||
channel: WebGPU,
|
channel: WebGPU,
|
||||||
buffer: WebGPUBuffer,
|
buffer: WebGPUBuffer,
|
||||||
device: &GPUDevice,
|
device: &GPUDevice,
|
||||||
state: GPUBufferState,
|
|
||||||
size: GPUSize64,
|
size: GPUSize64,
|
||||||
map_info: DomRefCell<Option<GPUBufferMapInfo>>,
|
usage: GPUFlagsConstant,
|
||||||
|
mapping: Option<ActiveBufferMapping>,
|
||||||
label: USVString,
|
label: USVString,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
channel,
|
channel,
|
||||||
label: DomRefCell::new(label),
|
label: DomRefCell::new(label),
|
||||||
state: Cell::new(state),
|
|
||||||
device: Dom::from_ref(device),
|
device: Dom::from_ref(device),
|
||||||
buffer,
|
buffer,
|
||||||
map_promise: DomRefCell::new(None),
|
pending_map: DomRefCell::new(None),
|
||||||
size,
|
size,
|
||||||
map_info,
|
usage,
|
||||||
|
mapping: DomRefCell::new(mapping),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
channel: WebGPU,
|
channel: WebGPU,
|
||||||
buffer: WebGPUBuffer,
|
buffer: WebGPUBuffer,
|
||||||
device: &GPUDevice,
|
device: &GPUDevice,
|
||||||
state: GPUBufferState,
|
|
||||||
size: GPUSize64,
|
size: GPUSize64,
|
||||||
map_info: DomRefCell<Option<GPUBufferMapInfo>>,
|
usage: GPUFlagsConstant,
|
||||||
|
mapping: Option<ActiveBufferMapping>,
|
||||||
label: USVString,
|
label: USVString,
|
||||||
) -> DomRoot<Self> {
|
) -> DomRoot<Self> {
|
||||||
reflect_dom_object(
|
reflect_dom_object(
|
||||||
Box::new(GPUBuffer::new_inherited(
|
Box::new(GPUBuffer::new_inherited(
|
||||||
channel, buffer, device, state, size, map_info, label,
|
channel, buffer, device, size, usage, mapping, label,
|
||||||
)),
|
)),
|
||||||
global,
|
global,
|
||||||
)
|
)
|
||||||
|
@ -121,86 +130,49 @@ impl GPUBuffer {
|
||||||
pub fn id(&self) -> WebGPUBuffer {
|
pub fn id(&self) -> WebGPUBuffer {
|
||||||
self.buffer
|
self.buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(&self) -> GPUBufferState {
|
|
||||||
self.state.get()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for GPUBuffer {
|
impl Drop for GPUBuffer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if matches!(self.state(), GPUBufferState::Destroyed) {
|
self.Destroy()
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Err(e) = self
|
|
||||||
.channel
|
|
||||||
.0
|
|
||||||
.send(WebGPURequest::DropBuffer(self.buffer.0))
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"Failed to send WebGPURequest::DropBuffer({:?}) ({})",
|
|
||||||
self.buffer.0, e
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUBufferMethods for GPUBuffer {
|
impl GPUBufferMethods for GPUBuffer {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap>
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap>
|
||||||
fn Unmap(&self) -> Fallible<()> {
|
fn Unmap(&self) {
|
||||||
let cx = GlobalScope::get_cx();
|
|
||||||
// Step 1
|
// Step 1
|
||||||
match self.state.get() {
|
if let Some(promise) = self.pending_map.borrow_mut().take() {
|
||||||
GPUBufferState::Unmapped | GPUBufferState::Destroyed => {
|
promise.reject_error(Error::Abort);
|
||||||
// TODO: Record validation error on the current scope
|
}
|
||||||
return Ok(());
|
// Step 2
|
||||||
},
|
let mut mapping = self.mapping.borrow_mut().take();
|
||||||
// Step 3
|
let mapping = if let Some(mapping) = mapping.as_mut() {
|
||||||
GPUBufferState::Mapped | GPUBufferState::MappedAtCreation => {
|
mapping
|
||||||
let mut info = self.map_info.borrow_mut();
|
|
||||||
let m_info = if let Some(m_info) = info.as_mut() {
|
|
||||||
m_info
|
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Operation);
|
return;
|
||||||
};
|
};
|
||||||
let m_range = m_info.mapping_range.clone();
|
|
||||||
|
// Step 3
|
||||||
|
mapping.data.clear_views();
|
||||||
|
// Step 5&7
|
||||||
if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
|
if let Err(e) = self.channel.0.send(WebGPURequest::UnmapBuffer {
|
||||||
buffer_id: self.id().0,
|
buffer_id: self.id().0,
|
||||||
device_id: self.device.id().0,
|
array_buffer: IpcSharedMemory::from_bytes(mapping.data.data()),
|
||||||
array_buffer: IpcSharedMemory::from_bytes(&m_info.mapping.lock().unwrap()),
|
write_back: mapping.mode >= GPUMapModeConstants::WRITE,
|
||||||
is_map_read: m_info.map_mode == Some(GPUMapModeConstants::READ),
|
offset: mapping.range.start,
|
||||||
offset: m_range.start,
|
size: mapping.range.end - mapping.range.start,
|
||||||
size: m_range.end - m_range.start,
|
|
||||||
}) {
|
}) {
|
||||||
warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
|
warn!("Failed to send Buffer unmap ({:?}) ({})", self.buffer.0, e);
|
||||||
}
|
}
|
||||||
// Step 3.3
|
|
||||||
m_info.js_buffers.drain(..).for_each(|obj| {
|
|
||||||
obj.detach_buffer(cx);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// Step 2
|
|
||||||
GPUBufferState::MappingPending => {
|
|
||||||
let promise = self.map_promise.borrow_mut().take().unwrap();
|
|
||||||
promise.reject_error(Error::Operation);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// Step 4
|
|
||||||
self.state.set(GPUBufferState::Unmapped);
|
|
||||||
*self.map_info.borrow_mut() = None;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy>
|
/// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy
|
||||||
fn Destroy(&self) -> Fallible<()> {
|
fn Destroy(&self) {
|
||||||
let state = self.state.get();
|
// Step 1
|
||||||
match state {
|
self.Unmap();
|
||||||
GPUBufferState::Mapped | GPUBufferState::MappedAtCreation => {
|
// Step 2
|
||||||
self.Unmap()?;
|
|
||||||
},
|
|
||||||
GPUBufferState::Destroyed => return Ok(()),
|
|
||||||
_ => {},
|
|
||||||
};
|
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
.channel
|
.channel
|
||||||
.0
|
.0
|
||||||
|
@ -211,11 +183,9 @@ impl GPUBufferMethods for GPUBuffer {
|
||||||
self.buffer.0, e
|
self.buffer.0, e
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
self.state.set(GPUBufferState::Destroyed);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapasync-offset-size>
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapasync>
|
||||||
fn MapAsync(
|
fn MapAsync(
|
||||||
&self,
|
&self,
|
||||||
mode: u32,
|
mode: u32,
|
||||||
|
@ -224,22 +194,16 @@ impl GPUBufferMethods for GPUBuffer {
|
||||||
comp: InRealm,
|
comp: InRealm,
|
||||||
) -> Rc<Promise> {
|
) -> Rc<Promise> {
|
||||||
let promise = Promise::new_in_current_realm(comp);
|
let promise = Promise::new_in_current_realm(comp);
|
||||||
let range_size = if let Some(s) = size {
|
// Step 2
|
||||||
s
|
if self.pending_map.borrow().is_some() {
|
||||||
} else if offset >= self.size {
|
|
||||||
promise.reject_error(Error::Operation);
|
promise.reject_error(Error::Operation);
|
||||||
return promise;
|
return promise;
|
||||||
} else {
|
|
||||||
self.size - offset
|
|
||||||
};
|
|
||||||
if self.state.get() != GPUBufferState::Unmapped {
|
|
||||||
self.device
|
|
||||||
.dispatch_error(webgpu::Error::Validation(String::from(
|
|
||||||
"Buffer is not Unmapped",
|
|
||||||
)));
|
|
||||||
promise.reject_error(Error::Abort);
|
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
// Step 4
|
||||||
|
*self.pending_map.borrow_mut() = Some(promise.clone());
|
||||||
|
// Step 5
|
||||||
|
|
||||||
|
// This should be bitflags in wgpu-core
|
||||||
let host_map = match mode {
|
let host_map = match mode {
|
||||||
GPUMapModeConstants::READ => HostMap::Read,
|
GPUMapModeConstants::READ => HostMap::Read,
|
||||||
GPUMapModeConstants::WRITE => HostMap::Write,
|
GPUMapModeConstants::WRITE => HostMap::Write,
|
||||||
|
@ -248,13 +212,11 @@ impl GPUBufferMethods for GPUBuffer {
|
||||||
.dispatch_error(webgpu::Error::Validation(String::from(
|
.dispatch_error(webgpu::Error::Validation(String::from(
|
||||||
"Invalid MapModeFlags",
|
"Invalid MapModeFlags",
|
||||||
)));
|
)));
|
||||||
promise.reject_error(Error::Abort);
|
self.map_failure(&promise);
|
||||||
return promise;
|
return promise;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let map_range = offset..offset + range_size;
|
|
||||||
|
|
||||||
let sender = response_async(&promise, self);
|
let sender = response_async(&promise, self);
|
||||||
if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync {
|
if let Err(e) = self.channel.0.send(WebGPURequest::BufferMapAsync {
|
||||||
sender,
|
sender,
|
||||||
|
@ -262,80 +224,53 @@ impl GPUBufferMethods for GPUBuffer {
|
||||||
device_id: self.device.id().0,
|
device_id: self.device.id().0,
|
||||||
host_map,
|
host_map,
|
||||||
offset,
|
offset,
|
||||||
size: Some(range_size),
|
size,
|
||||||
}) {
|
}) {
|
||||||
warn!(
|
warn!(
|
||||||
"Failed to send BufferMapAsync ({:?}) ({})",
|
"Failed to send BufferMapAsync ({:?}) ({})",
|
||||||
self.buffer.0, e
|
self.buffer.0, e
|
||||||
);
|
);
|
||||||
promise.reject_error(Error::Operation);
|
self.map_failure(&promise);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
// Step 6
|
||||||
self.state.set(GPUBufferState::MappingPending);
|
|
||||||
*self.map_info.borrow_mut() = Some(GPUBufferMapInfo {
|
|
||||||
mapping: Arc::new(Mutex::new(Vec::with_capacity(0))),
|
|
||||||
mapping_range: map_range,
|
|
||||||
mapped_ranges: Vec::new(),
|
|
||||||
js_buffers: Vec::new(),
|
|
||||||
map_mode: Some(mode),
|
|
||||||
});
|
|
||||||
*self.map_promise.borrow_mut() = Some(promise.clone());
|
|
||||||
promise
|
promise
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-getmappedrange>
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-getmappedrange>
|
||||||
|
#[allow(unsafe_code)]
|
||||||
fn GetMappedRange(
|
fn GetMappedRange(
|
||||||
&self,
|
&self,
|
||||||
cx: JSContext,
|
_cx: JSContext,
|
||||||
offset: GPUSize64,
|
offset: GPUSize64,
|
||||||
size: Option<GPUSize64>,
|
size: Option<GPUSize64>,
|
||||||
) -> Fallible<ArrayBuffer> {
|
) -> Fallible<ArrayBuffer> {
|
||||||
let range_size = if let Some(s) = size {
|
let range_size = if let Some(s) = size {
|
||||||
s
|
s
|
||||||
} else if offset >= self.size {
|
|
||||||
return Err(Error::Operation);
|
|
||||||
} else {
|
} else {
|
||||||
self.size - offset
|
self.size.checked_sub(offset).unwrap_or(0)
|
||||||
};
|
};
|
||||||
let m_end = offset + range_size;
|
// Step 2: validation
|
||||||
let mut info = self.map_info.borrow_mut();
|
let mut mapping = self.mapping.borrow_mut();
|
||||||
let m_info = if let Some(m_info) = info.as_mut() {
|
let mapping = mapping.as_mut().ok_or(Error::Operation)?;
|
||||||
m_info
|
|
||||||
} else {
|
|
||||||
return Err(Error::Operation);
|
|
||||||
};
|
|
||||||
let mut valid = matches!(
|
|
||||||
self.state.get(),
|
|
||||||
GPUBufferState::Mapped | GPUBufferState::MappedAtCreation
|
|
||||||
);
|
|
||||||
|
|
||||||
valid &= offset % RANGE_OFFSET_ALIGN_MASK == 0 &&
|
let valid = offset % wgt::MAP_ALIGNMENT == 0 &&
|
||||||
range_size % RANGE_SIZE_ALIGN_MASK == 0 &&
|
range_size % wgt::COPY_BUFFER_ALIGNMENT == 0 &&
|
||||||
offset >= m_info.mapping_range.start &&
|
offset >= mapping.range.start &&
|
||||||
m_end <= m_info.mapping_range.end;
|
offset + range_size <= mapping.range.end;
|
||||||
valid &= m_info
|
|
||||||
.mapped_ranges
|
|
||||||
.iter()
|
|
||||||
.all(|range| range.start >= m_end || range.end <= offset);
|
|
||||||
if !valid {
|
if !valid {
|
||||||
return Err(Error::Operation);
|
return Err(Error::Operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
let heap_typed_array = create_new_external_array_buffer::<ArrayBufferU8>(
|
// Step 4
|
||||||
cx,
|
// only mapping.range is mapped with mapping.range.start at 0
|
||||||
Arc::clone(&m_info.mapping),
|
// so we need to rebase range to mapped.range
|
||||||
offset as usize,
|
let rebased_offset = (offset - mapping.range.start) as usize;
|
||||||
range_size as usize,
|
mapping
|
||||||
m_end as usize,
|
.data
|
||||||
);
|
.view(rebased_offset..rebased_offset + range_size as usize)
|
||||||
|
.map(|view| view.array_buffer())
|
||||||
let result = heap_typed_array.get_buffer().map_err(|_| Error::JSFailed);
|
.map_err(|()| Error::Operation)
|
||||||
|
|
||||||
m_info.mapped_ranges.push(offset..m_end);
|
|
||||||
m_info.js_buffers.push(heap_typed_array);
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label>
|
||||||
|
@ -347,30 +282,96 @@ impl GPUBufferMethods for GPUBuffer {
|
||||||
fn SetLabel(&self, value: USVString) {
|
fn SetLabel(&self, value: USVString) {
|
||||||
*self.label.borrow_mut() = value;
|
*self.label.borrow_mut() = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-size>
|
||||||
|
fn Size(&self) -> GPUSize64 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-usage>
|
||||||
|
fn Usage(&self) -> GPUFlagsConstant {
|
||||||
|
self.usage
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapstate>
|
||||||
|
fn MapState(&self) -> GPUBufferMapState {
|
||||||
|
// Step 1&2&3
|
||||||
|
if self.mapping.borrow().is_some() {
|
||||||
|
GPUBufferMapState::Mapped
|
||||||
|
} else if self.pending_map.borrow().is_some() {
|
||||||
|
GPUBufferMapState::Pending
|
||||||
|
} else {
|
||||||
|
GPUBufferMapState::Unmapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GPUBuffer {
|
||||||
|
fn map_failure(&self, p: &Rc<Promise>) {
|
||||||
|
let mut pending_map = self.pending_map.borrow_mut();
|
||||||
|
// Step 1
|
||||||
|
if pending_map.as_ref() != Some(p) {
|
||||||
|
assert!(p.is_rejected());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Step 2
|
||||||
|
assert!(p.is_pending());
|
||||||
|
// Step 3
|
||||||
|
pending_map.take();
|
||||||
|
// Step 4
|
||||||
|
if self.device.is_lost() {
|
||||||
|
p.reject_error(Error::Abort);
|
||||||
|
} else {
|
||||||
|
p.reject_error(Error::Operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_success(&self, p: &Rc<Promise>, wgpu_mapping: Mapping) {
|
||||||
|
let mut pending_map = self.pending_map.borrow_mut();
|
||||||
|
|
||||||
|
// Step 1
|
||||||
|
if pending_map.as_ref() != Some(p) {
|
||||||
|
assert!(p.is_rejected());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
assert!(p.is_pending());
|
||||||
|
|
||||||
|
// Step 4
|
||||||
|
let mapping = ActiveBufferMapping::new(
|
||||||
|
match wgpu_mapping.mode {
|
||||||
|
HostMap::Read => GPUMapModeConstants::READ,
|
||||||
|
HostMap::Write => GPUMapModeConstants::WRITE,
|
||||||
|
},
|
||||||
|
wgpu_mapping.range,
|
||||||
|
);
|
||||||
|
|
||||||
|
match mapping {
|
||||||
|
Err(error) => {
|
||||||
|
*pending_map = None;
|
||||||
|
p.reject_error(error.clone());
|
||||||
|
},
|
||||||
|
Ok(mut mapping) => {
|
||||||
|
// Step 5
|
||||||
|
mapping.data.load(&wgpu_mapping.data);
|
||||||
|
// Step 6
|
||||||
|
self.mapping.borrow_mut().replace(mapping);
|
||||||
|
// Step 7
|
||||||
|
pending_map.take();
|
||||||
|
p.resolve_native(&());
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncWGPUListener for GPUBuffer {
|
impl AsyncWGPUListener for GPUBuffer {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) {
|
fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) {
|
||||||
match response {
|
match response {
|
||||||
WebGPUResponse::BufferMapAsync(Ok(bytes)) => {
|
WebGPUResponse::BufferMapAsync(Ok(mapping)) => self.map_success(promise, mapping),
|
||||||
*self
|
WebGPUResponse::BufferMapAsync(Err(_)) => self.map_failure(promise),
|
||||||
.map_info
|
_ => unreachable!("Wrong response received on AsyncWGPUListener for GPUBuffer"),
|
||||||
.borrow_mut()
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.mapping
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.as_mut() = bytes.to_vec();
|
|
||||||
promise.resolve_native(&());
|
|
||||||
self.state.set(GPUBufferState::Mapped);
|
|
||||||
},
|
|
||||||
WebGPUResponse::BufferMapAsync(Err(e)) => {
|
|
||||||
warn!("Could not map buffer({:?})", e);
|
|
||||||
promise.reject_error(Error::Abort);
|
|
||||||
},
|
|
||||||
_ => unreachable!("GPUBuffer received wrong WebGPUResponse"),
|
|
||||||
}
|
}
|
||||||
*self.map_promise.borrow_mut() = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::hash::{Hash, Hasher};
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use webgpu::{WebGPU, WebGPUCommandBuffer, WebGPURequest};
|
use webgpu::{WebGPU, WebGPUCommandBuffer, WebGPURequest};
|
||||||
|
|
||||||
use crate::dom::bindings::cell::{DomRefCell, Ref};
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUCommandBufferMethods;
|
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUCommandBufferMethods;
|
||||||
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
|
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
|
@ -89,10 +89,6 @@ impl GPUCommandBuffer {
|
||||||
pub fn id(&self) -> WebGPUCommandBuffer {
|
pub fn id(&self) -> WebGPUCommandBuffer {
|
||||||
self.command_buffer
|
self.command_buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffers(&self) -> Ref<HashSet<Dom<GPUBuffer>>> {
|
|
||||||
self.buffers.borrow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUCommandBufferMethods for GPUCommandBuffer {
|
impl GPUCommandBufferMethods for GPUCommandBuffer {
|
||||||
|
|
|
@ -9,7 +9,6 @@ use std::cell::Cell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use js::jsapi::{Heap, JSObject};
|
use js::jsapi::{Heap, JSObject};
|
||||||
|
@ -24,7 +23,9 @@ use webgpu::{
|
||||||
WebGPUResponse,
|
WebGPUResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::bindings::codegen::Bindings::WebGPUBinding::{GPUPipelineErrorReason, GPUTextureFormat};
|
use super::bindings::codegen::Bindings::WebGPUBinding::{
|
||||||
|
GPUMapModeConstants, GPUPipelineErrorReason, GPUTextureFormat,
|
||||||
|
};
|
||||||
use super::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
|
use super::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode;
|
||||||
use super::bindings::error::Fallible;
|
use super::bindings::error::Fallible;
|
||||||
use super::gpu::AsyncWGPUListener;
|
use super::gpu::AsyncWGPUListener;
|
||||||
|
@ -55,7 +56,7 @@ use crate::dom::gpu::response_async;
|
||||||
use crate::dom::gpuadapter::GPUAdapter;
|
use crate::dom::gpuadapter::GPUAdapter;
|
||||||
use crate::dom::gpubindgroup::GPUBindGroup;
|
use crate::dom::gpubindgroup::GPUBindGroup;
|
||||||
use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
|
use crate::dom::gpubindgrouplayout::GPUBindGroupLayout;
|
||||||
use crate::dom::gpubuffer::{GPUBuffer, GPUBufferMapInfo, GPUBufferState};
|
use crate::dom::gpubuffer::{ActiveBufferMapping, GPUBuffer};
|
||||||
use crate::dom::gpucommandencoder::GPUCommandEncoder;
|
use crate::dom::gpucommandencoder::GPUCommandEncoder;
|
||||||
use crate::dom::gpucomputepipeline::GPUComputePipeline;
|
use crate::dom::gpucomputepipeline::GPUComputePipeline;
|
||||||
use crate::dom::gpuconvert::{
|
use crate::dom::gpuconvert::{
|
||||||
|
@ -213,6 +214,10 @@ impl GPUDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_lost(&self) -> bool {
|
||||||
|
self.lost_promise.borrow().is_fulfilled()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_pipeline_layout_data(
|
fn get_pipeline_layout_data(
|
||||||
&self,
|
&self,
|
||||||
layout: &GPUPipelineLayoutOrGPUAutoLayoutMode,
|
layout: &GPUPipelineLayoutOrGPUAutoLayoutMode,
|
||||||
|
@ -452,31 +457,23 @@ impl GPUDeviceMethods for GPUDevice {
|
||||||
.expect("Failed to create WebGPU buffer");
|
.expect("Failed to create WebGPU buffer");
|
||||||
|
|
||||||
let buffer = webgpu::WebGPUBuffer(id);
|
let buffer = webgpu::WebGPUBuffer(id);
|
||||||
let map_info;
|
let mapping = if descriptor.mappedAtCreation {
|
||||||
let state;
|
Some(ActiveBufferMapping::new(
|
||||||
if descriptor.mappedAtCreation {
|
GPUMapModeConstants::WRITE,
|
||||||
let buf_data = vec![0u8; descriptor.size as usize];
|
0..descriptor.size,
|
||||||
map_info = DomRefCell::new(Some(GPUBufferMapInfo {
|
)?)
|
||||||
mapping: Arc::new(Mutex::new(buf_data)),
|
|
||||||
mapping_range: 0..descriptor.size,
|
|
||||||
mapped_ranges: Vec::new(),
|
|
||||||
js_buffers: Vec::new(),
|
|
||||||
map_mode: None,
|
|
||||||
}));
|
|
||||||
state = GPUBufferState::MappedAtCreation;
|
|
||||||
} else {
|
} else {
|
||||||
map_info = DomRefCell::new(None);
|
None
|
||||||
state = GPUBufferState::Unmapped;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
Ok(GPUBuffer::new(
|
Ok(GPUBuffer::new(
|
||||||
&self.global(),
|
&self.global(),
|
||||||
self.channel.clone(),
|
self.channel.clone(),
|
||||||
buffer,
|
buffer,
|
||||||
self,
|
self,
|
||||||
state,
|
|
||||||
descriptor.size,
|
descriptor.size,
|
||||||
map_info,
|
descriptor.usage,
|
||||||
|
mapping,
|
||||||
descriptor.parent.label.clone(),
|
descriptor.parent.label.clone(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
use crate::dom::bindings::str::USVString;
|
use crate::dom::bindings::str::USVString;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState};
|
use crate::dom::gpubuffer::GPUBuffer;
|
||||||
use crate::dom::gpucommandbuffer::GPUCommandBuffer;
|
use crate::dom::gpucommandbuffer::GPUCommandBuffer;
|
||||||
use crate::dom::gpuconvert::{
|
use crate::dom::gpuconvert::{
|
||||||
convert_ic_texture, convert_image_data_layout, convert_texture_size_to_dict,
|
convert_ic_texture, convert_image_data_layout, convert_texture_size_to_dict,
|
||||||
|
@ -80,21 +80,6 @@ impl GPUQueueMethods for GPUQueue {
|
||||||
|
|
||||||
/// <https://gpuweb.github.io/gpuweb/#dom-gpuqueue-submit>
|
/// <https://gpuweb.github.io/gpuweb/#dom-gpuqueue-submit>
|
||||||
fn Submit(&self, command_buffers: Vec<DomRoot<GPUCommandBuffer>>) {
|
fn Submit(&self, command_buffers: Vec<DomRoot<GPUCommandBuffer>>) {
|
||||||
let valid = command_buffers.iter().all(|cb| {
|
|
||||||
cb.buffers()
|
|
||||||
.iter()
|
|
||||||
.all(|b| matches!(b.state(), GPUBufferState::Unmapped))
|
|
||||||
});
|
|
||||||
if !valid {
|
|
||||||
self.device
|
|
||||||
.borrow()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.dispatch_error(webgpu::Error::Validation(String::from(
|
|
||||||
"Referenced GPUBuffer(s) are not Unmapped",
|
|
||||||
)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let command_buffers = command_buffers.iter().map(|cb| cb.id().0).collect();
|
let command_buffers = command_buffers.iter().map(|cb| cb.id().0).collect();
|
||||||
self.channel
|
self.channel
|
||||||
.0
|
.0
|
||||||
|
|
|
@ -52,6 +52,12 @@ pub struct Promise {
|
||||||
permanent_js_root: Heap<JSVal>,
|
permanent_js_root: Heap<JSVal>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Promise {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.reflector == other.reflector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Private helper to enable adding new methods to `Rc<Promise>`.
|
/// Private helper to enable adding new methods to `Rc<Promise>`.
|
||||||
trait PromiseHelper {
|
trait PromiseHelper {
|
||||||
fn initialize(&self, cx: SafeJSContext);
|
fn initialize(&self, cx: SafeJSContext);
|
||||||
|
@ -231,6 +237,18 @@ impl Promise {
|
||||||
matches!(state, PromiseState::Rejected | PromiseState::Fulfilled)
|
matches!(state, PromiseState::Rejected | PromiseState::Fulfilled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn is_rejected(&self) -> bool {
|
||||||
|
let state = unsafe { GetPromiseState(self.promise_obj()) };
|
||||||
|
matches!(state, PromiseState::Rejected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn is_pending(&self) -> bool {
|
||||||
|
let state = unsafe { GetPromiseState(self.promise_obj()) };
|
||||||
|
matches!(state, PromiseState::Pending)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn promise_obj(&self) -> HandleObject {
|
pub fn promise_obj(&self) -> HandleObject {
|
||||||
let obj = self.reflector().get_jsobject();
|
let obj = self.reflector().get_jsobject();
|
||||||
|
|
|
@ -165,17 +165,28 @@ GPUDevice includes GPUObjectBase;
|
||||||
|
|
||||||
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
|
[Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"]
|
||||||
interface GPUBuffer {
|
interface GPUBuffer {
|
||||||
|
readonly attribute GPUSize64Out size;
|
||||||
|
readonly attribute GPUFlagsConstant usage;
|
||||||
|
|
||||||
|
readonly attribute GPUBufferMapState mapState;
|
||||||
|
|
||||||
[NewObject]
|
[NewObject]
|
||||||
Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
|
Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
|
||||||
[NewObject, Throws]
|
[NewObject, Throws]
|
||||||
ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
|
ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
|
||||||
[Throws]
|
|
||||||
undefined unmap();
|
undefined unmap();
|
||||||
[Throws]
|
|
||||||
undefined destroy();
|
undefined destroy();
|
||||||
};
|
};
|
||||||
GPUBuffer includes GPUObjectBase;
|
GPUBuffer includes GPUObjectBase;
|
||||||
|
|
||||||
|
|
||||||
|
enum GPUBufferMapState {
|
||||||
|
"unmapped",
|
||||||
|
"pending",
|
||||||
|
"mapped",
|
||||||
|
};
|
||||||
|
|
||||||
dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
|
dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
|
||||||
required GPUSize64 size;
|
required GPUSize64 size;
|
||||||
required GPUBufferUsageFlags usage;
|
required GPUBufferUsageFlags usage;
|
||||||
|
@ -184,24 +195,24 @@ dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
|
||||||
|
|
||||||
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
|
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
|
||||||
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
|
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
|
||||||
interface GPUBufferUsage {
|
namespace GPUBufferUsage {
|
||||||
const GPUBufferUsageFlags MAP_READ = 0x0001;
|
const GPUFlagsConstant MAP_READ = 0x0001;
|
||||||
const GPUBufferUsageFlags MAP_WRITE = 0x0002;
|
const GPUFlagsConstant MAP_WRITE = 0x0002;
|
||||||
const GPUBufferUsageFlags COPY_SRC = 0x0004;
|
const GPUFlagsConstant COPY_SRC = 0x0004;
|
||||||
const GPUBufferUsageFlags COPY_DST = 0x0008;
|
const GPUFlagsConstant COPY_DST = 0x0008;
|
||||||
const GPUBufferUsageFlags INDEX = 0x0010;
|
const GPUFlagsConstant INDEX = 0x0010;
|
||||||
const GPUBufferUsageFlags VERTEX = 0x0020;
|
const GPUFlagsConstant VERTEX = 0x0020;
|
||||||
const GPUBufferUsageFlags UNIFORM = 0x0040;
|
const GPUFlagsConstant UNIFORM = 0x0040;
|
||||||
const GPUBufferUsageFlags STORAGE = 0x0080;
|
const GPUFlagsConstant STORAGE = 0x0080;
|
||||||
const GPUBufferUsageFlags INDIRECT = 0x0100;
|
const GPUFlagsConstant INDIRECT = 0x0100;
|
||||||
const GPUBufferUsageFlags QUERY_RESOLVE = 0x0200;
|
const GPUFlagsConstant QUERY_RESOLVE = 0x0200;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef [EnforceRange] unsigned long GPUMapModeFlags;
|
typedef [EnforceRange] unsigned long GPUMapModeFlags;
|
||||||
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
|
[Exposed=(Window, DedicatedWorker), Pref="dom.webgpu.enabled"]
|
||||||
interface GPUMapMode {
|
namespace GPUMapMode {
|
||||||
const GPUMapModeFlags READ = 0x0001;
|
const GPUFlagsConstant READ = 0x0001;
|
||||||
const GPUMapModeFlags WRITE = 0x0002;
|
const GPUFlagsConstant WRITE = 0x0002;
|
||||||
};
|
};
|
||||||
|
|
||||||
[Exposed=(Window, DedicatedWorker), Serializable , Pref="dom.webgpu.enabled"]
|
[Exposed=(Window, DedicatedWorker), Serializable , Pref="dom.webgpu.enabled"]
|
||||||
|
|
|
@ -267,9 +267,8 @@ pub enum WebGPURequest {
|
||||||
},
|
},
|
||||||
UnmapBuffer {
|
UnmapBuffer {
|
||||||
buffer_id: id::BufferId,
|
buffer_id: id::BufferId,
|
||||||
device_id: id::DeviceId,
|
|
||||||
array_buffer: IpcSharedMemory,
|
array_buffer: IpcSharedMemory,
|
||||||
is_map_read: bool,
|
write_back: bool,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
size: u64,
|
size: u64,
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,10 +4,13 @@
|
||||||
|
|
||||||
//! IPC messages that are send to WebGPU DOM objects.
|
//! IPC messages that are send to WebGPU DOM objects.
|
||||||
|
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
use ipc_channel::ipc::IpcSharedMemory;
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use wgc::id;
|
use wgc::id;
|
||||||
use wgc::pipeline::CreateShaderModuleError;
|
use wgc::pipeline::CreateShaderModuleError;
|
||||||
|
use wgpu_core::device::HostMap;
|
||||||
use wgpu_core::instance::{RequestAdapterError, RequestDeviceError};
|
use wgpu_core::instance::{RequestAdapterError, RequestDeviceError};
|
||||||
use wgpu_core::resource::BufferAccessError;
|
use wgpu_core::resource::BufferAccessError;
|
||||||
pub use {wgpu_core as wgc, wgpu_types as wgt};
|
pub use {wgpu_core as wgc, wgpu_types as wgt};
|
||||||
|
@ -72,6 +75,13 @@ pub struct Pipeline<T: std::fmt::Debug + Serialize> {
|
||||||
pub label: String,
|
pub label: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Mapping {
|
||||||
|
pub data: IpcSharedMemory,
|
||||||
|
pub mode: HostMap,
|
||||||
|
pub range: Range<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum WebGPUResponse {
|
pub enum WebGPUResponse {
|
||||||
|
@ -85,7 +95,7 @@ pub enum WebGPUResponse {
|
||||||
Result<wgt::DeviceDescriptor<Option<String>>, RequestDeviceError>,
|
Result<wgt::DeviceDescriptor<Option<String>>, RequestDeviceError>,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BufferMapAsync(Result<IpcSharedMemory, BufferAccessError>),
|
BufferMapAsync(Result<Mapping, BufferAccessError>),
|
||||||
SubmittedWorkDone,
|
SubmittedWorkDone,
|
||||||
PoppedErrorScope(Result<Option<Error>, PopError>),
|
PoppedErrorScope(Result<Option<Error>, PopError>),
|
||||||
CompilationInfo(Option<ShaderCompilationInfo>),
|
CompilationInfo(Option<ShaderCompilationInfo>),
|
||||||
|
|
|
@ -40,8 +40,8 @@ use crate::gpu_error::ErrorScope;
|
||||||
use crate::poll_thread::Poller;
|
use crate::poll_thread::Poller;
|
||||||
use crate::render_commands::apply_render_command;
|
use crate::render_commands::apply_render_command;
|
||||||
use crate::{
|
use crate::{
|
||||||
Adapter, ComputePassId, Error, Pipeline, PopError, PresentationData, RenderPassId, WebGPU,
|
Adapter, ComputePassId, Error, Mapping, Pipeline, PopError, PresentationData, RenderPassId,
|
||||||
WebGPUAdapter, WebGPUDevice, WebGPUMsg, WebGPUQueue, WebGPURequest, WebGPUResponse,
|
WebGPU, WebGPUAdapter, WebGPUDevice, WebGPUMsg, WebGPUQueue, WebGPURequest, WebGPUResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PRESENTATION_BUFFER_COUNT: usize = 10;
|
pub const PRESENTATION_BUFFER_COUNT: usize = 10;
|
||||||
|
@ -191,11 +191,11 @@ impl WGPU {
|
||||||
let callback = BufferMapCallback::from_rust(Box::from(
|
let callback = BufferMapCallback::from_rust(Box::from(
|
||||||
move |result: BufferAccessResult| {
|
move |result: BufferAccessResult| {
|
||||||
drop(token);
|
drop(token);
|
||||||
let response = result.map(|_| {
|
let response = result.and_then(|_| {
|
||||||
let global = &glob;
|
let global = &glob;
|
||||||
let (slice_pointer, range_size) = gfx_select!(buffer_id =>
|
let (slice_pointer, range_size) = gfx_select!(buffer_id =>
|
||||||
global.buffer_get_mapped_range(buffer_id, 0, None))
|
global.buffer_get_mapped_range(buffer_id, offset, size))
|
||||||
.unwrap();
|
?;
|
||||||
// SAFETY: guarantee to be safe from wgpu
|
// SAFETY: guarantee to be safe from wgpu
|
||||||
let data = unsafe {
|
let data = unsafe {
|
||||||
slice::from_raw_parts(
|
slice::from_raw_parts(
|
||||||
|
@ -204,7 +204,11 @@ impl WGPU {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
IpcSharedMemory::from_bytes(data)
|
Ok(Mapping {
|
||||||
|
data: IpcSharedMemory::from_bytes(data),
|
||||||
|
range: offset..offset + range_size,
|
||||||
|
mode: host_map,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
if let Err(e) =
|
if let Err(e) =
|
||||||
resp_sender.send(WebGPUResponse::BufferMapAsync(response))
|
resp_sender.send(WebGPUResponse::BufferMapAsync(response))
|
||||||
|
@ -226,13 +230,6 @@ impl WGPU {
|
||||||
operation
|
operation
|
||||||
));
|
));
|
||||||
self.poller.wake();
|
self.poller.wake();
|
||||||
if let Err(e) = &result {
|
|
||||||
if let Err(w) =
|
|
||||||
sender.send(WebGPUResponse::BufferMapAsync(Err(e.to_owned())))
|
|
||||||
{
|
|
||||||
warn!("Failed to send BufferMapAsync Response ({:?})", w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Per spec we also need to raise validation error here
|
// Per spec we also need to raise validation error here
|
||||||
self.maybe_dispatch_wgpu_error(device_id, result.err());
|
self.maybe_dispatch_wgpu_error(device_id, result.err());
|
||||||
},
|
},
|
||||||
|
@ -1208,21 +1205,20 @@ impl WGPU {
|
||||||
},
|
},
|
||||||
WebGPURequest::UnmapBuffer {
|
WebGPURequest::UnmapBuffer {
|
||||||
buffer_id,
|
buffer_id,
|
||||||
device_id,
|
|
||||||
array_buffer,
|
array_buffer,
|
||||||
is_map_read,
|
write_back,
|
||||||
offset,
|
offset,
|
||||||
size,
|
size,
|
||||||
} => {
|
} => {
|
||||||
let global = &self.global;
|
let global = &self.global;
|
||||||
if !is_map_read {
|
if write_back {
|
||||||
let (slice_pointer, range_size) =
|
if let Ok((slice_pointer, range_size)) = gfx_select!(
|
||||||
gfx_select!(buffer_id => global.buffer_get_mapped_range(
|
buffer_id => global.buffer_get_mapped_range(
|
||||||
buffer_id,
|
buffer_id,
|
||||||
offset,
|
offset,
|
||||||
Some(size)
|
Some(size)
|
||||||
))
|
)
|
||||||
.unwrap();
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
slice::from_raw_parts_mut(
|
slice::from_raw_parts_mut(
|
||||||
slice_pointer.as_ptr(),
|
slice_pointer.as_ptr(),
|
||||||
|
@ -1231,8 +1227,9 @@ impl WGPU {
|
||||||
}
|
}
|
||||||
.copy_from_slice(&array_buffer);
|
.copy_from_slice(&array_buffer);
|
||||||
}
|
}
|
||||||
let result = gfx_select!(buffer_id => global.buffer_unmap(buffer_id));
|
}
|
||||||
self.maybe_dispatch_wgpu_error(device_id, result.err());
|
// Ignore result because this operation always succeed from user perspective
|
||||||
|
let _result = gfx_select!(buffer_id => global.buffer_unmap(buffer_id));
|
||||||
},
|
},
|
||||||
WebGPURequest::WriteBuffer {
|
WebGPURequest::WriteBuffer {
|
||||||
device_id,
|
device_id,
|
||||||
|
|
1129
tests/wpt/webgpu/meta/webgpu/cts.https.html.ini
vendored
1129
tests/wpt/webgpu/meta/webgpu/cts.https.html.ini
vendored
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue