aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings
diff options
context:
space:
mode:
authorSamson <16504129+sagudev@users.noreply.github.com>2024-08-27 09:54:55 +0200
committerGitHub <noreply@github.com>2024-08-27 07:54:55 +0000
commit7fce24f9d54a015db7ee813fc16c74d2feacbb12 (patch)
treee344ef00f1f1b99b59158b789c064fda52b495b6 /components/script/dom/bindings
parentbb5926b3291877fb47d44ea376881c2d52701bbf (diff)
downloadservo-7fce24f9d54a015db7ee813fc16c74d2feacbb12.tar.gz
servo-7fce24f9d54a015db7ee813fc16c74d2feacbb12.zip
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>
Diffstat (limited to 'components/script/dom/bindings')
-rw-r--r--components/script/dom/bindings/buffer_source.rs142
-rw-r--r--components/script/dom/bindings/trace.rs2
2 files changed, 103 insertions, 41 deletions
diff --git a/components/script/dom/bindings/buffer_source.rs b/components/script/dom/bindings/buffer_source.rs
index 71c5051accd..5af00da3a45 100644
--- a/components/script/dom/bindings/buffer_source.rs
+++ b/components/script/dom/bindings/buffer_source.rs
@@ -4,18 +4,23 @@
#![allow(unsafe_code)]
-use std::borrow::BorrowMut;
use std::ffi::c_void;
use std::marker::PhantomData;
+use std::ops::Range;
use std::ptr;
-use std::sync::{Arc, Mutex};
+use std::sync::Arc;
-use js::jsapi::glue::NewExternalArrayBuffer;
-use js::jsapi::{Heap, JSObject, JS_GetArrayBufferViewBuffer, JS_IsArrayBufferViewObject};
+use js::jsapi::{
+ Heap, JSObject, JS_GetArrayBufferViewBuffer, JS_IsArrayBufferViewObject, NewExternalArrayBuffer,
+};
use js::rust::wrappers::DetachArrayBuffer;
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;
/// <https://webidl.spec.whatwg.org/#BufferSource>
@@ -402,44 +407,103 @@ where
}
}
-pub fn create_new_external_array_buffer<T>(
- cx: JSContext,
- mapping: Arc<Mutex<Vec<T::Element>>>,
- offset: usize,
- range_size: usize,
- m_end: usize,
-) -> HeapBufferSource<T>
-where
- T: TypedArrayElement + TypedArrayElementCreator,
- T::Element: Clone + Copy,
-{
- /// `freeFunc()` must be threadsafe, should be safely callable from any thread
- /// 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) {
- // 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
- // a generic type in this parameter.
- #[allow(clippy::from_raw_with_void_ptr)]
- let _ = Arc::from_raw(free_user_data as *const _);
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct DataBlock {
+ #[ignore_malloc_size_of = "Arc"]
+ data: Arc<Box<[u8]>>,
+ /// Data views (mutable subslices of data)
+ data_views: Vec<DataView>,
+}
+
+/// Returns true if two non-inclusive ranges overlap
+// 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(),
+ }
}
- unsafe {
- let mapping_slice_ptr = mapping.lock().unwrap().borrow_mut()[offset..m_end].as_mut_ptr();
+ /// 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)
+ }
- // 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,
- range_size,
- mapping_slice_ptr as _,
- Some(free_func),
- Arc::into_raw(mapping) as _,
- ));
+ /// 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()
+ }
- HeapBufferSource {
- buffer_source: BufferSource::ArrayBuffer(Heap::boxed(*array_buffer)),
- phantom: PhantomData,
+ 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
+ /// without causing conflicts, unexpected behavior.
+ 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
+ // the exact same line to fix it. Doing the cast is tricky because of the use of
+ // a generic type in this parameter.
+ #[allow(clippy::from_raw_with_void_ptr)]
+ drop(Arc::from_raw(free_user_data as *const _));
}
+ let raw: *mut Box<[u8]> = Arc::into_raw(Arc::clone(&self.data)) as _;
+ rooted!(in(*cx) let object = unsafe {
+ NewExternalArrayBuffer(
+ *cx,
+ range.end - range.start,
+ // SAFETY: This is safe because we have checked there is no overlapping view
+ (*raw)[range.clone()].as_mut_ptr() as _,
+ Some(free_func),
+ raw as _,
+ )
+ });
+ self.data_views.push(DataView {
+ range,
+ 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())
+ })
}
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index cc5462f9dc1..ae05cf36ec4 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -61,7 +61,6 @@ use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString};
-use crate::dom::gpubuffer::GPUBufferState;
use crate::dom::gpucanvascontext::WebGPUContextId;
use crate::dom::htmlimageelement::SourceSet;
use crate::dom::htmlmediaelement::HTMLMediaElementFetchContext;
@@ -366,7 +365,6 @@ unsafe_no_jsmanaged_fields!(WindowProxyHandler);
unsafe_no_jsmanaged_fields!(DOMString);
unsafe_no_jsmanaged_fields!(USVString);
unsafe_no_jsmanaged_fields!(WebGPUContextId);
-unsafe_no_jsmanaged_fields!(GPUBufferState);
unsafe_no_jsmanaged_fields!(SourceSet);
unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext);
unsafe_no_jsmanaged_fields!(StreamConsumer);