diff options
author | Leo Kettmeir <crowlkats@toaxl.com> | 2023-12-09 01:19:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-09 01:19:16 +0100 |
commit | 393abed3873d83019feb5bcebb10a6929133862a (patch) | |
tree | c346e6d628e6b037fb8f881a70ca2ae6f70692b6 /ext/webgpu/command_encoder.rs | |
parent | 123d9ea047a2e10803e260ebf00f31fcc98463ee (diff) |
feat: bring back WebGPU (#20812)
Signed-off-by: Leo Kettmeir <crowlkats@toaxl.com>
Co-authored-by: Kenta Moriuchi <moriken@kimamass.com>
Co-authored-by: Bartek IwaĆczuk <biwanczuk@gmail.com>
Diffstat (limited to 'ext/webgpu/command_encoder.rs')
-rw-r--r-- | ext/webgpu/command_encoder.rs | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/ext/webgpu/command_encoder.rs b/ext/webgpu/command_encoder.rs new file mode 100644 index 000000000..c5947abaf --- /dev/null +++ b/ext/webgpu/command_encoder.rs @@ -0,0 +1,633 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use crate::WebGpuQuerySet; +use deno_core::error::AnyError; +use deno_core::op2; +use deno_core::OpState; +use deno_core::Resource; +use deno_core::ResourceId; +use serde::Deserialize; +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; + +use super::error::WebGpuResult; + +pub(crate) struct WebGpuCommandEncoder( + pub(crate) super::Instance, + pub(crate) wgpu_core::id::CommandEncoderId, // TODO: should maybe be option? +); +impl Resource for WebGpuCommandEncoder { + fn name(&self) -> Cow<str> { + "webGPUCommandEncoder".into() + } + + fn close(self: Rc<Self>) { + let instance = &self.0; + gfx_select!(self.1 => instance.command_encoder_drop(self.1)); + } +} + +pub(crate) struct WebGpuCommandBuffer( + pub(crate) super::Instance, + pub(crate) RefCell<Option<wgpu_core::id::CommandBufferId>>, +); +impl Resource for WebGpuCommandBuffer { + fn name(&self) -> Cow<str> { + "webGPUCommandBuffer".into() + } + + fn close(self: Rc<Self>) { + if let Some(id) = *self.1.borrow() { + let instance = &self.0; + gfx_select!(id => instance.command_buffer_drop(id)); + } + } +} + +#[op2] +#[serde] +pub fn op_webgpu_create_command_encoder( + state: &mut OpState, + #[smi] device_rid: ResourceId, + #[string] label: Cow<str>, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let device_resource = state + .resource_table + .get::<super::WebGpuDevice>(device_rid)?; + let device = device_resource.1; + + let descriptor = wgpu_types::CommandEncoderDescriptor { label: Some(label) }; + + gfx_put!(device => instance.device_create_command_encoder( + device, + &descriptor, + () + ) => state, WebGpuCommandEncoder) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GpuRenderPassColorAttachment { + view: ResourceId, + resolve_target: Option<ResourceId>, + clear_value: Option<wgpu_types::Color>, + load_op: wgpu_core::command::LoadOp, + store_op: wgpu_core::command::StoreOp, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GpuRenderPassDepthStencilAttachment { + view: ResourceId, + depth_clear_value: f32, + depth_load_op: Option<wgpu_core::command::LoadOp>, + depth_store_op: Option<wgpu_core::command::StoreOp>, + depth_read_only: bool, + stencil_clear_value: u32, + stencil_load_op: Option<wgpu_core::command::LoadOp>, + stencil_store_op: Option<wgpu_core::command::StoreOp>, + stencil_read_only: bool, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GPURenderPassTimestampWrites { + query_set: ResourceId, + beginning_of_pass_write_index: Option<u32>, + end_of_pass_write_index: Option<u32>, +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_begin_render_pass( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[string] label: Cow<str>, + #[serde] color_attachments: Vec<Option<GpuRenderPassColorAttachment>>, + #[serde] depth_stencil_attachment: Option< + GpuRenderPassDepthStencilAttachment, + >, + #[smi] occlusion_query_set: Option<ResourceId>, + #[serde] timestamp_writes: Option<GPURenderPassTimestampWrites>, +) -> Result<WebGpuResult, AnyError> { + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + + let color_attachments = color_attachments + .into_iter() + .map(|color_attachment| { + let rp_at = if let Some(at) = color_attachment.as_ref() { + let texture_view_resource = + state + .resource_table + .get::<super::texture::WebGpuTextureView>(at.view)?; + + let resolve_target = at + .resolve_target + .map(|rid| { + state + .resource_table + .get::<super::texture::WebGpuTextureView>(rid) + }) + .transpose()? + .map(|texture| texture.1); + + Some(wgpu_core::command::RenderPassColorAttachment { + view: texture_view_resource.1, + resolve_target, + channel: wgpu_core::command::PassChannel { + load_op: at.load_op, + store_op: at.store_op, + clear_value: at.clear_value.unwrap_or_default(), + read_only: false, + }, + }) + } else { + None + }; + Ok(rp_at) + }) + .collect::<Result<Vec<_>, AnyError>>()?; + + let mut processed_depth_stencil_attachment = None; + + if let Some(attachment) = depth_stencil_attachment { + let texture_view_resource = + state + .resource_table + .get::<super::texture::WebGpuTextureView>(attachment.view)?; + + processed_depth_stencil_attachment = + Some(wgpu_core::command::RenderPassDepthStencilAttachment { + view: texture_view_resource.1, + depth: wgpu_core::command::PassChannel { + load_op: attachment + .depth_load_op + .unwrap_or(wgpu_core::command::LoadOp::Load), + store_op: attachment + .depth_store_op + .unwrap_or(wgpu_core::command::StoreOp::Store), + clear_value: attachment.depth_clear_value, + read_only: attachment.depth_read_only, + }, + stencil: wgpu_core::command::PassChannel { + load_op: attachment + .stencil_load_op + .unwrap_or(wgpu_core::command::LoadOp::Load), + store_op: attachment + .stencil_store_op + .unwrap_or(wgpu_core::command::StoreOp::Store), + clear_value: attachment.stencil_clear_value, + read_only: attachment.stencil_read_only, + }, + }); + } + + let timestamp_writes = if let Some(timestamp_writes) = timestamp_writes { + let query_set_resource = state + .resource_table + .get::<WebGpuQuerySet>(timestamp_writes.query_set)?; + let query_set = query_set_resource.1; + + Some(wgpu_core::command::RenderPassTimestampWrites { + query_set, + beginning_of_pass_write_index: timestamp_writes + .beginning_of_pass_write_index, + end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, + }) + } else { + None + }; + + let occlusion_query_set_resource = occlusion_query_set + .map(|rid| state.resource_table.get::<WebGpuQuerySet>(rid)) + .transpose()? + .map(|query_set| query_set.1); + + let descriptor = wgpu_core::command::RenderPassDescriptor { + label: Some(label), + color_attachments: Cow::from(color_attachments), + depth_stencil_attachment: processed_depth_stencil_attachment.as_ref(), + timestamp_writes: timestamp_writes.as_ref(), + occlusion_query_set: occlusion_query_set_resource, + }; + + let render_pass = wgpu_core::command::RenderPass::new( + command_encoder_resource.1, + &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 GPUComputePassTimestampWrites { + query_set: ResourceId, + beginning_of_pass_write_index: Option<u32>, + end_of_pass_write_index: Option<u32>, +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_begin_compute_pass( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[string] label: Cow<str>, + #[serde] timestamp_writes: Option<GPUComputePassTimestampWrites>, +) -> Result<WebGpuResult, AnyError> { + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + + let timestamp_writes = if let Some(timestamp_writes) = timestamp_writes { + let query_set_resource = state + .resource_table + .get::<WebGpuQuerySet>(timestamp_writes.query_set)?; + let query_set = query_set_resource.1; + + Some(wgpu_core::command::ComputePassTimestampWrites { + query_set, + beginning_of_pass_write_index: timestamp_writes + .beginning_of_pass_write_index, + end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, + }) + } else { + None + }; + + let descriptor = wgpu_core::command::ComputePassDescriptor { + label: Some(label), + timestamp_writes: timestamp_writes.as_ref(), + }; + + let compute_pass = wgpu_core::command::ComputePass::new( + command_encoder_resource.1, + &descriptor, + ); + + let rid = state + .resource_table + .add(super::compute_pass::WebGpuComputePass(RefCell::new( + compute_pass, + ))); + + Ok(WebGpuResult::rid(rid)) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_copy_buffer_to_buffer( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[smi] source: ResourceId, + #[number] source_offset: u64, + #[smi] destination: ResourceId, + #[number] destination_offset: u64, + #[number] size: u64, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_buffer_resource = state + .resource_table + .get::<super::buffer::WebGpuBuffer>(source)?; + let source_buffer = source_buffer_resource.1; + let destination_buffer_resource = + state + .resource_table + .get::<super::buffer::WebGpuBuffer>(destination)?; + let destination_buffer = destination_buffer_resource.1; + + gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_buffer( + command_encoder, + source_buffer, + source_offset, + destination_buffer, + destination_offset, + 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, +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_copy_buffer_to_texture( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[serde] source: GpuImageCopyBuffer, + #[serde] destination: GpuImageCopyTexture, + #[serde] copy_size: wgpu_types::Extent3d, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_buffer_resource = + state + .resource_table + .get::<super::buffer::WebGpuBuffer>(source.buffer)?; + let destination_texture_resource = + state + .resource_table + .get::<super::texture::WebGpuTexture>(destination.texture)?; + + let source = wgpu_core::command::ImageCopyBuffer { + buffer: source_buffer_resource.1, + layout: wgpu_types::ImageDataLayout { + offset: source.offset, + bytes_per_row: source.bytes_per_row, + rows_per_image: source.rows_per_image, + }, + }; + let destination = wgpu_core::command::ImageCopyTexture { + texture: destination_texture_resource.id, + mip_level: destination.mip_level, + origin: destination.origin, + aspect: destination.aspect, + }; + gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_texture( + command_encoder, + &source, + &destination, + ©_size + )) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_copy_texture_to_buffer( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[serde] source: GpuImageCopyTexture, + #[serde] destination: GpuImageCopyBuffer, + #[serde] copy_size: wgpu_types::Extent3d, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_texture_resource = + state + .resource_table + .get::<super::texture::WebGpuTexture>(source.texture)?; + let destination_buffer_resource = + state + .resource_table + .get::<super::buffer::WebGpuBuffer>(destination.buffer)?; + + let source = wgpu_core::command::ImageCopyTexture { + texture: source_texture_resource.id, + mip_level: source.mip_level, + origin: source.origin, + aspect: source.aspect, + }; + let destination = wgpu_core::command::ImageCopyBuffer { + buffer: destination_buffer_resource.1, + layout: wgpu_types::ImageDataLayout { + offset: destination.offset, + bytes_per_row: destination.bytes_per_row, + rows_per_image: destination.rows_per_image, + }, + }; + gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_buffer( + command_encoder, + &source, + &destination, + ©_size + )) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_copy_texture_to_texture( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[serde] source: GpuImageCopyTexture, + #[serde] destination: GpuImageCopyTexture, + #[serde] copy_size: wgpu_types::Extent3d, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_texture_resource = + state + .resource_table + .get::<super::texture::WebGpuTexture>(source.texture)?; + let destination_texture_resource = + state + .resource_table + .get::<super::texture::WebGpuTexture>(destination.texture)?; + + let source = wgpu_core::command::ImageCopyTexture { + texture: source_texture_resource.id, + mip_level: source.mip_level, + origin: source.origin, + aspect: source.aspect, + }; + let destination = wgpu_core::command::ImageCopyTexture { + texture: destination_texture_resource.id, + mip_level: destination.mip_level, + origin: destination.origin, + aspect: destination.aspect, + }; + gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_texture( + command_encoder, + &source, + &destination, + ©_size + )) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_clear_buffer( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[smi] buffer_rid: ResourceId, + #[number] offset: u64, + #[number] size: u64, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let destination_resource = state + .resource_table + .get::<super::buffer::WebGpuBuffer>(buffer_rid)?; + + gfx_ok!(command_encoder => instance.command_encoder_clear_buffer( + command_encoder, + destination_resource.1, + offset, + std::num::NonZeroU64::new(size) + )) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_push_debug_group( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[string] group_label: &str, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + + gfx_ok!(command_encoder => instance.command_encoder_push_debug_group(command_encoder, group_label)) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_pop_debug_group( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + + gfx_ok!(command_encoder => instance.command_encoder_pop_debug_group(command_encoder)) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_insert_debug_marker( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[string] marker_label: &str, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + + gfx_ok!(command_encoder => instance.command_encoder_insert_debug_marker( + command_encoder, + marker_label + )) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_write_timestamp( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[smi] query_set: ResourceId, + query_index: u32, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let query_set_resource = state + .resource_table + .get::<super::WebGpuQuerySet>(query_set)?; + + gfx_ok!(command_encoder => instance.command_encoder_write_timestamp( + command_encoder, + query_set_resource.1, + query_index + )) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_resolve_query_set( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[smi] query_set: ResourceId, + first_query: u32, + query_count: u32, + #[smi] destination: ResourceId, + #[number] destination_offset: u64, +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let command_encoder_resource = state + .resource_table + .get::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let query_set_resource = state + .resource_table + .get::<super::WebGpuQuerySet>(query_set)?; + let destination_resource = state + .resource_table + .get::<super::buffer::WebGpuBuffer>(destination)?; + + gfx_ok!(command_encoder => instance.command_encoder_resolve_query_set( + command_encoder, + query_set_resource.1, + first_query, + query_count, + destination_resource.1, + destination_offset + )) +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_finish( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[string] label: Cow<str>, +) -> Result<WebGpuResult, AnyError> { + let command_encoder_resource = state + .resource_table + .take::<WebGpuCommandEncoder>(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let instance = state.borrow::<super::Instance>(); + + let descriptor = wgpu_types::CommandBufferDescriptor { label: Some(label) }; + + let (val, maybe_err) = gfx_select!(command_encoder => instance.command_encoder_finish( + command_encoder, + &descriptor + )); + + let rid = state.resource_table.add(WebGpuCommandBuffer( + instance.clone(), + RefCell::new(Some(val)), + )); + + Ok(WebGpuResult::rid_err(rid, maybe_err)) +} |