Skip to content

Commit 23f0db8

Browse files
committed
wip
1 parent c3ceae9 commit 23f0db8

15 files changed

Lines changed: 3083 additions & 1 deletion

File tree

Cargo.lock

Lines changed: 29 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ members = [
2424
"wgpu-types",
2525
"wgpu",
2626
"xtask",
27+
"wgpu-remote",
2728
]
2829
exclude = []
2930
default-members = [

wgpu-remote/Cargo.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[package]
2+
name = "wgpu-remote"
3+
edition.workspace = true
4+
rust-version.workspace = true
5+
keywords.workspace = true
6+
license.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
version.workspace = true
10+
authors.workspace = true
11+
12+
[features]
13+
default = ["serde"]
14+
15+
[[bin]]
16+
name = "parse-webidl"
17+
path = "webidl/main.rs"
18+
19+
[dependencies]
20+
hashbrown = { workspace = true }
21+
weedle = "0.13"
22+
23+
[dependencies.serde]
24+
optional = true
25+
version = "1.0"
26+
features = ["derive"]
27+
28+
[lints]
29+
workspace = true

wgpu-remote/rust-toolchain.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[toolchain]
2+
channel = "nightly"
3+
components = ["rustfmt", "clippy", "miri"]

wgpu-remote/src/block.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//! Definition of the [`Block`] type.
2+
3+
use std::mem::ManuallyDrop;
4+
5+
/// A block of bytes, with a callback to free it.
6+
///
7+
/// A `Block` represents a slice of bytes managed by an external allocator. The
8+
/// `Block` also keeps a callback that will return the bytes to that external
9+
/// allocator when the `Block` is dropped.
10+
///
11+
/// This crate uses `Block`s to track regions of shared memory obtained from a
12+
/// [`Sender`] implementation, subdividing that into smaller allocations for
13+
/// individual `wgpu` buffers using [`Pool`].
14+
///
15+
/// [`Sender`]: crate::transport::Sender
16+
/// [`Pool`]: crate::Pool
17+
//
18+
// If `allocator_api` were stabilized, then this could just be `Box<[u8]>`.
19+
pub struct Block {
20+
/// A block of bytes, aligned on an eight-byte boundary.
21+
// TODO: use NonNull?
22+
bytes: *mut [u8],
23+
24+
/// The function to which `bytes` should be passed when this `Block` is dropped.
25+
// TODO: make the allocator a type parameter to `Block`.
26+
free: ManuallyDrop<Box<dyn FnOnce(*mut [u8]) + Send + 'static>>,
27+
}
28+
29+
/// Safety: Raw pointers like `Block::bytes` are not `Send` by
30+
/// default, but we only expose `bytes` when given a `&mut self`, so
31+
/// it should be fine.
32+
unsafe impl Send for Block {}
33+
unsafe impl Sync for Block {}
34+
35+
impl Block {
36+
/// Create a [`Block`] tracking `bytes`.
37+
///
38+
/// When the returned `Block` is dropped, it will call `free`,
39+
/// passing it the pointer to the block's bytes.
40+
///
41+
/// # Safety
42+
///
43+
/// The given `bytes` pointer must be valid for reading and writing the
44+
/// entire slice. It must remain so until it is passed to `free` by the new
45+
/// `Block`'s [`Drop`] implementation.
46+
///
47+
/// The given `bytes` must not be accessed through any other pointer until
48+
/// after the new `Block` is dropped.
49+
pub unsafe fn new(bytes: *mut [u8], free: impl FnOnce(*mut [u8]) + Send + 'static) -> Block {
50+
Self {
51+
bytes,
52+
free: ManuallyDrop::new(Box::new(free)),
53+
}
54+
}
55+
56+
/// Return the length in bytes of the slice this `Block` manages.
57+
#[inline]
58+
#[expect(clippy::len_without_is_empty)]
59+
pub fn len(&self) -> usize {
60+
self.bytes.len()
61+
}
62+
63+
/// Return a pointer to the memory this `Block` manages.
64+
///
65+
/// The returned memory remains allocated until this `Block` is dropped.
66+
#[inline]
67+
pub fn bytes_mut(&self) -> *mut [u8] {
68+
self.bytes
69+
}
70+
}
71+
72+
impl Drop for Block {
73+
fn drop(&mut self) {
74+
// Safety: We are in the `Drop` method, so `self.free` will
75+
// never be used after we return, and this method doesn't use
76+
// it either.
77+
let free = unsafe { ManuallyDrop::take(&mut self.free) };
78+
(free)(self.bytes);
79+
}
80+
}
81+
82+
#[cfg(test)]
83+
impl Block {
84+
pub fn from_vec(vec: Vec<u8>) -> Block {
85+
let slice = vec.into_boxed_slice();
86+
let slice_ptr = Box::into_raw(slice);
87+
let free = |slice_ptr| {
88+
// Safety: `slice_ptr` was obtained from a `Vec` in the first place.
89+
drop(Vec::from(unsafe { Box::from_raw(slice_ptr) }));
90+
};
91+
92+
// Safety: `free` is designed to receive a `*mut [u8]` obtained from a
93+
// `Vec<u8>`.
94+
unsafe { Self::new(slice_ptr, free) }
95+
}
96+
}
97+
98+
/// `Block` implements `Send`.
99+
#[test]
100+
fn block_is_send() {
101+
fn require_send<T: Send>(_t: T) {}
102+
let block = unsafe { Block::new(&mut [], |_bytes| ()) };
103+
require_send(block);
104+
}
105+
106+
/// When a `Block` is dropped, the callback gets invoked.
107+
#[test]
108+
fn drop_called() {
109+
use std::sync::Arc;
110+
fn require_send<T: Send>(_t: T) {}
111+
112+
let counter = Arc::new(());
113+
114+
// Brute force the `Arc<()>` into a `*mut [u8]`.
115+
let bytes = Arc::into_raw(Arc::clone(&counter)) as *mut ();
116+
let bytes = std::ptr::slice_from_raw_parts_mut(bytes as *mut u8, 0);
117+
118+
let free_bytes = move |bytes| {
119+
// Brute force the `*mut [u8]` back into an `Arc<()>`.
120+
let bytes = bytes as *mut u8 as *mut ();
121+
let clone_of_counter = unsafe { Arc::from_raw(bytes) };
122+
drop(clone_of_counter);
123+
};
124+
let block = unsafe { Block::new(bytes, free_bytes) };
125+
assert_eq!(Arc::strong_count(&counter), 2);
126+
require_send(block);
127+
assert_eq!(Arc::strong_count(&counter), 1);
128+
}

wgpu-remote/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*! Process isolation for wgpu.
2+
3+
This crate implements a backend for `wgpu` that forwards operations
4+
across an inter-process communication channel, so that the actual GPU
5+
access takes place in a different process from the code driving the
6+
`wgpu` API. You can use this to isolate GPU access to improve security
7+
or robustness, or perhaps, in the other direction, to isolate the code
8+
using wgpu.
9+
10+
Code using the `wgpu` API with this crate as a backend is considered
11+
to be the "client", and the code responding to its requests and
12+
driving the GPU is considered the "server".
13+
14+
The client and server communicate via implementations of the
15+
[`transport`] module's [`Sender`] and [`Receiver`] traits. These are
16+
meant to be easily implemented on most platforms, or integrated with
17+
an application's existing IPC mechanisms. They provide a low level
18+
interface, with methods for creating blocks of shared memory, and
19+
exchanging messages held in that memory. This crate also provides a
20+
few basic implementations of `Sender` and `Receiver`; see the
21+
[`transport`] module documentation for details.
22+
23+
This crate requires the client and server to be able to share memory.
24+
It is probably not suitable for allowing the two parties to run on
25+
entirely different machines and communicate over a network.
26+
27+
[`Sender`]: transport::Sender
28+
[`Receiver`]: transport::Receiver
29+
30+
*/
31+
32+
mod block;
33+
mod pool;
34+
pub mod transport;
35+
36+
pub use block::Block;
37+
pub use pool::Pool;

0 commit comments

Comments
 (0)