diff options
author | Leo Kettmeir <crowlkats@toaxl.com> | 2023-12-09 01:19:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-09 01:19:16 +0100 |
commit | 393abed3873d83019feb5bcebb10a6929133862a (patch) | |
tree | c346e6d628e6b037fb8f881a70ca2ae6f70692b6 /ext/webgpu/buffer.rs | |
parent | 123d9ea047a2e10803e260ebf00f31fcc98463ee (diff) |
feat: bring back WebGPU (#20812)
Signed-off-by: Leo Kettmeir <crowlkats@toaxl.com>
Co-authored-by: Kenta Moriuchi <moriken@kimamass.com>
Co-authored-by: Bartek IwaĆczuk <biwanczuk@gmail.com>
Diffstat (limited to 'ext/webgpu/buffer.rs')
-rw-r--r-- | ext/webgpu/buffer.rs | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/ext/webgpu/buffer.rs b/ext/webgpu/buffer.rs new file mode 100644 index 000000000..2f9724825 --- /dev/null +++ b/ext/webgpu/buffer.rs @@ -0,0 +1,205 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::futures::channel::oneshot; +use deno_core::op2; +use deno_core::OpState; +use deno_core::Resource; +use deno_core::ResourceId; +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; +use std::time::Duration; +use wgpu_core::resource::BufferAccessResult; + +use super::error::DomExceptionOperationError; +use super::error::WebGpuResult; + +pub(crate) struct WebGpuBuffer( + pub(crate) super::Instance, + pub(crate) wgpu_core::id::BufferId, +); +impl Resource for WebGpuBuffer { + fn name(&self) -> Cow<str> { + "webGPUBuffer".into() + } + + fn close(self: Rc<Self>) { + let instance = &self.0; + gfx_select!(self.1 => instance.buffer_drop(self.1, true)); + } +} + +struct WebGpuBufferMapped(*mut u8, usize); +impl Resource for WebGpuBufferMapped { + fn name(&self) -> Cow<str> { + "webGPUBufferMapped".into() + } +} + +#[op2] +#[serde] +pub fn op_webgpu_create_buffer( + state: &mut OpState, + #[smi] device_rid: ResourceId, + #[string] label: Cow<str>, + #[number] size: u64, + usage: u32, + mapped_at_creation: bool, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let device_resource = state + .resource_table + .get::<super::WebGpuDevice>(device_rid)?; + let device = device_resource.1; + + let descriptor = wgpu_core::resource::BufferDescriptor { + label: Some(label), + size, + usage: wgpu_types::BufferUsages::from_bits(usage) + .ok_or_else(|| type_error("usage is not valid"))?, + mapped_at_creation, + }; + + gfx_put!(device => instance.device_create_buffer( + device, + &descriptor, + () + ) => state, WebGpuBuffer) +} + +#[op2(async)] +#[serde] +pub async fn op_webgpu_buffer_get_map_async( + state: Rc<RefCell<OpState>>, + #[smi] buffer_rid: ResourceId, + #[smi] device_rid: ResourceId, + mode: u32, + #[number] offset: u64, + #[number] size: u64, +) -> Result<WebGpuResult, AnyError> { + let (sender, receiver) = oneshot::channel::<BufferAccessResult>(); + + let device; + { + let state_ = state.borrow(); + let instance = state_.borrow::<super::Instance>(); + let buffer_resource = + state_.resource_table.get::<WebGpuBuffer>(buffer_rid)?; + let buffer = buffer_resource.1; + let device_resource = state_ + .resource_table + .get::<super::WebGpuDevice>(device_rid)?; + device = device_resource.1; + + let callback = Box::new(move |status| { + sender.send(status).unwrap(); + }); + + // TODO(lucacasonato): error handling + let maybe_err = gfx_select!(buffer => instance.buffer_map_async( + buffer, + offset..(offset + size), + wgpu_core::resource::BufferMapOperation { + host: match mode { + 1 => wgpu_core::device::HostMap::Read, + 2 => wgpu_core::device::HostMap::Write, + _ => unreachable!(), + }, + callback: wgpu_core::resource::BufferMapCallback::from_rust(callback), + } + )) + .err(); + + if maybe_err.is_some() { + return Ok(WebGpuResult::maybe_err(maybe_err)); + } + } + + let done = Rc::new(RefCell::new(false)); + let done_ = done.clone(); + let device_poll_fut = async move { + while !*done.borrow() { + { + let state = state.borrow(); + let instance = state.borrow::<super::Instance>(); + gfx_select!(device => instance.device_poll(device, wgpu_types::Maintain::Wait)) + .unwrap(); + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + Ok::<(), AnyError>(()) + }; + + let receiver_fut = async move { + receiver.await??; + let mut done = done_.borrow_mut(); + *done = true; + Ok::<(), AnyError>(()) + }; + + tokio::try_join!(device_poll_fut, receiver_fut)?; + + Ok(WebGpuResult::empty()) +} + +#[op2] +#[serde] +pub fn op_webgpu_buffer_get_mapped_range( + state: &mut OpState, + #[smi] buffer_rid: ResourceId, + #[number] offset: u64, + #[number] size: Option<u64>, + #[buffer] buf: &mut [u8], +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let buffer_resource = state.resource_table.get::<WebGpuBuffer>(buffer_rid)?; + let buffer = buffer_resource.1; + + let (slice_pointer, range_size) = + gfx_select!(buffer => instance.buffer_get_mapped_range( + buffer, + offset, + size + )) + .map_err(|e| DomExceptionOperationError::new(&e.to_string()))?; + + // SAFETY: guarantee to be safe from wgpu + let slice = unsafe { + std::slice::from_raw_parts_mut(slice_pointer, range_size as usize) + }; + buf.copy_from_slice(slice); + + let rid = state + .resource_table + .add(WebGpuBufferMapped(slice_pointer, range_size as usize)); + + Ok(WebGpuResult::rid(rid)) +} + +#[op2] +#[serde] +pub fn op_webgpu_buffer_unmap( + state: &mut OpState, + #[smi] buffer_rid: ResourceId, + #[smi] mapped_rid: ResourceId, + #[buffer] buf: Option<&[u8]>, +) -> Result<WebGpuResult, AnyError> { + let mapped_resource = state + .resource_table + .take::<WebGpuBufferMapped>(mapped_rid)?; + let instance = state.borrow::<super::Instance>(); + let buffer_resource = state.resource_table.get::<WebGpuBuffer>(buffer_rid)?; + let buffer = buffer_resource.1; + + if let Some(buf) = buf { + // SAFETY: guarantee to be safe from wgpu + let slice = unsafe { + std::slice::from_raw_parts_mut(mapped_resource.0, mapped_resource.1) + }; + slice.copy_from_slice(buf); + } + + gfx_ok!(buffer => instance.buffer_unmap(buffer)) +} |