Skip to content

Commit

Permalink
[rs] prototype of async/await for buffer mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
swiftcoder committed Nov 22, 2019
1 parent 1968eb8 commit a85f95d
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 73 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ Cargo.lock
# Other
.DS_Store

# VSCode project
.vscode

# Output from capture example
red.png
1 change: 1 addition & 0 deletions wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ log = "0.4"
png = "0.15"
winit = "0.20.0-alpha4"
zerocopy = "0.2"
futures = "0.3"
30 changes: 15 additions & 15 deletions wgpu/examples/capture/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use std::fs::File;
use std::mem::size_of;

fn main() {
async fn run() {
env_logger::init();

let adapter = wgpu::Adapter::request(
Expand Down Expand Up @@ -86,21 +86,21 @@ fn main() {
queue.submit(&[command_buffer]);

// Write the buffer as a PNG
output_buffer.map_read_async(
0,
(size * size) as usize * size_of::<u32>(),
move |result: wgpu::BufferMapAsyncResult<&[u8]>| {
let mut png_encoder = png::Encoder::new(File::create("red.png").unwrap(), size, size);
png_encoder.set_depth(png::BitDepth::Eight);
png_encoder.set_color(png::ColorType::RGBA);
png_encoder
.write_header()
.unwrap()
.write_image_data(result.unwrap().data)
.unwrap();
},
);
if let Ok(mapping) = output_buffer.map_read(0u64, (size * size) as u64 * size_of::<u32>() as u64).await {
let mut png_encoder = png::Encoder::new(File::create("red.png").unwrap(), size, size);
png_encoder.set_depth(png::BitDepth::Eight);
png_encoder.set_color(png::ColorType::RGBA);
png_encoder
.write_header()
.unwrap()
.write_image_data(mapping.as_slice())
.unwrap();
}

// The device will be polled when it is dropped but we can also poll it explicitly
device.poll(true);
}

fn main() {
futures::executor::block_on(run());
}
26 changes: 14 additions & 12 deletions wgpu/examples/hello-compute/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{convert::TryInto as _, str::FromStr};
use zerocopy::AsBytes as _;

fn main() {
async fn run() {
env_logger::init();

// For now this just panics if you didn't pass numbers. Could add proper error handling.
Expand Down Expand Up @@ -93,15 +93,17 @@ fn main() {

queue.submit(&[encoder.finish()]);

// FIXME: Align and use `LayoutVerified`
staging_buffer.map_read_async(0, slice_size, |result| {
if let Ok(mapping) = result {
let times: Box<[u32]> = mapping
.data
.chunks_exact(4)
.map(|b| u32::from_ne_bytes(b.try_into().unwrap()))
.collect();
println!("Times: {:?}", times);
}
});
if let Ok(mapping) = staging_buffer.map_read(0u64, size).await {
let times : Box<[u32]> = mapping
.as_slice()
.chunks_exact(4)
.map(|b| u32::from_ne_bytes(b.try_into().unwrap()))
.collect();

println!("Times: {:?}", times);
}
}

fn main() {
futures::executor::block_on(run());
}
72 changes: 72 additions & 0 deletions wgpu/src/future.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};

struct GpuFutureInner<T> {
id: wgc::id::DeviceId,
result: Option<T>,
waker: Option<Waker>,
}

/// A Future that can poll the wgpu::Device
pub struct GpuFuture<T> {
inner: Arc<Mutex<GpuFutureInner<T>>>,
}

/// A completion handle to set the result on a GpuFuture
pub struct GpuFutureCompletion<T> {
inner: Arc<Mutex<GpuFutureInner<T>>>,
}

impl<T> Future for GpuFuture<T>
{
type Output = T;

fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll<Self::Output> {
// grab a clone of the Arc
let arc = Arc::clone(&Pin::into_inner(self).inner);

// grab the device id and set the waker, but release the lock, so that the native callback can write to it
let device_id = {
let mut inner = arc.lock().unwrap();
inner.waker.replace(context.waker().clone());
inner.id
};

// polling the device should trigger the callback
wgn::wgpu_device_poll(device_id, true);

// now take the lock again, and check whether the future is complete
let mut inner = arc.lock().unwrap();
match inner.result.take() {
Some(value) => Poll::Ready(value),
_ => Poll::Pending,
}
}
}

impl<T> GpuFutureCompletion<T> {
pub fn complete(self, value: T) {
let mut inner = self.inner.lock().unwrap();
inner.result.replace(value);
if let Some(waker) = &inner.waker {
waker.wake_by_ref();
}
}
}

pub(crate) fn new_gpu_future<T>(id: wgc::id::DeviceId) -> (GpuFuture<T>, GpuFutureCompletion<T>) {
let inner = Arc::new(Mutex::new(GpuFutureInner {
id,
result: None,
waker: None,
}));

(
GpuFuture {
inner: inner.clone(),
},
GpuFutureCompletion { inner },
)
}
Loading

0 comments on commit a85f95d

Please sign in to comment.