summaryrefslogtreecommitdiff
path: root/ext/webgpu/src
diff options
context:
space:
mode:
Diffstat (limited to 'ext/webgpu/src')
-rw-r--r--ext/webgpu/src/binding.rs341
-rw-r--r--ext/webgpu/src/buffer.rs229
-rw-r--r--ext/webgpu/src/bundle.rs459
-rw-r--r--ext/webgpu/src/command_encoder.rs646
-rw-r--r--ext/webgpu/src/compute_pass.rs354
-rw-r--r--ext/webgpu/src/error.rs292
-rw-r--r--ext/webgpu/src/lib.rs908
-rw-r--r--ext/webgpu/src/queue.rs142
-rw-r--r--ext/webgpu/src/render_pass.rs649
-rw-r--r--ext/webgpu/src/shader.rs52
10 files changed, 4072 insertions, 0 deletions
diff --git a/ext/webgpu/src/binding.rs b/ext/webgpu/src/binding.rs
new file mode 100644
index 000000000..9a8fa455f
--- /dev/null
+++ b/ext/webgpu/src/binding.rs
@@ -0,0 +1,341 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::convert::{TryFrom, TryInto};
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuBindGroupLayout(
+ pub(crate) wgpu_core::id::BindGroupLayoutId,
+);
+impl Resource for WebGpuBindGroupLayout {
+ fn name(&self) -> Cow<str> {
+ "webGPUBindGroupLayout".into()
+ }
+}
+
+pub(crate) struct WebGpuBindGroup(pub(crate) wgpu_core::id::BindGroupId);
+impl Resource for WebGpuBindGroup {
+ fn name(&self) -> Cow<str> {
+ "webGPUBindGroup".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuBufferBindingLayout {
+ r#type: GpuBufferBindingType,
+ has_dynamic_offset: bool,
+ min_binding_size: u64,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+enum GpuBufferBindingType {
+ Uniform,
+ Storage,
+ ReadOnlyStorage,
+}
+
+impl From<GpuBufferBindingType> for wgpu_types::BufferBindingType {
+ fn from(binding_type: GpuBufferBindingType) -> Self {
+ match binding_type {
+ GpuBufferBindingType::Uniform => wgpu_types::BufferBindingType::Uniform,
+ GpuBufferBindingType::Storage => {
+ wgpu_types::BufferBindingType::Storage { read_only: false }
+ }
+ GpuBufferBindingType::ReadOnlyStorage => {
+ wgpu_types::BufferBindingType::Storage { read_only: true }
+ }
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuSamplerBindingLayout {
+ r#type: wgpu_types::SamplerBindingType,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuTextureBindingLayout {
+ sample_type: GpuTextureSampleType,
+ view_dimension: wgpu_types::TextureViewDimension,
+ multisampled: bool,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+enum GpuTextureSampleType {
+ Float,
+ UnfilterableFloat,
+ Depth,
+ Sint,
+ Uint,
+}
+
+impl From<GpuTextureSampleType> for wgpu_types::TextureSampleType {
+ fn from(sample_type: GpuTextureSampleType) -> Self {
+ match sample_type {
+ GpuTextureSampleType::Float => {
+ wgpu_types::TextureSampleType::Float { filterable: true }
+ }
+ GpuTextureSampleType::UnfilterableFloat => {
+ wgpu_types::TextureSampleType::Float { filterable: false }
+ }
+ GpuTextureSampleType::Depth => wgpu_types::TextureSampleType::Depth,
+ GpuTextureSampleType::Sint => wgpu_types::TextureSampleType::Sint,
+ GpuTextureSampleType::Uint => wgpu_types::TextureSampleType::Uint,
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuStorageTextureBindingLayout {
+ access: GpuStorageTextureAccess,
+ format: wgpu_types::TextureFormat,
+ view_dimension: wgpu_types::TextureViewDimension,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+enum GpuStorageTextureAccess {
+ WriteOnly,
+}
+
+impl From<GpuStorageTextureAccess> for wgpu_types::StorageTextureAccess {
+ fn from(access: GpuStorageTextureAccess) -> Self {
+ match access {
+ GpuStorageTextureAccess::WriteOnly => {
+ wgpu_types::StorageTextureAccess::WriteOnly
+ }
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuBindGroupLayoutEntry {
+ binding: u32,
+ visibility: u32,
+ #[serde(flatten)]
+ binding_type: GpuBindingType,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum GpuBindingType {
+ Buffer(GpuBufferBindingLayout),
+ Sampler(GpuSamplerBindingLayout),
+ Texture(GpuTextureBindingLayout),
+ StorageTexture(GpuStorageTextureBindingLayout),
+}
+
+impl TryFrom<GpuBindingType> for wgpu_types::BindingType {
+ type Error = AnyError;
+
+ fn try_from(
+ binding_type: GpuBindingType,
+ ) -> Result<wgpu_types::BindingType, Self::Error> {
+ let binding_type = match binding_type {
+ GpuBindingType::Buffer(buffer) => wgpu_types::BindingType::Buffer {
+ ty: buffer.r#type.into(),
+ has_dynamic_offset: buffer.has_dynamic_offset,
+ min_binding_size: std::num::NonZeroU64::new(buffer.min_binding_size),
+ },
+ GpuBindingType::Sampler(sampler) => {
+ wgpu_types::BindingType::Sampler(sampler.r#type)
+ }
+ GpuBindingType::Texture(texture) => wgpu_types::BindingType::Texture {
+ sample_type: texture.sample_type.into(),
+ view_dimension: texture.view_dimension,
+ multisampled: texture.multisampled,
+ },
+ GpuBindingType::StorageTexture(storage_texture) => {
+ wgpu_types::BindingType::StorageTexture {
+ access: storage_texture.access.into(),
+ format: storage_texture.format,
+ view_dimension: storage_texture.view_dimension,
+ }
+ }
+ };
+ Ok(binding_type)
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateBindGroupLayoutArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ entries: Vec<GpuBindGroupLayoutEntry>,
+}
+
+pub fn op_webgpu_create_bind_group_layout(
+ state: &mut OpState,
+ args: CreateBindGroupLayoutArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+
+ let mut entries = vec![];
+
+ for entry in args.entries {
+ entries.push(wgpu_types::BindGroupLayoutEntry {
+ binding: entry.binding,
+ visibility: wgpu_types::ShaderStages::from_bits(entry.visibility)
+ .unwrap(),
+ ty: entry.binding_type.try_into()?,
+ count: None, // native-only
+ });
+ }
+
+ let descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
+ label: args.label.map(Cow::from),
+ entries: Cow::from(entries),
+ };
+
+ gfx_put!(device => instance.device_create_bind_group_layout(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ) => state, WebGpuBindGroupLayout)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreatePipelineLayoutArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ bind_group_layouts: Vec<u32>,
+}
+
+pub fn op_webgpu_create_pipeline_layout(
+ state: &mut OpState,
+ args: CreatePipelineLayoutArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+
+ let mut bind_group_layouts = vec![];
+
+ for rid in &args.bind_group_layouts {
+ let bind_group_layout =
+ state.resource_table.get::<WebGpuBindGroupLayout>(*rid)?;
+ bind_group_layouts.push(bind_group_layout.0);
+ }
+
+ let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
+ label: args.label.map(Cow::from),
+ bind_group_layouts: Cow::from(bind_group_layouts),
+ push_constant_ranges: Default::default(),
+ };
+
+ gfx_put!(device => instance.device_create_pipeline_layout(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ) => state, super::pipeline::WebGpuPipelineLayout)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuBindGroupEntry {
+ binding: u32,
+ kind: String,
+ resource: ResourceId,
+ offset: Option<u64>,
+ size: Option<u64>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateBindGroupArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ layout: ResourceId,
+ entries: Vec<GpuBindGroupEntry>,
+}
+
+pub fn op_webgpu_create_bind_group(
+ state: &mut OpState,
+ args: CreateBindGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+
+ let mut entries = vec![];
+
+ for entry in &args.entries {
+ let e = wgpu_core::binding_model::BindGroupEntry {
+ binding: entry.binding,
+ resource: match entry.kind.as_str() {
+ "GPUSampler" => {
+ let sampler_resource =
+ state
+ .resource_table
+ .get::<super::sampler::WebGpuSampler>(entry.resource)?;
+ wgpu_core::binding_model::BindingResource::Sampler(sampler_resource.0)
+ }
+ "GPUTextureView" => {
+ let texture_view_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(entry.resource)?;
+ wgpu_core::binding_model::BindingResource::TextureView(
+ texture_view_resource.0,
+ )
+ }
+ "GPUBufferBinding" => {
+ let buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(entry.resource)?;
+ wgpu_core::binding_model::BindingResource::Buffer(
+ wgpu_core::binding_model::BufferBinding {
+ buffer_id: buffer_resource.0,
+ offset: entry.offset.unwrap_or(0),
+ size: std::num::NonZeroU64::new(entry.size.unwrap_or(0)),
+ },
+ )
+ }
+ _ => unreachable!(),
+ },
+ };
+ entries.push(e);
+ }
+
+ let bind_group_layout = state
+ .resource_table
+ .get::<WebGpuBindGroupLayout>(args.layout)?;
+
+ let descriptor = wgpu_core::binding_model::BindGroupDescriptor {
+ label: args.label.map(Cow::from),
+ layout: bind_group_layout.0,
+ entries: Cow::from(entries),
+ };
+
+ gfx_put!(device => instance.device_create_bind_group(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ) => state, WebGpuBindGroup)
+}
diff --git a/ext/webgpu/src/buffer.rs b/ext/webgpu/src/buffer.rs
new file mode 100644
index 000000000..3f2c07883
--- /dev/null
+++ b/ext/webgpu/src/buffer.rs
@@ -0,0 +1,229 @@
+// Copyright 2018-2022 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::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use deno_core::ZeroCopyBuf;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::time::Duration;
+
+use super::error::DomExceptionOperationError;
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuBuffer(pub(crate) wgpu_core::id::BufferId);
+impl Resource for WebGpuBuffer {
+ fn name(&self) -> Cow<str> {
+ "webGPUBuffer".into()
+ }
+}
+
+struct WebGpuBufferMapped(*mut u8, usize);
+impl Resource for WebGpuBufferMapped {
+ fn name(&self) -> Cow<str> {
+ "webGPUBufferMapped".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateBufferArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ size: u64,
+ usage: u32,
+ mapped_at_creation: bool,
+}
+
+pub fn op_webgpu_create_buffer(
+ state: &mut OpState,
+ args: CreateBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+
+ let descriptor = wgpu_core::resource::BufferDescriptor {
+ label: args.label.map(Cow::from),
+ size: args.size,
+ usage: wgpu_types::BufferUsages::from_bits(args.usage)
+ .ok_or_else(|| type_error("usage is not valid"))?,
+ mapped_at_creation: args.mapped_at_creation,
+ };
+
+ gfx_put!(device => instance.device_create_buffer(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ) => state, WebGpuBuffer)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BufferGetMapAsyncArgs {
+ buffer_rid: ResourceId,
+ device_rid: ResourceId,
+ mode: u32,
+ offset: u64,
+ size: u64,
+}
+
+pub async fn op_webgpu_buffer_get_map_async(
+ state: Rc<RefCell<OpState>>,
+ args: BufferGetMapAsyncArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let (sender, receiver) = oneshot::channel::<Result<(), AnyError>>();
+
+ let device;
+ {
+ let state_ = state.borrow();
+ let instance = state_.borrow::<super::Instance>();
+ let buffer_resource =
+ state_.resource_table.get::<WebGpuBuffer>(args.buffer_rid)?;
+ let buffer = buffer_resource.0;
+ let device_resource = state_
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ device = device_resource.0;
+
+ let boxed_sender = Box::new(sender);
+ let sender_ptr = Box::into_raw(boxed_sender) as *mut u8;
+
+ extern "C" fn buffer_map_future_wrapper(
+ status: wgpu_core::resource::BufferMapAsyncStatus,
+ user_data: *mut u8,
+ ) {
+ let sender_ptr = user_data as *mut oneshot::Sender<Result<(), AnyError>>;
+ let boxed_sender = unsafe { Box::from_raw(sender_ptr) };
+ boxed_sender
+ .send(match status {
+ wgpu_core::resource::BufferMapAsyncStatus::Success => Ok(()),
+ _ => unreachable!(), // TODO
+ })
+ .unwrap();
+ }
+
+ // TODO(lucacasonato): error handling
+ let maybe_err = gfx_select!(buffer => instance.buffer_map_async(
+ buffer,
+ args.offset..(args.offset + args.size),
+ wgpu_core::resource::BufferMapOperation {
+ host: match args.mode {
+ 1 => wgpu_core::device::HostMap::Read,
+ 2 => wgpu_core::device::HostMap::Write,
+ _ => unreachable!(),
+ },
+ callback: buffer_map_future_wrapper,
+ user_data: sender_ptr,
+ }
+ ))
+ .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, false)).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())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BufferGetMappedRangeArgs {
+ buffer_rid: ResourceId,
+ offset: u64,
+ size: Option<u64>,
+}
+
+pub fn op_webgpu_buffer_get_mapped_range(
+ state: &mut OpState,
+ args: BufferGetMappedRangeArgs,
+ mut zero_copy: ZeroCopyBuf,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource =
+ state.resource_table.get::<WebGpuBuffer>(args.buffer_rid)?;
+ let buffer = buffer_resource.0;
+
+ let (slice_pointer, range_size) =
+ gfx_select!(buffer => instance.buffer_get_mapped_range(
+ buffer,
+ args.offset,
+ args.size
+ ))
+ .map_err(|e| DomExceptionOperationError::new(&e.to_string()))?;
+
+ let slice = unsafe {
+ std::slice::from_raw_parts_mut(slice_pointer, range_size as usize)
+ };
+ zero_copy.copy_from_slice(slice);
+
+ let rid = state
+ .resource_table
+ .add(WebGpuBufferMapped(slice_pointer, range_size as usize));
+
+ Ok(WebGpuResult::rid(rid))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BufferUnmapArgs {
+ buffer_rid: ResourceId,
+ mapped_rid: ResourceId,
+}
+
+pub fn op_webgpu_buffer_unmap(
+ state: &mut OpState,
+ args: BufferUnmapArgs,
+ zero_copy: Option<ZeroCopyBuf>,
+) -> Result<WebGpuResult, AnyError> {
+ let mapped_resource = state
+ .resource_table
+ .take::<WebGpuBufferMapped>(args.mapped_rid)?;
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource =
+ state.resource_table.get::<WebGpuBuffer>(args.buffer_rid)?;
+ let buffer = buffer_resource.0;
+
+ let slice_pointer = mapped_resource.0;
+ let size = mapped_resource.1;
+
+ if let Some(buffer) = zero_copy {
+ let slice = unsafe { std::slice::from_raw_parts_mut(slice_pointer, size) };
+ slice.copy_from_slice(&buffer);
+ }
+
+ gfx_ok!(buffer => instance.buffer_unmap(buffer))
+}
diff --git a/ext/webgpu/src/bundle.rs b/ext/webgpu/src/bundle.rs
new file mode 100644
index 000000000..ea327651a
--- /dev/null
+++ b/ext/webgpu/src/bundle.rs
@@ -0,0 +1,459 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use super::error::WebGpuResult;
+
+struct WebGpuRenderBundleEncoder(
+ RefCell<wgpu_core::command::RenderBundleEncoder>,
+);
+impl Resource for WebGpuRenderBundleEncoder {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderBundleEncoder".into()
+ }
+}
+
+pub(crate) struct WebGpuRenderBundle(pub(crate) wgpu_core::id::RenderBundleId);
+impl Resource for WebGpuRenderBundle {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderBundle".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateRenderBundleEncoderArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ color_formats: Vec<wgpu_types::TextureFormat>,
+ depth_stencil_format: Option<wgpu_types::TextureFormat>,
+ sample_count: u32,
+ depth_read_only: bool,
+ stencil_read_only: bool,
+}
+
+pub fn op_webgpu_create_render_bundle_encoder(
+ state: &mut OpState,
+ args: CreateRenderBundleEncoderArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+
+ let mut color_formats = vec![];
+
+ for format in args.color_formats {
+ color_formats.push(format);
+ }
+
+ let depth_stencil = if let Some(format) = args.depth_stencil_format {
+ Some(wgpu_types::RenderBundleDepthStencil {
+ format,
+ depth_read_only: args.depth_read_only,
+ stencil_read_only: args.stencil_read_only,
+ })
+ } else {
+ None
+ };
+
+ let descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
+ label: args.label.map(Cow::from),
+ color_formats: Cow::from(color_formats),
+ sample_count: args.sample_count,
+ depth_stencil,
+ multiview: None,
+ };
+
+ let res =
+ wgpu_core::command::RenderBundleEncoder::new(&descriptor, device, None);
+ let (render_bundle_encoder, maybe_err) = match res {
+ Ok(encoder) => (encoder, None),
+ Err(e) => (
+ wgpu_core::command::RenderBundleEncoder::dummy(device),
+ Some(e),
+ ),
+ };
+
+ let rid = state
+ .resource_table
+ .add(WebGpuRenderBundleEncoder(RefCell::new(
+ render_bundle_encoder,
+ )));
+
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderFinishArgs {
+ render_bundle_encoder_rid: ResourceId,
+ label: Option<String>,
+}
+
+pub fn op_webgpu_render_bundle_encoder_finish(
+ state: &mut OpState,
+ args: RenderBundleEncoderFinishArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .take::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+ let render_bundle_encoder = Rc::try_unwrap(render_bundle_encoder_resource)
+ .ok()
+ .expect("unwrapping render_bundle_encoder_resource should succeed")
+ .0
+ .into_inner();
+ let instance = state.borrow::<super::Instance>();
+
+ gfx_put!(render_bundle_encoder.parent() => instance.render_bundle_encoder_finish(
+ render_bundle_encoder,
+ &wgpu_core::command::RenderBundleDescriptor {
+ label: args.label.map(Cow::from),
+ },
+ std::marker::PhantomData
+ ) => state, WebGpuRenderBundle)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetBindGroupArgs {
+ render_bundle_encoder_rid: ResourceId,
+ index: u32,
+ bind_group: ResourceId,
+ dynamic_offsets_data: ZeroCopyBuf,
+ dynamic_offsets_data_start: usize,
+ dynamic_offsets_data_length: usize,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_bind_group(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetBindGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let bind_group_resource =
+ state
+ .resource_table
+ .get::<super::binding::WebGpuBindGroup>(args.bind_group)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ // Align the data
+ assert!(args.dynamic_offsets_data.len() % std::mem::size_of::<u32>() == 0);
+ // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a
+ // multiple of 4.
+ let (prefix, dynamic_offsets_data, suffix) =
+ unsafe { args.dynamic_offsets_data.align_to::<u32>() };
+ assert!(prefix.is_empty());
+ assert!(suffix.is_empty());
+
+ let start = args.dynamic_offsets_data_start;
+ let len = args.dynamic_offsets_data_length;
+
+ // Assert that length and start are both in bounds
+ assert!(start <= dynamic_offsets_data.len());
+ assert!(len <= dynamic_offsets_data.len() - start);
+
+ let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ dynamic_offsets_data.as_ptr(),
+ dynamic_offsets_data.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderPushDebugGroupArgs {
+ render_bundle_encoder_rid: ResourceId,
+ group_label: String,
+}
+
+pub fn op_webgpu_render_bundle_encoder_push_debug_group(
+ state: &mut OpState,
+ args: RenderBundleEncoderPushDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ let label = std::ffi::CString::new(args.group_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ label.as_ptr(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderPopDebugGroupArgs {
+ render_bundle_encoder_rid: ResourceId,
+}
+
+pub fn op_webgpu_render_bundle_encoder_pop_debug_group(
+ state: &mut OpState,
+ args: RenderBundleEncoderPopDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderInsertDebugMarkerArgs {
+ render_bundle_encoder_rid: ResourceId,
+ marker_label: String,
+}
+
+pub fn op_webgpu_render_bundle_encoder_insert_debug_marker(
+ state: &mut OpState,
+ args: RenderBundleEncoderInsertDebugMarkerArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ let label = std::ffi::CString::new(args.marker_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ label.as_ptr(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetPipelineArgs {
+ render_bundle_encoder_rid: ResourceId,
+ pipeline: ResourceId,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_pipeline(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetPipelineArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pipeline_resource =
+ state
+ .resource_table
+ .get::<super::pipeline::WebGpuRenderPipeline>(args.pipeline)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ render_pipeline_resource.0,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetIndexBufferArgs {
+ render_bundle_encoder_rid: ResourceId,
+ buffer: ResourceId,
+ index_format: wgpu_types::IndexFormat,
+ offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_index_buffer(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetIndexBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.buffer)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ render_bundle_encoder_resource
+ .0
+ .borrow_mut()
+ .set_index_buffer(
+ buffer_resource.0,
+ args.index_format,
+ args.offset,
+ std::num::NonZeroU64::new(args.size),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetVertexBufferArgs {
+ render_bundle_encoder_rid: ResourceId,
+ slot: u32,
+ buffer: ResourceId,
+ offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetVertexBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.buffer)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.slot,
+ buffer_resource.0,
+ args.offset,
+ std::num::NonZeroU64::new(args.size),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderDrawArgs {
+ render_bundle_encoder_rid: ResourceId,
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_bundle_encoder_draw(
+ state: &mut OpState,
+ args: RenderBundleEncoderDrawArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.vertex_count,
+ args.instance_count,
+ args.first_vertex,
+ args.first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderDrawIndexedArgs {
+ render_bundle_encoder_rid: ResourceId,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_bundle_encoder_draw_indexed(
+ state: &mut OpState,
+ args: RenderBundleEncoderDrawIndexedArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.index_count,
+ args.instance_count,
+ args.first_index,
+ args.base_vertex,
+ args.first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderDrawIndirectArgs {
+ render_bundle_encoder_rid: ResourceId,
+ indirect_buffer: ResourceId,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_render_bundle_encoder_draw_indirect(
+ state: &mut OpState,
+ args: RenderBundleEncoderDrawIndirectArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.indirect_buffer)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(args.render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
diff --git a/ext/webgpu/src/command_encoder.rs b/ext/webgpu/src/command_encoder.rs
new file mode 100644
index 000000000..894b08f27
--- /dev/null
+++ b/ext/webgpu/src/command_encoder.rs
@@ -0,0 +1,646 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::num::NonZeroU32;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuCommandEncoder(
+ pub(crate) wgpu_core::id::CommandEncoderId,
+);
+impl Resource for WebGpuCommandEncoder {
+ fn name(&self) -> Cow<str> {
+ "webGPUCommandEncoder".into()
+ }
+}
+
+pub(crate) struct WebGpuCommandBuffer(
+ pub(crate) wgpu_core::id::CommandBufferId,
+);
+impl Resource for WebGpuCommandBuffer {
+ fn name(&self) -> Cow<str> {
+ "webGPUCommandBuffer".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateCommandEncoderArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ _measure_execution_time: Option<bool>, // not yet implemented
+}
+
+pub fn op_webgpu_create_command_encoder(
+ state: &mut OpState,
+ args: CreateCommandEncoderArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+
+ let descriptor = wgpu_types::CommandEncoderDescriptor {
+ label: args.label.map(Cow::from),
+ };
+
+ gfx_put!(device => instance.device_create_command_encoder(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ) => state, WebGpuCommandEncoder)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuRenderPassColorAttachment {
+ view: ResourceId,
+ resolve_target: Option<ResourceId>,
+ load_op: GpuLoadOp<wgpu_types::Color>,
+ store_op: wgpu_core::command::StoreOp,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+enum GpuLoadOp<T> {
+ Load,
+ Clear(T),
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuRenderPassDepthStencilAttachment {
+ view: ResourceId,
+ depth_load_op: GpuLoadOp<f32>,
+ depth_store_op: wgpu_core::command::StoreOp,
+ depth_read_only: bool,
+ stencil_load_op: GpuLoadOp<u32>,
+ stencil_store_op: wgpu_core::command::StoreOp,
+ stencil_read_only: bool,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderBeginRenderPassArgs {
+ command_encoder_rid: ResourceId,
+ label: Option<String>,
+ color_attachments: Vec<GpuRenderPassColorAttachment>,
+ depth_stencil_attachment: Option<GpuRenderPassDepthStencilAttachment>,
+ _occlusion_query_set: Option<u32>, // not yet implemented
+}
+
+pub fn op_webgpu_command_encoder_begin_render_pass(
+ state: &mut OpState,
+ args: CommandEncoderBeginRenderPassArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+
+ let mut color_attachments = vec![];
+
+ for color_attachment in args.color_attachments {
+ let texture_view_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(color_attachment.view)?;
+
+ let resolve_target = color_attachment
+ .resolve_target
+ .map(|rid| {
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(rid)
+ })
+ .transpose()?
+ .map(|texture| texture.0);
+
+ let attachment = wgpu_core::command::RenderPassColorAttachment {
+ view: texture_view_resource.0,
+ resolve_target,
+ channel: match color_attachment.load_op {
+ GpuLoadOp::Load => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Load,
+ store_op: color_attachment.store_op,
+ clear_value: Default::default(),
+ read_only: false,
+ },
+ GpuLoadOp::Clear(color) => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Clear,
+ store_op: color_attachment.store_op,
+ clear_value: color,
+ read_only: false,
+ },
+ },
+ };
+
+ color_attachments.push(attachment)
+ }
+
+ let mut depth_stencil_attachment = None;
+
+ if let Some(attachment) = args.depth_stencil_attachment {
+ let texture_view_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(attachment.view)?;
+
+ depth_stencil_attachment =
+ Some(wgpu_core::command::RenderPassDepthStencilAttachment {
+ view: texture_view_resource.0,
+ depth: match attachment.depth_load_op {
+ GpuLoadOp::Load => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Load,
+ store_op: attachment.depth_store_op,
+ clear_value: 0.0,
+ read_only: attachment.depth_read_only,
+ },
+ GpuLoadOp::Clear(value) => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Clear,
+ store_op: attachment.depth_store_op,
+ clear_value: value,
+ read_only: attachment.depth_read_only,
+ },
+ },
+ stencil: match attachment.stencil_load_op {
+ GpuLoadOp::Load => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Load,
+ store_op: attachment.stencil_store_op,
+ clear_value: 0,
+ read_only: attachment.stencil_read_only,
+ },
+ GpuLoadOp::Clear(value) => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Clear,
+ store_op: attachment.stencil_store_op,
+ clear_value: value,
+ read_only: attachment.stencil_read_only,
+ },
+ },
+ });
+ }
+
+ let descriptor = wgpu_core::command::RenderPassDescriptor {
+ label: args.label.map(Cow::from),
+ color_attachments: Cow::from(color_attachments),
+ depth_stencil_attachment: depth_stencil_attachment.as_ref(),
+ };
+
+ let render_pass = wgpu_core::command::RenderPass::new(
+ command_encoder_resource.0,
+ &descriptor,
+ );
+
+ let rid = state
+ .resource_table
+ .add(super::render_pass::WebGpuRenderPass(RefCell::new(
+ render_pass,
+ )));
+
+ Ok(WebGpuResult::rid(rid))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderBeginComputePassArgs {
+ command_encoder_rid: ResourceId,
+ label: Option<String>,
+}
+
+pub fn op_webgpu_command_encoder_begin_compute_pass(
+ state: &mut OpState,
+ args: CommandEncoderBeginComputePassArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+
+ let descriptor = wgpu_core::command::ComputePassDescriptor {
+ label: args.label.map(Cow::from),
+ };
+
+ let compute_pass = wgpu_core::command::ComputePass::new(
+ command_encoder_resource.0,
+ &descriptor,
+ );
+
+ let rid = state
+ .resource_table
+ .add(super::compute_pass::WebGpuComputePass(RefCell::new(
+ compute_pass,
+ )));
+
+ Ok(WebGpuResult::rid(rid))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyBufferToBufferArgs {
+ command_encoder_rid: ResourceId,
+ source: ResourceId,
+ source_offset: u64,
+ destination: ResourceId,
+ destination_offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_command_encoder_copy_buffer_to_buffer(
+ state: &mut OpState,
+ args: CommandEncoderCopyBufferToBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.source)?;
+ let source_buffer = source_buffer_resource.0;
+ let destination_buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.destination)?;
+ let destination_buffer = destination_buffer_resource.0;
+
+ gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_buffer(
+ command_encoder,
+ source_buffer,
+ args.source_offset,
+ destination_buffer,
+ args.destination_offset,
+ args.size
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuImageCopyBuffer {
+ buffer: ResourceId,
+ offset: u64,
+ bytes_per_row: Option<u32>,
+ rows_per_image: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuImageCopyTexture {
+ pub texture: ResourceId,
+ pub mip_level: u32,
+ pub origin: wgpu_types::Origin3d,
+ pub aspect: wgpu_types::TextureAspect,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyBufferToTextureArgs {
+ command_encoder_rid: ResourceId,
+ source: GpuImageCopyBuffer,
+ destination: GpuImageCopyTexture,
+ copy_size: wgpu_types::Extent3d,
+}
+
+pub fn op_webgpu_command_encoder_copy_buffer_to_texture(
+ state: &mut OpState,
+ args: CommandEncoderCopyBufferToTextureArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.source.buffer)?;
+ let destination_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(args.destination.texture)?;
+
+ let source = wgpu_core::command::ImageCopyBuffer {
+ buffer: source_buffer_resource.0,
+ layout: wgpu_types::ImageDataLayout {
+ offset: args.source.offset,
+ bytes_per_row: NonZeroU32::new(args.source.bytes_per_row.unwrap_or(0)),
+ rows_per_image: NonZeroU32::new(args.source.rows_per_image.unwrap_or(0)),
+ },
+ };
+ let destination = wgpu_core::command::ImageCopyTexture {
+ texture: destination_texture_resource.0,
+ mip_level: args.destination.mip_level,
+ origin: args.destination.origin,
+ aspect: args.destination.aspect,
+ };
+ gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_texture(
+ command_encoder,
+ &source,
+ &destination,
+ &args.copy_size
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyTextureToBufferArgs {
+ command_encoder_rid: ResourceId,
+ source: GpuImageCopyTexture,
+ destination: GpuImageCopyBuffer,
+ copy_size: wgpu_types::Extent3d,
+}
+
+pub fn op_webgpu_command_encoder_copy_texture_to_buffer(
+ state: &mut OpState,
+ args: CommandEncoderCopyTextureToBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(args.source.texture)?;
+ let destination_buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.destination.buffer)?;
+
+ let source = wgpu_core::command::ImageCopyTexture {
+ texture: source_texture_resource.0,
+ mip_level: args.source.mip_level,
+ origin: args.source.origin,
+ aspect: args.source.aspect,
+ };
+ let destination = wgpu_core::command::ImageCopyBuffer {
+ buffer: destination_buffer_resource.0,
+ layout: wgpu_types::ImageDataLayout {
+ offset: args.destination.offset,
+ bytes_per_row: NonZeroU32::new(
+ args.destination.bytes_per_row.unwrap_or(0),
+ ),
+ rows_per_image: NonZeroU32::new(
+ args.destination.rows_per_image.unwrap_or(0),
+ ),
+ },
+ };
+ gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_buffer(
+ command_encoder,
+ &source,
+ &destination,
+ &args.copy_size
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyTextureToTextureArgs {
+ command_encoder_rid: ResourceId,
+ source: GpuImageCopyTexture,
+ destination: GpuImageCopyTexture,
+ copy_size: wgpu_types::Extent3d,
+}
+
+pub fn op_webgpu_command_encoder_copy_texture_to_texture(
+ state: &mut OpState,
+ args: CommandEncoderCopyTextureToTextureArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(args.source.texture)?;
+ let destination_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(args.destination.texture)?;
+
+ let source = wgpu_core::command::ImageCopyTexture {
+ texture: source_texture_resource.0,
+ mip_level: args.source.mip_level,
+ origin: args.source.origin,
+ aspect: args.source.aspect,
+ };
+ let destination = wgpu_core::command::ImageCopyTexture {
+ texture: destination_texture_resource.0,
+ mip_level: args.destination.mip_level,
+ origin: args.destination.origin,
+ aspect: args.destination.aspect,
+ };
+ gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_texture(
+ command_encoder,
+ &source,
+ &destination,
+ &args.copy_size
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderClearBufferArgs {
+ command_encoder_rid: u32,
+ destination_rid: u32,
+ destination_offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_command_encoder_clear_buffer(
+ state: &mut OpState,
+ args: CommandEncoderClearBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let destination_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.destination_rid)?;
+
+ gfx_ok!(command_encoder => instance.command_encoder_clear_buffer(
+ command_encoder,
+ destination_resource.0,
+ args.destination_offset,
+ std::num::NonZeroU64::new(args.size)
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderPushDebugGroupArgs {
+ command_encoder_rid: ResourceId,
+ group_label: String,
+}
+
+pub fn op_webgpu_command_encoder_push_debug_group(
+ state: &mut OpState,
+ args: CommandEncoderPushDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+
+ gfx_ok!(command_encoder => instance
+ .command_encoder_push_debug_group(command_encoder, &args.group_label))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderPopDebugGroupArgs {
+ command_encoder_rid: ResourceId,
+}
+
+pub fn op_webgpu_command_encoder_pop_debug_group(
+ state: &mut OpState,
+ args: CommandEncoderPopDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+
+ gfx_ok!(command_encoder => instance.command_encoder_pop_debug_group(command_encoder))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderInsertDebugMarkerArgs {
+ command_encoder_rid: ResourceId,
+ marker_label: String,
+}
+
+pub fn op_webgpu_command_encoder_insert_debug_marker(
+ state: &mut OpState,
+ args: CommandEncoderInsertDebugMarkerArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+
+ gfx_ok!(command_encoder => instance.command_encoder_insert_debug_marker(
+ command_encoder,
+ &args.marker_label
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderWriteTimestampArgs {
+ command_encoder_rid: ResourceId,
+ query_set: ResourceId,
+ query_index: u32,
+}
+
+pub fn op_webgpu_command_encoder_write_timestamp(
+ state: &mut OpState,
+ args: CommandEncoderWriteTimestampArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(args.query_set)?;
+
+ gfx_ok!(command_encoder => instance.command_encoder_write_timestamp(
+ command_encoder,
+ query_set_resource.0,
+ args.query_index
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderResolveQuerySetArgs {
+ command_encoder_rid: ResourceId,
+ query_set: ResourceId,
+ first_query: u32,
+ query_count: u32,
+ destination: ResourceId,
+ destination_offset: u64,
+}
+
+pub fn op_webgpu_command_encoder_resolve_query_set(
+ state: &mut OpState,
+ args: CommandEncoderResolveQuerySetArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(args.query_set)?;
+ let destination_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.destination)?;
+
+ gfx_ok!(command_encoder => instance.command_encoder_resolve_query_set(
+ command_encoder,
+ query_set_resource.0,
+ args.first_query,
+ args.query_count,
+ destination_resource.0,
+ args.destination_offset
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderFinishArgs {
+ command_encoder_rid: ResourceId,
+ label: Option<String>,
+}
+
+pub fn op_webgpu_command_encoder_finish(
+ state: &mut OpState,
+ args: CommandEncoderFinishArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .take::<WebGpuCommandEncoder>(args.command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.0;
+ let instance = state.borrow::<super::Instance>();
+
+ let descriptor = wgpu_types::CommandBufferDescriptor {
+ label: args.label.map(Cow::from),
+ };
+
+ gfx_put!(command_encoder => instance.command_encoder_finish(
+ command_encoder,
+ &descriptor
+ ) => state, WebGpuCommandBuffer)
+}
diff --git a/ext/webgpu/src/compute_pass.rs b/ext/webgpu/src/compute_pass.rs
new file mode 100644
index 000000000..03d9163cb
--- /dev/null
+++ b/ext/webgpu/src/compute_pass.rs
@@ -0,0 +1,354 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuComputePass(
+ pub(crate) RefCell<wgpu_core::command::ComputePass>,
+);
+impl Resource for WebGpuComputePass {
+ fn name(&self) -> Cow<str> {
+ "webGPUComputePass".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassSetPipelineArgs {
+ compute_pass_rid: ResourceId,
+ pipeline: ResourceId,
+}
+
+pub fn op_webgpu_compute_pass_set_pipeline(
+ state: &mut OpState,
+ args: ComputePassSetPipelineArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pipeline_resource =
+ state
+ .resource_table
+ .get::<super::pipeline::WebGpuComputePipeline>(args.pipeline)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_set_pipeline(
+ &mut compute_pass_resource.0.borrow_mut(),
+ compute_pipeline_resource.0,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassDispatchArgs {
+ compute_pass_rid: ResourceId,
+ x: u32,
+ y: u32,
+ z: u32,
+}
+
+pub fn op_webgpu_compute_pass_dispatch(
+ state: &mut OpState,
+ args: ComputePassDispatchArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch(
+ &mut compute_pass_resource.0.borrow_mut(),
+ args.x,
+ args.y,
+ args.z,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassDispatchIndirectArgs {
+ compute_pass_rid: ResourceId,
+ indirect_buffer: ResourceId,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_compute_pass_dispatch_indirect(
+ state: &mut OpState,
+ args: ComputePassDispatchIndirectArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.indirect_buffer)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch_indirect(
+ &mut compute_pass_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassBeginPipelineStatisticsQueryArgs {
+ compute_pass_rid: ResourceId,
+ query_set: ResourceId,
+ query_index: u32,
+}
+
+pub fn op_webgpu_compute_pass_begin_pipeline_statistics_query(
+ state: &mut OpState,
+ args: ComputePassBeginPipelineStatisticsQueryArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(args.query_set)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_begin_pipeline_statistics_query(
+ &mut compute_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassEndPipelineStatisticsQueryArgs {
+ compute_pass_rid: ResourceId,
+}
+
+pub fn op_webgpu_compute_pass_end_pipeline_statistics_query(
+ state: &mut OpState,
+ args: ComputePassEndPipelineStatisticsQueryArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_end_pipeline_statistics_query(
+ &mut compute_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassWriteTimestampArgs {
+ compute_pass_rid: ResourceId,
+ query_set: ResourceId,
+ query_index: u32,
+}
+
+pub fn op_webgpu_compute_pass_write_timestamp(
+ state: &mut OpState,
+ args: ComputePassWriteTimestampArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(args.query_set)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_write_timestamp(
+ &mut compute_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassEndPassArgs {
+ command_encoder_rid: ResourceId,
+ compute_pass_rid: ResourceId,
+}
+
+pub fn op_webgpu_compute_pass_end_pass(
+ state: &mut OpState,
+ args: ComputePassEndPassArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<super::command_encoder::WebGpuCommandEncoder>(
+ args.command_encoder_rid,
+ )?;
+ let command_encoder = command_encoder_resource.0;
+ let compute_pass_resource = state
+ .resource_table
+ .take::<WebGpuComputePass>(args.compute_pass_rid)?;
+ let compute_pass = &compute_pass_resource.0.borrow();
+ let instance = state.borrow::<super::Instance>();
+
+ gfx_ok!(command_encoder => instance.command_encoder_run_compute_pass(
+ command_encoder,
+ compute_pass
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassSetBindGroupArgs {
+ compute_pass_rid: ResourceId,
+ index: u32,
+ bind_group: ResourceId,
+ dynamic_offsets_data: ZeroCopyBuf,
+ dynamic_offsets_data_start: usize,
+ dynamic_offsets_data_length: usize,
+}
+
+pub fn op_webgpu_compute_pass_set_bind_group(
+ state: &mut OpState,
+ args: ComputePassSetBindGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let bind_group_resource =
+ state
+ .resource_table
+ .get::<super::binding::WebGpuBindGroup>(args.bind_group)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ // Align the data
+ assert!(args.dynamic_offsets_data_start % std::mem::size_of::<u32>() == 0);
+ // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a
+ // multiple of 4.
+ let (prefix, dynamic_offsets_data, suffix) =
+ unsafe { args.dynamic_offsets_data.align_to::<u32>() };
+ assert!(prefix.is_empty());
+ assert!(suffix.is_empty());
+
+ let start = args.dynamic_offsets_data_start;
+ let len = args.dynamic_offsets_data_length;
+
+ // Assert that length and start are both in bounds
+ assert!(start <= dynamic_offsets_data.len());
+ assert!(len <= dynamic_offsets_data.len() - start);
+
+ let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_set_bind_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ dynamic_offsets_data.as_ptr(),
+ dynamic_offsets_data.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassPushDebugGroupArgs {
+ compute_pass_rid: ResourceId,
+ group_label: String,
+}
+
+pub fn op_webgpu_compute_pass_push_debug_group(
+ state: &mut OpState,
+ args: ComputePassPushDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ let label = std::ffi::CString::new(args.group_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_push_debug_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassPopDebugGroupArgs {
+ compute_pass_rid: ResourceId,
+}
+
+pub fn op_webgpu_compute_pass_pop_debug_group(
+ state: &mut OpState,
+ args: ComputePassPopDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_pop_debug_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassInsertDebugMarkerArgs {
+ compute_pass_rid: ResourceId,
+ marker_label: String,
+}
+
+pub fn op_webgpu_compute_pass_insert_debug_marker(
+ state: &mut OpState,
+ args: ComputePassInsertDebugMarkerArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(args.compute_pass_rid)?;
+
+ let label = std::ffi::CString::new(args.marker_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_insert_debug_marker(
+ &mut compute_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
diff --git a/ext/webgpu/src/error.rs b/ext/webgpu/src/error.rs
new file mode 100644
index 000000000..ae6e48054
--- /dev/null
+++ b/ext/webgpu/src/error.rs
@@ -0,0 +1,292 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use serde::Serialize;
+use std::convert::From;
+use std::fmt;
+use wgpu_core::binding_model::CreateBindGroupError;
+use wgpu_core::binding_model::CreateBindGroupLayoutError;
+use wgpu_core::binding_model::CreatePipelineLayoutError;
+use wgpu_core::binding_model::GetBindGroupLayoutError;
+use wgpu_core::command::ClearError;
+use wgpu_core::command::CommandEncoderError;
+use wgpu_core::command::ComputePassError;
+use wgpu_core::command::CopyError;
+use wgpu_core::command::CreateRenderBundleError;
+use wgpu_core::command::QueryError;
+use wgpu_core::command::RenderBundleError;
+use wgpu_core::command::RenderPassError;
+use wgpu_core::device::queue::QueueSubmitError;
+use wgpu_core::device::queue::QueueWriteError;
+use wgpu_core::device::DeviceError;
+use wgpu_core::pipeline::CreateComputePipelineError;
+use wgpu_core::pipeline::CreateRenderPipelineError;
+use wgpu_core::pipeline::CreateShaderModuleError;
+use wgpu_core::resource::BufferAccessError;
+use wgpu_core::resource::CreateBufferError;
+use wgpu_core::resource::CreateQuerySetError;
+use wgpu_core::resource::CreateSamplerError;
+use wgpu_core::resource::CreateTextureError;
+use wgpu_core::resource::CreateTextureViewError;
+
+#[derive(Serialize)]
+pub struct WebGpuResult {
+ pub rid: Option<ResourceId>,
+ pub err: Option<WebGpuError>,
+}
+
+impl WebGpuResult {
+ pub fn rid(rid: ResourceId) -> Self {
+ Self {
+ rid: Some(rid),
+ err: None,
+ }
+ }
+
+ pub fn rid_err<T: Into<WebGpuError>>(
+ rid: ResourceId,
+ err: Option<T>,
+ ) -> Self {
+ Self {
+ rid: Some(rid),
+ err: err.map(|e| e.into()),
+ }
+ }
+
+ pub fn maybe_err<T: Into<WebGpuError>>(err: Option<T>) -> Self {
+ Self {
+ rid: None,
+ err: err.map(|e| e.into()),
+ }
+ }
+
+ pub fn empty() -> Self {
+ Self {
+ rid: None,
+ err: None,
+ }
+ }
+}
+
+#[derive(Serialize)]
+#[serde(tag = "type", content = "value")]
+#[serde(rename_all = "kebab-case")]
+pub enum WebGpuError {
+ Lost,
+ OutOfMemory,
+ Validation(String),
+}
+
+impl From<CreateBufferError> for WebGpuError {
+ fn from(err: CreateBufferError) -> Self {
+ match err {
+ CreateBufferError::Device(err) => err.into(),
+ CreateBufferError::AccessError(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<DeviceError> for WebGpuError {
+ fn from(err: DeviceError) -> Self {
+ match err {
+ DeviceError::Lost => WebGpuError::Lost,
+ DeviceError::OutOfMemory => WebGpuError::OutOfMemory,
+ DeviceError::Invalid => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<BufferAccessError> for WebGpuError {
+ fn from(err: BufferAccessError) -> Self {
+ match err {
+ BufferAccessError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateBindGroupLayoutError> for WebGpuError {
+ fn from(err: CreateBindGroupLayoutError) -> Self {
+ match err {
+ CreateBindGroupLayoutError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreatePipelineLayoutError> for WebGpuError {
+ fn from(err: CreatePipelineLayoutError) -> Self {
+ match err {
+ CreatePipelineLayoutError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateBindGroupError> for WebGpuError {
+ fn from(err: CreateBindGroupError) -> Self {
+ match err {
+ CreateBindGroupError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<RenderBundleError> for WebGpuError {
+ fn from(err: RenderBundleError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateRenderBundleError> for WebGpuError {
+ fn from(err: CreateRenderBundleError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<CopyError> for WebGpuError {
+ fn from(err: CopyError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<CommandEncoderError> for WebGpuError {
+ fn from(err: CommandEncoderError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<QueryError> for WebGpuError {
+ fn from(err: QueryError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<ComputePassError> for WebGpuError {
+ fn from(err: ComputePassError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateComputePipelineError> for WebGpuError {
+ fn from(err: CreateComputePipelineError) -> Self {
+ match err {
+ CreateComputePipelineError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<GetBindGroupLayoutError> for WebGpuError {
+ fn from(err: GetBindGroupLayoutError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateRenderPipelineError> for WebGpuError {
+ fn from(err: CreateRenderPipelineError) -> Self {
+ match err {
+ CreateRenderPipelineError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<RenderPassError> for WebGpuError {
+ fn from(err: RenderPassError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateSamplerError> for WebGpuError {
+ fn from(err: CreateSamplerError) -> Self {
+ match err {
+ CreateSamplerError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateShaderModuleError> for WebGpuError {
+ fn from(err: CreateShaderModuleError) -> Self {
+ match err {
+ CreateShaderModuleError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateTextureError> for WebGpuError {
+ fn from(err: CreateTextureError) -> Self {
+ match err {
+ CreateTextureError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateTextureViewError> for WebGpuError {
+ fn from(err: CreateTextureViewError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateQuerySetError> for WebGpuError {
+ fn from(err: CreateQuerySetError) -> Self {
+ match err {
+ CreateQuerySetError::Device(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<QueueSubmitError> for WebGpuError {
+ fn from(err: QueueSubmitError) -> Self {
+ match err {
+ QueueSubmitError::Queue(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<QueueWriteError> for WebGpuError {
+ fn from(err: QueueWriteError) -> Self {
+ match err {
+ QueueWriteError::Queue(err) => err.into(),
+ err => WebGpuError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<ClearError> for WebGpuError {
+ fn from(err: ClearError) -> Self {
+ WebGpuError::Validation(err.to_string())
+ }
+}
+
+#[derive(Debug)]
+pub struct DomExceptionOperationError {
+ pub msg: String,
+}
+
+impl DomExceptionOperationError {
+ pub fn new(msg: &str) -> Self {
+ DomExceptionOperationError {
+ msg: msg.to_string(),
+ }
+ }
+}
+
+impl fmt::Display for DomExceptionOperationError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.pad(&self.msg)
+ }
+}
+
+impl std::error::Error for DomExceptionOperationError {}
+
+pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
+ e.downcast_ref::<DomExceptionOperationError>()
+ .map(|_| "DOMExceptionOperationError")
+}
diff --git a/ext/webgpu/src/lib.rs b/ext/webgpu/src/lib.rs
new file mode 100644
index 000000000..9d227a51e
--- /dev/null
+++ b/ext/webgpu/src/lib.rs
@@ -0,0 +1,908 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::include_js_files;
+use deno_core::op_async;
+use deno_core::op_sync;
+use deno_core::Extension;
+use deno_core::OpFn;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use serde::Serialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::HashSet;
+use std::rc::Rc;
+pub use wgpu_core;
+pub use wgpu_types;
+use wgpu_types::PowerPreference;
+
+use error::DomExceptionOperationError;
+use error::WebGpuResult;
+
+#[macro_use]
+mod macros {
+ macro_rules! gfx_select {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
+ match $id.backend() {
+ #[cfg(not(target_os = "macos"))]
+ wgpu_types::Backend::Vulkan => $global.$method::<wgpu_core::api::Vulkan>( $($param),* ),
+ #[cfg(target_os = "macos")]
+ wgpu_types::Backend::Metal => $global.$method::<wgpu_core::api::Metal>( $($param),* ),
+ #[cfg(windows)]
+ wgpu_types::Backend::Dx12 => $global.$method::<wgpu_core::api::Dx12>( $($param),* ),
+ #[cfg(all(unix, not(target_os = "macos")))]
+ wgpu_types::Backend::Gl => $global.$method::<wgpu_core::api::Gles>( $($param),+ ),
+ other => panic!("Unexpected backend {:?}", other),
+ }
+ };
+ }
+
+ macro_rules! gfx_put {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* ) => $state:expr, $rc:expr) => {{
+ let (val, maybe_err) = gfx_select!($id => $global.$method($($param),*));
+ let rid = $state.resource_table.add($rc(val));
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+ }};
+ }
+
+ macro_rules! gfx_ok {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {{
+ let maybe_err = gfx_select!($id => $global.$method($($param),*)).err();
+ Ok(WebGpuResult::maybe_err(maybe_err))
+ }};
+ }
+}
+
+pub mod binding;
+pub mod buffer;
+pub mod bundle;
+pub mod command_encoder;
+pub mod compute_pass;
+pub mod error;
+pub mod pipeline;
+pub mod queue;
+pub mod render_pass;
+pub mod sampler;
+pub mod shader;
+pub mod texture;
+
+pub struct Unstable(pub bool);
+
+fn check_unstable(state: &OpState, api_name: &str) {
+ let unstable = state.borrow::<Unstable>();
+ if !unstable.0 {
+ eprintln!(
+ "Unstable API '{}'. The --unstable flag must be provided.",
+ api_name
+ );
+ std::process::exit(70);
+ }
+}
+
+type Instance = wgpu_core::hub::Global<wgpu_core::hub::IdentityManagerFactory>;
+
+struct WebGpuAdapter(wgpu_core::id::AdapterId);
+impl Resource for WebGpuAdapter {
+ fn name(&self) -> Cow<str> {
+ "webGPUAdapter".into()
+ }
+}
+
+struct WebGpuDevice(wgpu_core::id::DeviceId);
+impl Resource for WebGpuDevice {
+ fn name(&self) -> Cow<str> {
+ "webGPUDevice".into()
+ }
+}
+
+struct WebGpuQuerySet(wgpu_core::id::QuerySetId);
+impl Resource for WebGpuQuerySet {
+ fn name(&self) -> Cow<str> {
+ "webGPUQuerySet".into()
+ }
+}
+
+pub fn init(unstable: bool) -> Extension {
+ Extension::builder()
+ .js(include_js_files!(
+ prefix "deno:ext/webgpu",
+ "01_webgpu.js",
+ "02_idl_types.js",
+ ))
+ .ops(declare_webgpu_ops())
+ .state(move |state| {
+ // TODO: check & possibly streamline this
+ // Unstable might be able to be OpMiddleware
+ // let unstable_checker = state.borrow::<super::UnstableChecker>();
+ // let unstable = unstable_checker.unstable;
+ state.put(Unstable(unstable));
+ Ok(())
+ })
+ .build()
+}
+
+fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> {
+ let mut return_features: Vec<&'static str> = vec![];
+
+ if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) {
+ return_features.push("depth-clip-control");
+ }
+ if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
+ return_features.push("pipeline-statistics-query");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
+ return_features.push("texture-compression-bc");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
+ return_features.push("texture-compression-etc2");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR) {
+ return_features.push("texture-compression-astc");
+ }
+ if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
+ return_features.push("timestamp-query");
+ }
+ if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) {
+ return_features.push("indirect-first-instance");
+ }
+
+ // extended from spec
+ if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
+ return_features.push("mappable-primary-buffers");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) {
+ return_features.push("texture-binding-array");
+ }
+ if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) {
+ return_features.push("buffer-binding-array");
+ }
+ if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
+ return_features.push("storage-resource-binding-array");
+ }
+ if features.contains(
+ wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
+ ) {
+ return_features.push("sampled-texture-and-storage-buffer-array-non-uniform-indexing");
+ }
+ if features.contains(
+ wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
+ ) {
+ return_features.push("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing");
+ }
+ if features.contains(wgpu_types::Features::UNSIZED_BINDING_ARRAY) {
+ return_features.push("unsized-binding-array");
+ }
+ if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
+ return_features.push("multi-draw-indirect");
+ }
+ if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
+ return_features.push("multi-draw-indirect-count");
+ }
+ if features.contains(wgpu_types::Features::PUSH_CONSTANTS) {
+ return_features.push("push-constants");
+ }
+ if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
+ return_features.push("address-mode-clamp-to-border");
+ }
+ if features
+ .contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES)
+ {
+ return_features.push("texture-adapter-specific-format-features");
+ }
+ if features.contains(wgpu_types::Features::SHADER_FLOAT64) {
+ return_features.push("shader-float64");
+ }
+ if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
+ return_features.push("vertex-attribute-64bit");
+ }
+ if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) {
+ return_features.push("conservative-rasterization");
+ }
+ if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) {
+ return_features.push("vertex-writable-storage");
+ }
+ if features.contains(wgpu_types::Features::CLEAR_COMMANDS) {
+ return_features.push("clear-texture");
+ }
+ if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) {
+ return_features.push("spirv-shader-passthrough");
+ }
+ if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) {
+ return_features.push("shader-primitive-index");
+ }
+
+ return_features
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RequestAdapterArgs {
+ power_preference: Option<wgpu_types::PowerPreference>,
+ force_fallback_adapter: bool,
+}
+
+#[derive(Serialize)]
+#[serde(untagged)]
+pub enum GpuAdapterDeviceOrErr {
+ Error { err: String },
+ Features(GpuAdapterDevice),
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuAdapterDevice {
+ rid: ResourceId,
+ name: Option<String>,
+ limits: wgpu_types::Limits,
+ features: Vec<&'static str>,
+ is_software: bool,
+}
+
+pub async fn op_webgpu_request_adapter(
+ state: Rc<RefCell<OpState>>,
+ args: RequestAdapterArgs,
+ _: (),
+) -> Result<GpuAdapterDeviceOrErr, AnyError> {
+ let mut state = state.borrow_mut();
+ check_unstable(&state, "navigator.gpu.requestAdapter");
+ let backends = std::env::var("DENO_WEBGPU_BACKEND")
+ .ok()
+ .map_or_else(wgpu_types::Backends::all, |s| {
+ wgpu_core::instance::parse_backends_from_comma_list(&s)
+ });
+ let instance = if let Some(instance) = state.try_borrow::<Instance>() {
+ instance
+ } else {
+ state.put(wgpu_core::hub::Global::new(
+ "webgpu",
+ wgpu_core::hub::IdentityManagerFactory,
+ backends,
+ ));
+ state.borrow::<Instance>()
+ };
+
+ let descriptor = wgpu_core::instance::RequestAdapterOptions {
+ power_preference: match args.power_preference {
+ Some(power_preference) => power_preference,
+ None => PowerPreference::default(),
+ },
+ force_fallback_adapter: args.force_fallback_adapter,
+ compatible_surface: None, // windowless
+ };
+ let res = instance.request_adapter(
+ &descriptor,
+ wgpu_core::instance::AdapterInputs::Mask(backends, |_| {
+ std::marker::PhantomData
+ }),
+ );
+
+ let adapter = match res {
+ Ok(adapter) => adapter,
+ Err(err) => {
+ return Ok(GpuAdapterDeviceOrErr::Error {
+ err: err.to_string(),
+ })
+ }
+ };
+ let name = gfx_select!(adapter => instance.adapter_get_info(adapter))?.name;
+ let adapter_features =
+ gfx_select!(adapter => instance.adapter_features(adapter))?;
+ let features = deserialize_features(&adapter_features);
+ let adapter_limits =
+ gfx_select!(adapter => instance.adapter_limits(adapter))?;
+
+ let rid = state.resource_table.add(WebGpuAdapter(adapter));
+
+ Ok(GpuAdapterDeviceOrErr::Features(GpuAdapterDevice {
+ rid,
+ name: Some(name),
+ features,
+ limits: adapter_limits,
+ is_software: false,
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RequestDeviceArgs {
+ adapter_rid: ResourceId,
+ label: Option<String>,
+ required_features: Option<GpuRequiredFeatures>,
+ required_limits: Option<wgpu_types::Limits>,
+}
+
+#[derive(Deserialize)]
+pub struct GpuRequiredFeatures(HashSet<String>);
+
+impl From<GpuRequiredFeatures> for wgpu_types::Features {
+ fn from(required_features: GpuRequiredFeatures) -> wgpu_types::Features {
+ let mut features: wgpu_types::Features = wgpu_types::Features::empty();
+ features.set(
+ wgpu_types::Features::DEPTH_CLIP_CONTROL,
+ required_features.0.contains("depth-clip-control"),
+ );
+ features.set(
+ wgpu_types::Features::PIPELINE_STATISTICS_QUERY,
+ required_features.0.contains("pipeline-statistics-query"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_COMPRESSION_BC,
+ required_features.0.contains("texture-compression-bc"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_COMPRESSION_ETC2,
+ required_features.0.contains("texture-compression-etc2"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR,
+ required_features.0.contains("texture-compression-astc"),
+ );
+ features.set(
+ wgpu_types::Features::TIMESTAMP_QUERY,
+ required_features.0.contains("timestamp-query"),
+ );
+ features.set(
+ wgpu_types::Features::INDIRECT_FIRST_INSTANCE,
+ required_features.0.contains("indirect-first-instance"),
+ );
+
+ // extended from spec
+ features.set(
+ wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS,
+ required_features.0.contains("mappable-primary-buffers"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_BINDING_ARRAY,
+ required_features.0.contains("texture-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::BUFFER_BINDING_ARRAY,
+ required_features.0.contains("buffer-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY,
+ required_features
+ .0
+ .contains("storage-resource-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
+ required_features
+ .0
+ .contains("sampled-texture-and-storage-buffer-array-non-uniform-indexing"),
+ );
+ features.set(
+ wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
+ required_features
+ .0
+ .contains("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing"),
+ );
+ features.set(
+ wgpu_types::Features::UNSIZED_BINDING_ARRAY,
+ required_features.0.contains("unsized-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::MULTI_DRAW_INDIRECT,
+ required_features.0.contains("multi-draw-indirect"),
+ );
+ features.set(
+ wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT,
+ required_features.0.contains("multi-draw-indirect-count"),
+ );
+ features.set(
+ wgpu_types::Features::PUSH_CONSTANTS,
+ required_features.0.contains("push-constants"),
+ );
+ features.set(
+ wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
+ required_features.0.contains("address-mode-clamp-to-border"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
+ required_features
+ .0
+ .contains("texture-adapter-specific-format-features"),
+ );
+ features.set(
+ wgpu_types::Features::SHADER_FLOAT64,
+ required_features.0.contains("shader-float64"),
+ );
+ features.set(
+ wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT,
+ required_features.0.contains("vertex-attribute-64bit"),
+ );
+ features.set(
+ wgpu_types::Features::CONSERVATIVE_RASTERIZATION,
+ required_features.0.contains("conservative-rasterization"),
+ );
+ features.set(
+ wgpu_types::Features::VERTEX_WRITABLE_STORAGE,
+ required_features.0.contains("vertex-writable-storage"),
+ );
+ features.set(
+ wgpu_types::Features::CLEAR_COMMANDS,
+ required_features.0.contains("clear-commands"),
+ );
+ features.set(
+ wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH,
+ required_features.0.contains("spirv-shader-passthrough"),
+ );
+ features.set(
+ wgpu_types::Features::SHADER_PRIMITIVE_INDEX,
+ required_features.0.contains("shader-primitive-index"),
+ );
+
+ features
+ }
+}
+
+pub async fn op_webgpu_request_device(
+ state: Rc<RefCell<OpState>>,
+ args: RequestDeviceArgs,
+ _: (),
+) -> Result<GpuAdapterDevice, AnyError> {
+ let mut state = state.borrow_mut();
+ let adapter_resource = state
+ .resource_table
+ .get::<WebGpuAdapter>(args.adapter_rid)?;
+ let adapter = adapter_resource.0;
+ let instance = state.borrow::<Instance>();
+
+ let descriptor = wgpu_types::DeviceDescriptor {
+ label: args.label.map(Cow::from),
+ features: args.required_features.map(Into::into).unwrap_or_default(),
+ limits: args.required_limits.map(Into::into).unwrap_or_default(),
+ };
+
+ let (device, maybe_err) = gfx_select!(adapter => instance.adapter_request_device(
+ adapter,
+ &descriptor,
+ std::env::var("DENO_WEBGPU_TRACE").ok().as_ref().map(std::path::Path::new),
+ std::marker::PhantomData
+ ));
+ if let Some(err) = maybe_err {
+ return Err(DomExceptionOperationError::new(&err.to_string()).into());
+ }
+
+ let device_features =
+ gfx_select!(device => instance.device_features(device))?;
+ let features = deserialize_features(&device_features);
+ let limits = gfx_select!(device => instance.device_limits(device))?;
+
+ let rid = state.resource_table.add(WebGpuDevice(device));
+
+ Ok(GpuAdapterDevice {
+ rid,
+ name: None,
+ features,
+ limits,
+ // TODO(lucacasonato): report correctly from wgpu
+ is_software: false,
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateQuerySetArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ #[serde(flatten)]
+ r#type: GpuQueryType,
+ count: u32,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case", tag = "type")]
+enum GpuQueryType {
+ Occlusion,
+ #[serde(rename_all = "camelCase")]
+ PipelineStatistics {
+ pipeline_statistics: HashSet<String>,
+ },
+ Timestamp,
+}
+
+impl From<GpuQueryType> for wgpu_types::QueryType {
+ fn from(query_type: GpuQueryType) -> Self {
+ match query_type {
+ GpuQueryType::Occlusion => wgpu_types::QueryType::Occlusion,
+ GpuQueryType::PipelineStatistics {
+ pipeline_statistics,
+ } => {
+ use wgpu_types::PipelineStatisticsTypes;
+
+ let mut types = PipelineStatisticsTypes::empty();
+
+ if pipeline_statistics.contains("vertex-shader-invocations") {
+ types.set(PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS, true);
+ }
+ if pipeline_statistics.contains("clipper-invocations") {
+ types.set(PipelineStatisticsTypes::CLIPPER_INVOCATIONS, true);
+ }
+ if pipeline_statistics.contains("clipper-primitives-out") {
+ types.set(PipelineStatisticsTypes::CLIPPER_PRIMITIVES_OUT, true);
+ }
+ if pipeline_statistics.contains("fragment-shader-invocations") {
+ types.set(PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS, true);
+ }
+ if pipeline_statistics.contains("compute-shader-invocations") {
+ types.set(PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS, true);
+ }
+
+ wgpu_types::QueryType::PipelineStatistics(types)
+ }
+ GpuQueryType::Timestamp => wgpu_types::QueryType::Timestamp,
+ }
+ }
+}
+
+pub fn op_webgpu_create_query_set(
+ state: &mut OpState,
+ args: CreateQuerySetArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let device_resource =
+ state.resource_table.get::<WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+ let instance = &state.borrow::<Instance>();
+
+ let descriptor = wgpu_types::QuerySetDescriptor {
+ label: args.label.map(Cow::from),
+ ty: args.r#type.into(),
+ count: args.count,
+ };
+
+ gfx_put!(device => instance.device_create_query_set(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ) => state, WebGpuQuerySet)
+}
+
+fn declare_webgpu_ops() -> Vec<(&'static str, Box<OpFn>)> {
+ vec![
+ // Request device/adapter
+ (
+ "op_webgpu_request_adapter",
+ op_async(op_webgpu_request_adapter),
+ ),
+ (
+ "op_webgpu_request_device",
+ op_async(op_webgpu_request_device),
+ ),
+ // Query Set
+ (
+ "op_webgpu_create_query_set",
+ op_sync(op_webgpu_create_query_set),
+ ),
+ // buffer
+ (
+ "op_webgpu_create_buffer",
+ op_sync(buffer::op_webgpu_create_buffer),
+ ),
+ (
+ "op_webgpu_buffer_get_mapped_range",
+ op_sync(buffer::op_webgpu_buffer_get_mapped_range),
+ ),
+ (
+ "op_webgpu_buffer_unmap",
+ op_sync(buffer::op_webgpu_buffer_unmap),
+ ),
+ // buffer async
+ (
+ "op_webgpu_buffer_get_map_async",
+ op_async(buffer::op_webgpu_buffer_get_map_async),
+ ),
+ // remaining sync ops
+
+ // texture
+ (
+ "op_webgpu_create_texture",
+ op_sync(texture::op_webgpu_create_texture),
+ ),
+ (
+ "op_webgpu_create_texture_view",
+ op_sync(texture::op_webgpu_create_texture_view),
+ ),
+ // sampler
+ (
+ "op_webgpu_create_sampler",
+ op_sync(sampler::op_webgpu_create_sampler),
+ ),
+ // binding
+ (
+ "op_webgpu_create_bind_group_layout",
+ op_sync(binding::op_webgpu_create_bind_group_layout),
+ ),
+ (
+ "op_webgpu_create_pipeline_layout",
+ op_sync(binding::op_webgpu_create_pipeline_layout),
+ ),
+ (
+ "op_webgpu_create_bind_group",
+ op_sync(binding::op_webgpu_create_bind_group),
+ ),
+ // pipeline
+ (
+ "op_webgpu_create_compute_pipeline",
+ op_sync(pipeline::op_webgpu_create_compute_pipeline),
+ ),
+ (
+ "op_webgpu_compute_pipeline_get_bind_group_layout",
+ op_sync(pipeline::op_webgpu_compute_pipeline_get_bind_group_layout),
+ ),
+ (
+ "op_webgpu_create_render_pipeline",
+ op_sync(pipeline::op_webgpu_create_render_pipeline),
+ ),
+ (
+ "op_webgpu_render_pipeline_get_bind_group_layout",
+ op_sync(pipeline::op_webgpu_render_pipeline_get_bind_group_layout),
+ ),
+ // command_encoder
+ (
+ "op_webgpu_create_command_encoder",
+ op_sync(command_encoder::op_webgpu_create_command_encoder),
+ ),
+ (
+ "op_webgpu_command_encoder_begin_render_pass",
+ op_sync(command_encoder::op_webgpu_command_encoder_begin_render_pass),
+ ),
+ (
+ "op_webgpu_command_encoder_begin_compute_pass",
+ op_sync(command_encoder::op_webgpu_command_encoder_begin_compute_pass),
+ ),
+ (
+ "op_webgpu_command_encoder_copy_buffer_to_buffer",
+ op_sync(command_encoder::op_webgpu_command_encoder_copy_buffer_to_buffer),
+ ),
+ (
+ "op_webgpu_command_encoder_copy_buffer_to_texture",
+ op_sync(
+ command_encoder::op_webgpu_command_encoder_copy_buffer_to_texture,
+ ),
+ ),
+ (
+ "op_webgpu_command_encoder_copy_texture_to_buffer",
+ op_sync(
+ command_encoder::op_webgpu_command_encoder_copy_texture_to_buffer,
+ ),
+ ),
+ (
+ "op_webgpu_command_encoder_copy_texture_to_texture",
+ op_sync(
+ command_encoder::op_webgpu_command_encoder_copy_texture_to_texture,
+ ),
+ ),
+ (
+ "op_webgpu_command_encoder_clear_buffer",
+ op_sync(command_encoder::op_webgpu_command_encoder_clear_buffer),
+ ),
+ (
+ "op_webgpu_command_encoder_push_debug_group",
+ op_sync(command_encoder::op_webgpu_command_encoder_push_debug_group),
+ ),
+ (
+ "op_webgpu_command_encoder_pop_debug_group",
+ op_sync(command_encoder::op_webgpu_command_encoder_pop_debug_group),
+ ),
+ (
+ "op_webgpu_command_encoder_insert_debug_marker",
+ op_sync(command_encoder::op_webgpu_command_encoder_insert_debug_marker),
+ ),
+ (
+ "op_webgpu_command_encoder_write_timestamp",
+ op_sync(command_encoder::op_webgpu_command_encoder_write_timestamp),
+ ),
+ (
+ "op_webgpu_command_encoder_resolve_query_set",
+ op_sync(command_encoder::op_webgpu_command_encoder_resolve_query_set),
+ ),
+ (
+ "op_webgpu_command_encoder_finish",
+ op_sync(command_encoder::op_webgpu_command_encoder_finish),
+ ),
+ // render_pass
+ (
+ "op_webgpu_render_pass_set_viewport",
+ op_sync(render_pass::op_webgpu_render_pass_set_viewport),
+ ),
+ (
+ "op_webgpu_render_pass_set_scissor_rect",
+ op_sync(render_pass::op_webgpu_render_pass_set_scissor_rect),
+ ),
+ (
+ "op_webgpu_render_pass_set_blend_constant",
+ op_sync(render_pass::op_webgpu_render_pass_set_blend_constant),
+ ),
+ (
+ "op_webgpu_render_pass_set_stencil_reference",
+ op_sync(render_pass::op_webgpu_render_pass_set_stencil_reference),
+ ),
+ (
+ "op_webgpu_render_pass_begin_pipeline_statistics_query",
+ op_sync(
+ render_pass::op_webgpu_render_pass_begin_pipeline_statistics_query,
+ ),
+ ),
+ (
+ "op_webgpu_render_pass_end_pipeline_statistics_query",
+ op_sync(render_pass::op_webgpu_render_pass_end_pipeline_statistics_query),
+ ),
+ (
+ "op_webgpu_render_pass_write_timestamp",
+ op_sync(render_pass::op_webgpu_render_pass_write_timestamp),
+ ),
+ (
+ "op_webgpu_render_pass_execute_bundles",
+ op_sync(render_pass::op_webgpu_render_pass_execute_bundles),
+ ),
+ (
+ "op_webgpu_render_pass_end_pass",
+ op_sync(render_pass::op_webgpu_render_pass_end_pass),
+ ),
+ (
+ "op_webgpu_render_pass_set_bind_group",
+ op_sync(render_pass::op_webgpu_render_pass_set_bind_group),
+ ),
+ (
+ "op_webgpu_render_pass_push_debug_group",
+ op_sync(render_pass::op_webgpu_render_pass_push_debug_group),
+ ),
+ (
+ "op_webgpu_render_pass_pop_debug_group",
+ op_sync(render_pass::op_webgpu_render_pass_pop_debug_group),
+ ),
+ (
+ "op_webgpu_render_pass_insert_debug_marker",
+ op_sync(render_pass::op_webgpu_render_pass_insert_debug_marker),
+ ),
+ (
+ "op_webgpu_render_pass_set_pipeline",
+ op_sync(render_pass::op_webgpu_render_pass_set_pipeline),
+ ),
+ (
+ "op_webgpu_render_pass_set_index_buffer",
+ op_sync(render_pass::op_webgpu_render_pass_set_index_buffer),
+ ),
+ (
+ "op_webgpu_render_pass_set_vertex_buffer",
+ op_sync(render_pass::op_webgpu_render_pass_set_vertex_buffer),
+ ),
+ (
+ "op_webgpu_render_pass_draw",
+ op_sync(render_pass::op_webgpu_render_pass_draw),
+ ),
+ (
+ "op_webgpu_render_pass_draw_indexed",
+ op_sync(render_pass::op_webgpu_render_pass_draw_indexed),
+ ),
+ (
+ "op_webgpu_render_pass_draw_indirect",
+ op_sync(render_pass::op_webgpu_render_pass_draw_indirect),
+ ),
+ (
+ "op_webgpu_render_pass_draw_indexed_indirect",
+ op_sync(render_pass::op_webgpu_render_pass_draw_indexed_indirect),
+ ),
+ // compute_pass
+ (
+ "op_webgpu_compute_pass_set_pipeline",
+ op_sync(compute_pass::op_webgpu_compute_pass_set_pipeline),
+ ),
+ (
+ "op_webgpu_compute_pass_dispatch",
+ op_sync(compute_pass::op_webgpu_compute_pass_dispatch),
+ ),
+ (
+ "op_webgpu_compute_pass_dispatch_indirect",
+ op_sync(compute_pass::op_webgpu_compute_pass_dispatch_indirect),
+ ),
+ (
+ "op_webgpu_compute_pass_begin_pipeline_statistics_query",
+ op_sync(
+ compute_pass::op_webgpu_compute_pass_begin_pipeline_statistics_query,
+ ),
+ ),
+ (
+ "op_webgpu_compute_pass_end_pipeline_statistics_query",
+ op_sync(
+ compute_pass::op_webgpu_compute_pass_end_pipeline_statistics_query,
+ ),
+ ),
+ (
+ "op_webgpu_compute_pass_write_timestamp",
+ op_sync(compute_pass::op_webgpu_compute_pass_write_timestamp),
+ ),
+ (
+ "op_webgpu_compute_pass_end_pass",
+ op_sync(compute_pass::op_webgpu_compute_pass_end_pass),
+ ),
+ (
+ "op_webgpu_compute_pass_set_bind_group",
+ op_sync(compute_pass::op_webgpu_compute_pass_set_bind_group),
+ ),
+ (
+ "op_webgpu_compute_pass_push_debug_group",
+ op_sync(compute_pass::op_webgpu_compute_pass_push_debug_group),
+ ),
+ (
+ "op_webgpu_compute_pass_pop_debug_group",
+ op_sync(compute_pass::op_webgpu_compute_pass_pop_debug_group),
+ ),
+ (
+ "op_webgpu_compute_pass_insert_debug_marker",
+ op_sync(compute_pass::op_webgpu_compute_pass_insert_debug_marker),
+ ),
+ // bundle
+ (
+ "op_webgpu_create_render_bundle_encoder",
+ op_sync(bundle::op_webgpu_create_render_bundle_encoder),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_finish",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_finish),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_set_bind_group",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_set_bind_group),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_push_debug_group",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_push_debug_group),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_pop_debug_group",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_pop_debug_group),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_insert_debug_marker",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_insert_debug_marker),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_set_pipeline",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_set_pipeline),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_set_index_buffer",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_set_index_buffer),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_set_vertex_buffer",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_set_vertex_buffer),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_draw",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_draw),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_draw_indexed",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_draw_indexed),
+ ),
+ (
+ "op_webgpu_render_bundle_encoder_draw_indirect",
+ op_sync(bundle::op_webgpu_render_bundle_encoder_draw_indirect),
+ ),
+ // queue
+ (
+ "op_webgpu_queue_submit",
+ op_sync(queue::op_webgpu_queue_submit),
+ ),
+ (
+ "op_webgpu_write_buffer",
+ op_sync(queue::op_webgpu_write_buffer),
+ ),
+ (
+ "op_webgpu_write_texture",
+ op_sync(queue::op_webgpu_write_texture),
+ ),
+ // shader
+ (
+ "op_webgpu_create_shader_module",
+ op_sync(shader::op_webgpu_create_shader_module),
+ ),
+ ]
+}
diff --git a/ext/webgpu/src/queue.rs b/ext/webgpu/src/queue.rs
new file mode 100644
index 000000000..a662c4ead
--- /dev/null
+++ b/ext/webgpu/src/queue.rs
@@ -0,0 +1,142 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use std::num::NonZeroU32;
+
+use deno_core::error::AnyError;
+use deno_core::OpState;
+use deno_core::ResourceId;
+use deno_core::ZeroCopyBuf;
+use serde::Deserialize;
+
+use super::error::WebGpuResult;
+
+type WebGpuQueue = super::WebGpuDevice;
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct QueueSubmitArgs {
+ queue_rid: ResourceId,
+ command_buffers: Vec<ResourceId>,
+}
+
+pub fn op_webgpu_queue_submit(
+ state: &mut OpState,
+ args: QueueSubmitArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let queue_resource =
+ state.resource_table.get::<WebGpuQueue>(args.queue_rid)?;
+ let queue = queue_resource.0;
+
+ let mut ids = vec![];
+
+ for rid in args.command_buffers {
+ let buffer_resource =
+ state
+ .resource_table
+ .get::<super::command_encoder::WebGpuCommandBuffer>(rid)?;
+ ids.push(buffer_resource.0);
+ }
+
+ let maybe_err =
+ gfx_select!(queue => instance.queue_submit(queue, &ids)).err();
+
+ Ok(WebGpuResult::maybe_err(maybe_err))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuImageDataLayout {
+ offset: u64,
+ bytes_per_row: Option<u32>,
+ rows_per_image: Option<u32>,
+}
+
+impl From<GpuImageDataLayout> for wgpu_types::ImageDataLayout {
+ fn from(layout: GpuImageDataLayout) -> Self {
+ wgpu_types::ImageDataLayout {
+ offset: layout.offset,
+ bytes_per_row: NonZeroU32::new(layout.bytes_per_row.unwrap_or(0)),
+ rows_per_image: NonZeroU32::new(layout.rows_per_image.unwrap_or(0)),
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct QueueWriteBufferArgs {
+ queue_rid: ResourceId,
+ buffer: ResourceId,
+ buffer_offset: u64,
+ data_offset: usize,
+ size: Option<usize>,
+}
+
+pub fn op_webgpu_write_buffer(
+ state: &mut OpState,
+ args: QueueWriteBufferArgs,
+ zero_copy: ZeroCopyBuf,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.buffer)?;
+ let buffer = buffer_resource.0;
+ let queue_resource =
+ state.resource_table.get::<WebGpuQueue>(args.queue_rid)?;
+ let queue = queue_resource.0;
+
+ let data = match args.size {
+ Some(size) => &zero_copy[args.data_offset..(args.data_offset + size)],
+ None => &zero_copy[args.data_offset..],
+ };
+ let maybe_err = gfx_select!(queue => instance.queue_write_buffer(
+ queue,
+ buffer,
+ args.buffer_offset,
+ data
+ ))
+ .err();
+
+ Ok(WebGpuResult::maybe_err(maybe_err))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct QueueWriteTextureArgs {
+ queue_rid: ResourceId,
+ destination: super::command_encoder::GpuImageCopyTexture,
+ data_layout: GpuImageDataLayout,
+ size: wgpu_types::Extent3d,
+}
+
+pub fn op_webgpu_write_texture(
+ state: &mut OpState,
+ args: QueueWriteTextureArgs,
+ zero_copy: ZeroCopyBuf,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let texture_resource = state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(args.destination.texture)?;
+ let queue_resource =
+ state.resource_table.get::<WebGpuQueue>(args.queue_rid)?;
+ let queue = queue_resource.0;
+
+ let destination = wgpu_core::command::ImageCopyTexture {
+ texture: texture_resource.0,
+ mip_level: args.destination.mip_level,
+ origin: args.destination.origin,
+ aspect: args.destination.aspect,
+ };
+ let data_layout = args.data_layout.into();
+
+ gfx_ok!(queue => instance.queue_write_texture(
+ queue,
+ &destination,
+ &*zero_copy,
+ &data_layout,
+ &args.size
+ ))
+}
diff --git a/ext/webgpu/src/render_pass.rs b/ext/webgpu/src/render_pass.rs
new file mode 100644
index 000000000..469bf727e
--- /dev/null
+++ b/ext/webgpu/src/render_pass.rs
@@ -0,0 +1,649 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuRenderPass(
+ pub(crate) RefCell<wgpu_core::command::RenderPass>,
+);
+impl Resource for WebGpuRenderPass {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderPass".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetViewportArgs {
+ render_pass_rid: ResourceId,
+ x: f32,
+ y: f32,
+ width: f32,
+ height: f32,
+ min_depth: f32,
+ max_depth: f32,
+}
+
+pub fn op_webgpu_render_pass_set_viewport(
+ state: &mut OpState,
+ args: RenderPassSetViewportArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_viewport(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.x,
+ args.y,
+ args.width,
+ args.height,
+ args.min_depth,
+ args.max_depth,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetScissorRectArgs {
+ render_pass_rid: ResourceId,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+}
+
+pub fn op_webgpu_render_pass_set_scissor_rect(
+ state: &mut OpState,
+ args: RenderPassSetScissorRectArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_scissor_rect(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.x,
+ args.y,
+ args.width,
+ args.height,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetBlendConstantArgs {
+ render_pass_rid: ResourceId,
+ color: wgpu_types::Color,
+}
+
+pub fn op_webgpu_render_pass_set_blend_constant(
+ state: &mut OpState,
+ args: RenderPassSetBlendConstantArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_blend_constant(
+ &mut render_pass_resource.0.borrow_mut(),
+ &args.color,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetStencilReferenceArgs {
+ render_pass_rid: ResourceId,
+ reference: u32,
+}
+
+pub fn op_webgpu_render_pass_set_stencil_reference(
+ state: &mut OpState,
+ args: RenderPassSetStencilReferenceArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_stencil_reference(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.reference,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassBeginPipelineStatisticsQueryArgs {
+ render_pass_rid: ResourceId,
+ query_set: u32,
+ query_index: u32,
+}
+
+pub fn op_webgpu_render_pass_begin_pipeline_statistics_query(
+ state: &mut OpState,
+ args: RenderPassBeginPipelineStatisticsQueryArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(args.query_set)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_begin_pipeline_statistics_query(
+ &mut render_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassEndPipelineStatisticsQueryArgs {
+ render_pass_rid: ResourceId,
+}
+
+pub fn op_webgpu_render_pass_end_pipeline_statistics_query(
+ state: &mut OpState,
+ args: RenderPassEndPipelineStatisticsQueryArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_end_pipeline_statistics_query(
+ &mut render_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassWriteTimestampArgs {
+ render_pass_rid: ResourceId,
+ query_set: u32,
+ query_index: u32,
+}
+
+pub fn op_webgpu_render_pass_write_timestamp(
+ state: &mut OpState,
+ args: RenderPassWriteTimestampArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(args.query_set)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_write_timestamp(
+ &mut render_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassExecuteBundlesArgs {
+ render_pass_rid: ResourceId,
+ bundles: Vec<u32>,
+}
+
+pub fn op_webgpu_render_pass_execute_bundles(
+ state: &mut OpState,
+ args: RenderPassExecuteBundlesArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let mut render_bundle_ids = vec![];
+
+ for rid in &args.bundles {
+ let render_bundle_resource =
+ state
+ .resource_table
+ .get::<super::bundle::WebGpuRenderBundle>(*rid)?;
+ render_bundle_ids.push(render_bundle_resource.0);
+ }
+
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_execute_bundles(
+ &mut render_pass_resource.0.borrow_mut(),
+ render_bundle_ids.as_ptr(),
+ render_bundle_ids.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassEndPassArgs {
+ command_encoder_rid: ResourceId,
+ render_pass_rid: ResourceId,
+}
+
+pub fn op_webgpu_render_pass_end_pass(
+ state: &mut OpState,
+ args: RenderPassEndPassArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<super::command_encoder::WebGpuCommandEncoder>(
+ args.command_encoder_rid,
+ )?;
+ let command_encoder = command_encoder_resource.0;
+ let render_pass_resource = state
+ .resource_table
+ .take::<WebGpuRenderPass>(args.render_pass_rid)?;
+ let render_pass = &render_pass_resource.0.borrow();
+ let instance = state.borrow::<super::Instance>();
+
+ gfx_ok!(command_encoder => instance.command_encoder_run_render_pass(command_encoder, render_pass))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetBindGroupArgs {
+ render_pass_rid: ResourceId,
+ index: u32,
+ bind_group: u32,
+ dynamic_offsets_data: ZeroCopyBuf,
+ dynamic_offsets_data_start: usize,
+ dynamic_offsets_data_length: usize,
+}
+
+pub fn op_webgpu_render_pass_set_bind_group(
+ state: &mut OpState,
+ args: RenderPassSetBindGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let bind_group_resource =
+ state
+ .resource_table
+ .get::<super::binding::WebGpuBindGroup>(args.bind_group)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ // Align the data
+ assert!(args.dynamic_offsets_data_start % std::mem::size_of::<u32>() == 0);
+ // SAFETY: A u8 to u32 cast is safe because we asserted that the length is a
+ // multiple of 4.
+ let (prefix, dynamic_offsets_data, suffix) =
+ unsafe { args.dynamic_offsets_data.align_to::<u32>() };
+ assert!(prefix.is_empty());
+ assert!(suffix.is_empty());
+
+ let start = args.dynamic_offsets_data_start;
+ let len = args.dynamic_offsets_data_length;
+
+ // Assert that length and start are both in bounds
+ assert!(start <= dynamic_offsets_data.len());
+ assert!(len <= dynamic_offsets_data.len() - start);
+
+ let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_bind_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ dynamic_offsets_data.as_ptr(),
+ dynamic_offsets_data.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassPushDebugGroupArgs {
+ render_pass_rid: ResourceId,
+ group_label: String,
+}
+
+pub fn op_webgpu_render_pass_push_debug_group(
+ state: &mut OpState,
+ args: RenderPassPushDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ let label = std::ffi::CString::new(args.group_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_push_debug_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassPopDebugGroupArgs {
+ render_pass_rid: ResourceId,
+}
+
+pub fn op_webgpu_render_pass_pop_debug_group(
+ state: &mut OpState,
+ args: RenderPassPopDebugGroupArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_pop_debug_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassInsertDebugMarkerArgs {
+ render_pass_rid: ResourceId,
+ marker_label: String,
+}
+
+pub fn op_webgpu_render_pass_insert_debug_marker(
+ state: &mut OpState,
+ args: RenderPassInsertDebugMarkerArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ let label = std::ffi::CString::new(args.marker_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_insert_debug_marker(
+ &mut render_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetPipelineArgs {
+ render_pass_rid: ResourceId,
+ pipeline: u32,
+}
+
+pub fn op_webgpu_render_pass_set_pipeline(
+ state: &mut OpState,
+ args: RenderPassSetPipelineArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pipeline_resource =
+ state
+ .resource_table
+ .get::<super::pipeline::WebGpuRenderPipeline>(args.pipeline)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_pipeline(
+ &mut render_pass_resource.0.borrow_mut(),
+ render_pipeline_resource.0,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetIndexBufferArgs {
+ render_pass_rid: ResourceId,
+ buffer: u32,
+ index_format: wgpu_types::IndexFormat,
+ offset: u64,
+ size: Option<u64>,
+}
+
+pub fn op_webgpu_render_pass_set_index_buffer(
+ state: &mut OpState,
+ args: RenderPassSetIndexBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ let size = if let Some(size) = args.size {
+ Some(
+ std::num::NonZeroU64::new(size)
+ .ok_or_else(|| type_error("size must be larger than 0"))?,
+ )
+ } else {
+ None
+ };
+
+ render_pass_resource.0.borrow_mut().set_index_buffer(
+ buffer_resource.0,
+ args.index_format,
+ args.offset,
+ size,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetVertexBufferArgs {
+ render_pass_rid: ResourceId,
+ slot: u32,
+ buffer: u32,
+ offset: u64,
+ size: Option<u64>,
+}
+
+pub fn op_webgpu_render_pass_set_vertex_buffer(
+ state: &mut OpState,
+ args: RenderPassSetVertexBufferArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ let size = if let Some(size) = args.size {
+ Some(
+ std::num::NonZeroU64::new(size)
+ .ok_or_else(|| type_error("size must be larger than 0"))?,
+ )
+ } else {
+ None
+ };
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_vertex_buffer(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.slot,
+ buffer_resource.0,
+ args.offset,
+ size,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawArgs {
+ render_pass_rid: ResourceId,
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_pass_draw(
+ state: &mut OpState,
+ args: RenderPassDrawArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.vertex_count,
+ args.instance_count,
+ args.first_vertex,
+ args.first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawIndexedArgs {
+ render_pass_rid: ResourceId,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_pass_draw_indexed(
+ state: &mut OpState,
+ args: RenderPassDrawIndexedArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.index_count,
+ args.instance_count,
+ args.first_index,
+ args.base_vertex,
+ args.first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawIndirectArgs {
+ render_pass_rid: ResourceId,
+ indirect_buffer: u32,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_render_pass_draw_indirect(
+ state: &mut OpState,
+ args: RenderPassDrawIndirectArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.indirect_buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indirect(
+ &mut render_pass_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawIndexedIndirectArgs {
+ render_pass_rid: ResourceId,
+ indirect_buffer: u32,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_render_pass_draw_indexed_indirect(
+ state: &mut OpState,
+ args: RenderPassDrawIndexedIndirectArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(args.indirect_buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed_indirect(
+ &mut render_pass_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
diff --git a/ext/webgpu/src/shader.rs b/ext/webgpu/src/shader.rs
new file mode 100644
index 000000000..60290de8b
--- /dev/null
+++ b/ext/webgpu/src/shader.rs
@@ -0,0 +1,52 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuShaderModule(pub(crate) wgpu_core::id::ShaderModuleId);
+impl Resource for WebGpuShaderModule {
+ fn name(&self) -> Cow<str> {
+ "webGPUShaderModule".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateShaderModuleArgs {
+ device_rid: ResourceId,
+ label: Option<String>,
+ code: String,
+ _source_map: Option<()>, // not yet implemented
+}
+
+pub fn op_webgpu_create_shader_module(
+ state: &mut OpState,
+ args: CreateShaderModuleArgs,
+ _: (),
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.0;
+
+ let source =
+ wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::from(args.code));
+
+ let descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
+ label: args.label.map(Cow::from),
+ shader_bound_checks: wgpu_types::ShaderBoundChecks::default(),
+ };
+
+ gfx_put!(device => instance.device_create_shader_module(
+ device,
+ &descriptor,
+ source,
+ std::marker::PhantomData
+ ) => state, WebGpuShaderModule)
+}