diff options
Diffstat (limited to 'ext/webgpu/pipeline.rs')
-rw-r--r-- | ext/webgpu/pipeline.rs | 686 |
1 files changed, 686 insertions, 0 deletions
diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs new file mode 100644 index 000000000..6d11179a4 --- /dev/null +++ b/ext/webgpu/pipeline.rs @@ -0,0 +1,686 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::bad_resource_id; +use deno_core::error::AnyError; +use deno_core::ResourceId; +use deno_core::{OpState, Resource}; +use serde::Deserialize; +use serde::Serialize; +use std::borrow::Cow; + +use super::error::{WebGpuError, WebGpuResult}; + +pub(crate) struct WebGpuPipelineLayout( + pub(crate) wgpu_core::id::PipelineLayoutId, +); +impl Resource for WebGpuPipelineLayout { + fn name(&self) -> Cow<str> { + "webGPUPipelineLayout".into() + } +} + +pub(crate) struct WebGpuComputePipeline( + pub(crate) wgpu_core::id::ComputePipelineId, +); +impl Resource for WebGpuComputePipeline { + fn name(&self) -> Cow<str> { + "webGPUComputePipeline".into() + } +} + +pub(crate) struct WebGpuRenderPipeline( + pub(crate) wgpu_core::id::RenderPipelineId, +); +impl Resource for WebGpuRenderPipeline { + fn name(&self) -> Cow<str> { + "webGPURenderPipeline".into() + } +} + +pub fn serialize_index_format(format: String) -> wgpu_types::IndexFormat { + match format.as_str() { + "uint16" => wgpu_types::IndexFormat::Uint16, + "uint32" => wgpu_types::IndexFormat::Uint32, + _ => unreachable!(), + } +} + +fn serialize_stencil_operation( + operation: &str, +) -> wgpu_types::StencilOperation { + match operation { + "keep" => wgpu_types::StencilOperation::Keep, + "zero" => wgpu_types::StencilOperation::Zero, + "replace" => wgpu_types::StencilOperation::Replace, + "invert" => wgpu_types::StencilOperation::Invert, + "increment-clamp" => wgpu_types::StencilOperation::IncrementClamp, + "decrement-clamp" => wgpu_types::StencilOperation::DecrementClamp, + "increment-wrap" => wgpu_types::StencilOperation::IncrementWrap, + "decrement-wrap" => wgpu_types::StencilOperation::DecrementWrap, + _ => unreachable!(), + } +} + +fn serialize_stencil_face_state( + state: GpuStencilFaceState, +) -> wgpu_types::StencilFaceState { + wgpu_types::StencilFaceState { + compare: state + .compare + .as_ref() + .map_or(wgpu_types::CompareFunction::Always, |op| { + super::sampler::serialize_compare_function(op) + }), + fail_op: state + .fail_op + .as_ref() + .map_or(wgpu_types::StencilOperation::Keep, |op| { + serialize_stencil_operation(op) + }), + depth_fail_op: state + .depth_fail_op + .as_ref() + .map_or(wgpu_types::StencilOperation::Keep, |op| { + serialize_stencil_operation(op) + }), + pass_op: state + .pass_op + .as_ref() + .map_or(wgpu_types::StencilOperation::Keep, |op| { + serialize_stencil_operation(op) + }), + } +} + +fn serialize_blend_factor(blend_factor: &str) -> wgpu_types::BlendFactor { + match blend_factor { + "zero" => wgpu_types::BlendFactor::Zero, + "one" => wgpu_types::BlendFactor::One, + "src" => wgpu_types::BlendFactor::Src, + "one-minus-src" => wgpu_types::BlendFactor::OneMinusSrc, + "src-alpha" => wgpu_types::BlendFactor::SrcAlpha, + "one-minus-src-alpha" => wgpu_types::BlendFactor::OneMinusSrcAlpha, + "dst" => wgpu_types::BlendFactor::Dst, + "one-minus-dst" => wgpu_types::BlendFactor::OneMinusDst, + "dst-alpha" => wgpu_types::BlendFactor::DstAlpha, + "one-minus-dst-alpha" => wgpu_types::BlendFactor::OneMinusDstAlpha, + "src-alpha-saturated" => wgpu_types::BlendFactor::SrcAlphaSaturated, + "constant" => wgpu_types::BlendFactor::Constant, + "one-minus-constant" => wgpu_types::BlendFactor::OneMinusConstant, + _ => unreachable!(), + } +} + +fn serialize_blend_state(state: GpuBlendState) -> wgpu_types::BlendState { + wgpu_types::BlendState { + alpha: serialize_blend_component(state.alpha), + color: serialize_blend_component(state.color), + } +} + +fn serialize_blend_component( + blend: GpuBlendComponent, +) -> wgpu_types::BlendComponent { + wgpu_types::BlendComponent { + src_factor: blend + .src_factor + .as_ref() + .map_or(wgpu_types::BlendFactor::One, |factor| { + serialize_blend_factor(factor) + }), + dst_factor: blend + .dst_factor + .as_ref() + .map_or(wgpu_types::BlendFactor::Zero, |factor| { + serialize_blend_factor(factor) + }), + operation: match &blend.operation { + Some(operation) => match operation.as_str() { + "add" => wgpu_types::BlendOperation::Add, + "subtract" => wgpu_types::BlendOperation::Subtract, + "reverse-subtract" => wgpu_types::BlendOperation::ReverseSubtract, + "min" => wgpu_types::BlendOperation::Min, + "max" => wgpu_types::BlendOperation::Max, + _ => unreachable!(), + }, + None => wgpu_types::BlendOperation::Add, + }, + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuProgrammableStage { + module: u32, + entry_point: String, + // constants: HashMap<String, GPUPipelineConstantValue> +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateComputePipelineArgs { + device_rid: ResourceId, + label: Option<String>, + layout: Option<u32>, + compute: GpuProgrammableStage, +} + +pub fn op_webgpu_create_compute_pipeline( + state: &mut OpState, + args: CreateComputePipelineArgs, + _: (), +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let device_resource = state + .resource_table + .get::<super::WebGpuDevice>(args.device_rid) + .ok_or_else(bad_resource_id)?; + let device = device_resource.0; + + let pipeline_layout = if let Some(rid) = args.layout { + let id = state + .resource_table + .get::<WebGpuPipelineLayout>(rid) + .ok_or_else(bad_resource_id)?; + Some(id.0) + } else { + None + }; + + let compute_shader_module_resource = state + .resource_table + .get::<super::shader::WebGpuShaderModule>(args.compute.module) + .ok_or_else(bad_resource_id)?; + + let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor { + label: args.label.map(Cow::from), + layout: pipeline_layout, + stage: wgpu_core::pipeline::ProgrammableStageDescriptor { + module: compute_shader_module_resource.0, + entry_point: Cow::from(args.compute.entry_point), + // TODO(lucacasonato): support args.compute.constants + }, + }; + let implicit_pipelines = match args.layout { + Some(_) => None, + None => Some(wgpu_core::device::ImplicitPipelineIds { + root_id: std::marker::PhantomData, + group_ids: &[std::marker::PhantomData; wgpu_core::MAX_BIND_GROUPS], + }), + }; + + let (compute_pipeline, maybe_err) = gfx_select!(device => instance.device_create_compute_pipeline( + device, + &descriptor, + std::marker::PhantomData, + implicit_pipelines + )); + + let rid = state + .resource_table + .add(WebGpuComputePipeline(compute_pipeline)); + + Ok(WebGpuResult::rid_err(rid, maybe_err)) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ComputePipelineGetBindGroupLayoutArgs { + compute_pipeline_rid: ResourceId, + index: u32, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PipelineLayout { + rid: ResourceId, + label: String, + err: Option<WebGpuError>, +} + +pub fn op_webgpu_compute_pipeline_get_bind_group_layout( + state: &mut OpState, + args: ComputePipelineGetBindGroupLayoutArgs, + _: (), +) -> Result<PipelineLayout, AnyError> { + let instance = state.borrow::<super::Instance>(); + let compute_pipeline_resource = state + .resource_table + .get::<WebGpuComputePipeline>(args.compute_pipeline_rid) + .ok_or_else(bad_resource_id)?; + let compute_pipeline = compute_pipeline_resource.0; + + let (bind_group_layout, maybe_err) = gfx_select!(compute_pipeline => instance.compute_pipeline_get_bind_group_layout(compute_pipeline, args.index, std::marker::PhantomData)); + + let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout)); + + let rid = state + .resource_table + .add(super::binding::WebGpuBindGroupLayout(bind_group_layout)); + + Ok(PipelineLayout { + rid, + label, + err: maybe_err.map(WebGpuError::from), + }) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuPrimitiveState { + topology: Option<String>, + strip_index_format: Option<String>, + front_face: Option<String>, + cull_mode: Option<String>, + clamp_depth: bool, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +struct GpuBlendComponent { + src_factor: Option<String>, + dst_factor: Option<String>, + operation: Option<String>, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +struct GpuBlendState { + color: GpuBlendComponent, + alpha: GpuBlendComponent, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuColorTargetState { + format: String, + blend: Option<GpuBlendState>, + write_mask: Option<u32>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuStencilFaceState { + compare: Option<String>, + fail_op: Option<String>, + depth_fail_op: Option<String>, + pass_op: Option<String>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuDepthStencilState { + format: String, + depth_write_enabled: Option<bool>, + depth_compare: Option<String>, + stencil_front: Option<GpuStencilFaceState>, + stencil_back: Option<GpuStencilFaceState>, + stencil_read_mask: Option<u32>, + stencil_write_mask: Option<u32>, + depth_bias: Option<i32>, + depth_bias_slope_scale: Option<f32>, + depth_bias_clamp: Option<f32>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuVertexAttribute { + format: GpuVertexFormat, + offset: u64, + shader_location: u32, +} + +#[derive(Deserialize)] +#[serde(rename_all = "lowercase")] +enum GpuVertexFormat { + Uint8x2, + Uint8x4, + Sint8x2, + Sint8x4, + Unorm8x2, + Unorm8x4, + Snorm8x2, + Snorm8x4, + Uint16x2, + Uint16x4, + Sint16x2, + Sint16x4, + Unorm16x2, + Unorm16x4, + Snorm16x2, + Snorm16x4, + Float16x2, + Float16x4, + Float32, + Float32x2, + Float32x3, + Float32x4, + Uint32, + Uint32x2, + Uint32x3, + Uint32x4, + Sint32, + Sint32x2, + Sint32x3, + Sint32x4, + Float64, + Float64x2, + Float64x3, + Float64x4, +} + +impl From<GpuVertexFormat> for wgpu_types::VertexFormat { + fn from(vf: GpuVertexFormat) -> wgpu_types::VertexFormat { + use wgpu_types::VertexFormat; + match vf { + GpuVertexFormat::Uint8x2 => VertexFormat::Uint8x2, + GpuVertexFormat::Uint8x4 => VertexFormat::Uint8x4, + GpuVertexFormat::Sint8x2 => VertexFormat::Sint8x2, + GpuVertexFormat::Sint8x4 => VertexFormat::Sint8x4, + GpuVertexFormat::Unorm8x2 => VertexFormat::Unorm8x2, + GpuVertexFormat::Unorm8x4 => VertexFormat::Unorm8x4, + GpuVertexFormat::Snorm8x2 => VertexFormat::Snorm8x2, + GpuVertexFormat::Snorm8x4 => VertexFormat::Snorm8x4, + GpuVertexFormat::Uint16x2 => VertexFormat::Uint16x2, + GpuVertexFormat::Uint16x4 => VertexFormat::Uint16x4, + GpuVertexFormat::Sint16x2 => VertexFormat::Sint16x2, + GpuVertexFormat::Sint16x4 => VertexFormat::Sint16x4, + GpuVertexFormat::Unorm16x2 => VertexFormat::Unorm16x2, + GpuVertexFormat::Unorm16x4 => VertexFormat::Unorm16x4, + GpuVertexFormat::Snorm16x2 => VertexFormat::Snorm16x2, + GpuVertexFormat::Snorm16x4 => VertexFormat::Snorm16x4, + GpuVertexFormat::Float16x2 => VertexFormat::Float16x2, + GpuVertexFormat::Float16x4 => VertexFormat::Float16x4, + GpuVertexFormat::Float32 => VertexFormat::Float32, + GpuVertexFormat::Float32x2 => VertexFormat::Float32x2, + GpuVertexFormat::Float32x3 => VertexFormat::Float32x3, + GpuVertexFormat::Float32x4 => VertexFormat::Float32x4, + GpuVertexFormat::Uint32 => VertexFormat::Uint32, + GpuVertexFormat::Uint32x2 => VertexFormat::Uint32x2, + GpuVertexFormat::Uint32x3 => VertexFormat::Uint32x3, + GpuVertexFormat::Uint32x4 => VertexFormat::Uint32x4, + GpuVertexFormat::Sint32 => VertexFormat::Sint32, + GpuVertexFormat::Sint32x2 => VertexFormat::Sint32x2, + GpuVertexFormat::Sint32x3 => VertexFormat::Sint32x3, + GpuVertexFormat::Sint32x4 => VertexFormat::Sint32x4, + GpuVertexFormat::Float64 => VertexFormat::Float64, + GpuVertexFormat::Float64x2 => VertexFormat::Float64x2, + GpuVertexFormat::Float64x3 => VertexFormat::Float64x3, + GpuVertexFormat::Float64x4 => VertexFormat::Float64x4, + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuVertexBufferLayout { + array_stride: u64, + step_mode: Option<String>, + attributes: Vec<GpuVertexAttribute>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuVertexState { + module: u32, + entry_point: String, + buffers: Option<Vec<Option<GpuVertexBufferLayout>>>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuMultisampleState { + count: Option<u32>, + mask: Option<u64>, // against spec, but future proof + alpha_to_coverage_enabled: Option<bool>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuFragmentState { + targets: Vec<GpuColorTargetState>, + module: u32, + entry_point: String, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateRenderPipelineArgs { + device_rid: ResourceId, + label: Option<String>, + layout: Option<u32>, + vertex: GpuVertexState, + primitive: Option<GpuPrimitiveState>, + depth_stencil: Option<GpuDepthStencilState>, + multisample: Option<GpuMultisampleState>, + fragment: Option<GpuFragmentState>, +} + +pub fn op_webgpu_create_render_pipeline( + state: &mut OpState, + args: CreateRenderPipelineArgs, + _: (), +) -> Result<WebGpuResult, AnyError> { + let instance = state.borrow::<super::Instance>(); + let device_resource = state + .resource_table + .get::<super::WebGpuDevice>(args.device_rid) + .ok_or_else(bad_resource_id)?; + let device = device_resource.0; + + let layout = if let Some(rid) = args.layout { + let pipeline_layout_resource = state + .resource_table + .get::<WebGpuPipelineLayout>(rid) + .ok_or_else(bad_resource_id)?; + Some(pipeline_layout_resource.0) + } else { + None + }; + + let vertex_shader_module_resource = state + .resource_table + .get::<super::shader::WebGpuShaderModule>(args.vertex.module) + .ok_or_else(bad_resource_id)?; + + let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { + label: args.label.map(Cow::from), + layout, + vertex: wgpu_core::pipeline::VertexState { + stage: wgpu_core::pipeline::ProgrammableStageDescriptor { + module: vertex_shader_module_resource.0, + entry_point: Cow::from(args.vertex.entry_point), + }, + buffers: Cow::from(if let Some(buffers) = args.vertex.buffers { + let mut return_buffers = vec![]; + for buffer in buffers.into_iter().flatten() { + return_buffers.push(wgpu_core::pipeline::VertexBufferLayout { + array_stride: buffer.array_stride, + step_mode: match buffer.step_mode { + Some(step_mode) => match step_mode.as_str() { + "vertex" => wgpu_types::InputStepMode::Vertex, + "instance" => wgpu_types::InputStepMode::Instance, + _ => unreachable!(), + }, + None => wgpu_types::InputStepMode::Vertex, + }, + attributes: Cow::from( + buffer + .attributes + .into_iter() + .map(|attribute| wgpu_types::VertexAttribute { + format: attribute.format.into(), + offset: attribute.offset, + shader_location: attribute.shader_location, + }) + .collect::<Vec<wgpu_types::VertexAttribute>>(), + ), + }); + } + return_buffers + } else { + vec![] + }), + }, + primitive: args.primitive.map_or(Default::default(), |primitive| { + wgpu_types::PrimitiveState { + topology: match primitive.topology { + Some(topology) => match topology.as_str() { + "point-list" => wgpu_types::PrimitiveTopology::PointList, + "line-list" => wgpu_types::PrimitiveTopology::LineList, + "line-strip" => wgpu_types::PrimitiveTopology::LineStrip, + "triangle-list" => wgpu_types::PrimitiveTopology::TriangleList, + "triangle-strip" => wgpu_types::PrimitiveTopology::TriangleStrip, + _ => unreachable!(), + }, + None => wgpu_types::PrimitiveTopology::TriangleList, + }, + strip_index_format: primitive + .strip_index_format + .map(serialize_index_format), + front_face: match primitive.front_face { + Some(front_face) => match front_face.as_str() { + "ccw" => wgpu_types::FrontFace::Ccw, + "cw" => wgpu_types::FrontFace::Cw, + _ => unreachable!(), + }, + None => wgpu_types::FrontFace::Ccw, + }, + cull_mode: match primitive.cull_mode { + Some(cull_mode) => match cull_mode.as_str() { + "none" => None, + "front" => Some(wgpu_types::Face::Front), + "back" => Some(wgpu_types::Face::Back), + _ => unreachable!(), + }, + None => None, + }, + polygon_mode: Default::default(), // native-only + conservative: false, // native-only + clamp_depth: primitive.clamp_depth, + } + }), + depth_stencil: args.depth_stencil.map(|depth_stencil| { + wgpu_types::DepthStencilState { + format: super::texture::serialize_texture_format(&depth_stencil.format) + .unwrap(), + depth_write_enabled: depth_stencil.depth_write_enabled.unwrap_or(false), + depth_compare: match depth_stencil.depth_compare { + Some(depth_compare) => { + super::sampler::serialize_compare_function(&depth_compare) + } + None => wgpu_types::CompareFunction::Always, + }, + stencil: wgpu_types::StencilState { + front: depth_stencil + .stencil_front + .map_or(Default::default(), serialize_stencil_face_state), + back: depth_stencil + .stencil_back + .map_or(Default::default(), serialize_stencil_face_state), + read_mask: depth_stencil.stencil_read_mask.unwrap_or(0xFFFFFFFF), + write_mask: depth_stencil.stencil_write_mask.unwrap_or(0xFFFFFFFF), + }, + bias: wgpu_types::DepthBiasState { + constant: depth_stencil.depth_bias.unwrap_or(0), + slope_scale: depth_stencil.depth_bias_slope_scale.unwrap_or(0.0), + clamp: depth_stencil.depth_bias_clamp.unwrap_or(0.0), + }, + } + }), + multisample: args.multisample.map_or(Default::default(), |multisample| { + wgpu_types::MultisampleState { + count: multisample.count.unwrap_or(1), + mask: multisample.mask.unwrap_or(0xFFFFFFFF), + alpha_to_coverage_enabled: multisample + .alpha_to_coverage_enabled + .unwrap_or(false), + } + }), + fragment: args.fragment.map(|fragment| { + let fragment_shader_module_resource = state + .resource_table + .get::<super::shader::WebGpuShaderModule>(fragment.module) + .ok_or_else(bad_resource_id) + .unwrap(); + + wgpu_core::pipeline::FragmentState { + stage: wgpu_core::pipeline::ProgrammableStageDescriptor { + module: fragment_shader_module_resource.0, + entry_point: Cow::from(fragment.entry_point), + }, + targets: Cow::from( + fragment + .targets + .into_iter() + .map(|target| wgpu_types::ColorTargetState { + format: super::texture::serialize_texture_format(&target.format) + .unwrap(), + blend: target.blend.map(serialize_blend_state), + write_mask: target + .write_mask + .map_or(Default::default(), |mask| { + wgpu_types::ColorWrite::from_bits(mask).unwrap() + }), + }) + .collect::<Vec<wgpu_types::ColorTargetState>>(), + ), + } + }), + }; + + let implicit_pipelines = match args.layout { + Some(_) => None, + None => Some(wgpu_core::device::ImplicitPipelineIds { + root_id: std::marker::PhantomData, + group_ids: &[std::marker::PhantomData; wgpu_core::MAX_BIND_GROUPS], + }), + }; + + let (render_pipeline, maybe_err) = gfx_select!(device => instance.device_create_render_pipeline( + device, + &descriptor, + std::marker::PhantomData, + implicit_pipelines + )); + + let rid = state + .resource_table + .add(WebGpuRenderPipeline(render_pipeline)); + + Ok(WebGpuResult::rid_err(rid, maybe_err)) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RenderPipelineGetBindGroupLayoutArgs { + render_pipeline_rid: ResourceId, + index: u32, +} + +pub fn op_webgpu_render_pipeline_get_bind_group_layout( + state: &mut OpState, + args: RenderPipelineGetBindGroupLayoutArgs, + _: (), +) -> Result<PipelineLayout, AnyError> { + let instance = state.borrow::<super::Instance>(); + let render_pipeline_resource = state + .resource_table + .get::<WebGpuRenderPipeline>(args.render_pipeline_rid) + .ok_or_else(bad_resource_id)?; + let render_pipeline = render_pipeline_resource.0; + + let (bind_group_layout, maybe_err) = gfx_select!(render_pipeline => instance.render_pipeline_get_bind_group_layout(render_pipeline, args.index, std::marker::PhantomData)); + + let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout)); + + let rid = state + .resource_table + .add(super::binding::WebGpuBindGroupLayout(bind_group_layout)); + + Ok(PipelineLayout { + rid, + label, + err: maybe_err.map(WebGpuError::from), + }) +} |