diff options
author | Aaron O'Mullan <aaron.omullan@gmail.com> | 2022-01-20 15:23:53 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-20 15:23:53 +0100 |
commit | 3ab68bd0a2aff6df12388f2c3b5ed7ae3333a6ca (patch) | |
tree | 9a81824deb4e5a2b29c3eeb5a2adaa3e00720c45 /ext/webgpu/pipeline.rs | |
parent | 1cc38f5155bdc5605d74cd959660fa04f782ac63 (diff) |
revert(#13402): experiment: wgpu sync (#13439)
Diffstat (limited to 'ext/webgpu/pipeline.rs')
-rw-r--r-- | ext/webgpu/pipeline.rs | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs new file mode 100644 index 000000000..1d22bdba0 --- /dev/null +++ b/ext/webgpu/pipeline.rs @@ -0,0 +1,791 @@ +// 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 serde::Serialize; +use std::borrow::Cow; + +use crate::sampler::GpuCompareFunction; +use crate::texture::GpuTextureFormat; + +use super::error::{WebGpuError, WebGpuResult}; + +const MAX_BIND_GROUPS: usize = 8; + +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() + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GpuIndexFormat { + Uint16, + Uint32, +} + +impl From<GpuIndexFormat> for wgpu_types::IndexFormat { + fn from(value: GpuIndexFormat) -> wgpu_types::IndexFormat { + match value { + GpuIndexFormat::Uint16 => wgpu_types::IndexFormat::Uint16, + GpuIndexFormat::Uint32 => wgpu_types::IndexFormat::Uint32, + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GPUStencilOperation { + Keep, + Zero, + Replace, + Invert, + IncrementClamp, + DecrementClamp, + IncrementWrap, + DecrementWrap, +} + +impl From<GPUStencilOperation> for wgpu_types::StencilOperation { + fn from(value: GPUStencilOperation) -> wgpu_types::StencilOperation { + match value { + GPUStencilOperation::Keep => wgpu_types::StencilOperation::Keep, + GPUStencilOperation::Zero => wgpu_types::StencilOperation::Zero, + GPUStencilOperation::Replace => wgpu_types::StencilOperation::Replace, + GPUStencilOperation::Invert => wgpu_types::StencilOperation::Invert, + GPUStencilOperation::IncrementClamp => { + wgpu_types::StencilOperation::IncrementClamp + } + GPUStencilOperation::DecrementClamp => { + wgpu_types::StencilOperation::DecrementClamp + } + GPUStencilOperation::IncrementWrap => { + wgpu_types::StencilOperation::IncrementWrap + } + GPUStencilOperation::DecrementWrap => { + wgpu_types::StencilOperation::DecrementWrap + } + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GpuBlendFactor { + Zero, + One, + Src, + OneMinusSrc, + SrcAlpha, + OneMinusSrcAlpha, + Dst, + OneMinusDst, + DstAlpha, + OneMinusDstAlpha, + SrcAlphaSaturated, + Constant, + OneMinusConstant, +} + +impl From<GpuBlendFactor> for wgpu_types::BlendFactor { + fn from(value: GpuBlendFactor) -> wgpu_types::BlendFactor { + match value { + GpuBlendFactor::Zero => wgpu_types::BlendFactor::Zero, + GpuBlendFactor::One => wgpu_types::BlendFactor::One, + GpuBlendFactor::Src => wgpu_types::BlendFactor::Src, + GpuBlendFactor::OneMinusSrc => wgpu_types::BlendFactor::OneMinusSrc, + GpuBlendFactor::SrcAlpha => wgpu_types::BlendFactor::SrcAlpha, + GpuBlendFactor::OneMinusSrcAlpha => { + wgpu_types::BlendFactor::OneMinusSrcAlpha + } + GpuBlendFactor::Dst => wgpu_types::BlendFactor::Dst, + GpuBlendFactor::OneMinusDst => wgpu_types::BlendFactor::OneMinusDst, + GpuBlendFactor::DstAlpha => wgpu_types::BlendFactor::DstAlpha, + GpuBlendFactor::OneMinusDstAlpha => { + wgpu_types::BlendFactor::OneMinusDstAlpha + } + GpuBlendFactor::SrcAlphaSaturated => { + wgpu_types::BlendFactor::SrcAlphaSaturated + } + GpuBlendFactor::Constant => wgpu_types::BlendFactor::Constant, + GpuBlendFactor::OneMinusConstant => { + wgpu_types::BlendFactor::OneMinusConstant + } + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GpuBlendOperation { + Add, + Subtract, + ReverseSubtract, + Min, + Max, +} + +impl From<GpuBlendOperation> for wgpu_types::BlendOperation { + fn from(value: GpuBlendOperation) -> wgpu_types::BlendOperation { + match value { + GpuBlendOperation::Add => wgpu_types::BlendOperation::Add, + GpuBlendOperation::Subtract => wgpu_types::BlendOperation::Subtract, + GpuBlendOperation::ReverseSubtract => { + wgpu_types::BlendOperation::ReverseSubtract + } + GpuBlendOperation::Min => wgpu_types::BlendOperation::Min, + GpuBlendOperation::Max => wgpu_types::BlendOperation::Max, + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GpuPrimitiveTopology { + PointList, + LineList, + LineStrip, + TriangleList, + TriangleStrip, +} + +impl From<GpuPrimitiveTopology> for wgpu_types::PrimitiveTopology { + fn from(value: GpuPrimitiveTopology) -> wgpu_types::PrimitiveTopology { + match value { + GpuPrimitiveTopology::PointList => { + wgpu_types::PrimitiveTopology::PointList + } + GpuPrimitiveTopology::LineList => wgpu_types::PrimitiveTopology::LineList, + GpuPrimitiveTopology::LineStrip => { + wgpu_types::PrimitiveTopology::LineStrip + } + GpuPrimitiveTopology::TriangleList => { + wgpu_types::PrimitiveTopology::TriangleList + } + GpuPrimitiveTopology::TriangleStrip => { + wgpu_types::PrimitiveTopology::TriangleStrip + } + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GpuFrontFace { + Ccw, + Cw, +} + +impl From<GpuFrontFace> for wgpu_types::FrontFace { + fn from(value: GpuFrontFace) -> wgpu_types::FrontFace { + match value { + GpuFrontFace::Ccw => wgpu_types::FrontFace::Ccw, + GpuFrontFace::Cw => wgpu_types::FrontFace::Cw, + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum GpuCullMode { + None, + Front, + Back, +} + +impl From<GpuCullMode> for Option<wgpu_types::Face> { + fn from(value: GpuCullMode) -> Option<wgpu_types::Face> { + match value { + GpuCullMode::None => None, + GpuCullMode::Front => Some(wgpu_types::Face::Front), + GpuCullMode::Back => Some(wgpu_types::Face::Back), + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuProgrammableStage { + module: ResourceId, + entry_point: String, + // constants: HashMap<String, GPUPipelineConstantValue> +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateComputePipelineArgs { + device_rid: ResourceId, + label: Option<String>, + layout: Option<ResourceId>, + 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)?; + let device = device_resource.0; + + let pipeline_layout = if let Some(rid) = args.layout { + let id = state.resource_table.get::<WebGpuPipelineLayout>(rid)?; + Some(id.0) + } else { + None + }; + + let compute_shader_module_resource = + state + .resource_table + .get::<super::shader::WebGpuShaderModule>(args.compute.module)?; + + 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; 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)?; + 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: GpuPrimitiveTopology, + strip_index_format: Option<GpuIndexFormat>, + front_face: GpuFrontFace, + cull_mode: GpuCullMode, + clamp_depth: bool, +} + +impl From<GpuPrimitiveState> for wgpu_types::PrimitiveState { + fn from(value: GpuPrimitiveState) -> wgpu_types::PrimitiveState { + wgpu_types::PrimitiveState { + topology: value.topology.into(), + strip_index_format: value.strip_index_format.map(Into::into), + front_face: value.front_face.into(), + cull_mode: value.cull_mode.into(), + clamp_depth: value.clamp_depth, + polygon_mode: Default::default(), // native-only + conservative: false, // native-only + } + } +} +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuBlendComponent { + src_factor: GpuBlendFactor, + dst_factor: GpuBlendFactor, + operation: GpuBlendOperation, +} + +impl From<GpuBlendComponent> for wgpu_types::BlendComponent { + fn from(component: GpuBlendComponent) -> Self { + wgpu_types::BlendComponent { + src_factor: component.src_factor.into(), + dst_factor: component.dst_factor.into(), + operation: component.operation.into(), + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuBlendState { + color: GpuBlendComponent, + alpha: GpuBlendComponent, +} + +impl From<GpuBlendState> for wgpu_types::BlendState { + fn from(state: GpuBlendState) -> wgpu_types::BlendState { + wgpu_types::BlendState { + color: state.color.into(), + alpha: state.alpha.into(), + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuColorTargetState { + format: GpuTextureFormat, + blend: Option<GpuBlendState>, + write_mask: u32, +} + +impl TryFrom<GpuColorTargetState> for wgpu_types::ColorTargetState { + type Error = AnyError; + fn try_from( + state: GpuColorTargetState, + ) -> Result<wgpu_types::ColorTargetState, AnyError> { + Ok(wgpu_types::ColorTargetState { + format: state.format.try_into()?, + blend: state.blend.map(Into::into), + write_mask: wgpu_types::ColorWrites::from_bits_truncate(state.write_mask), + }) + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuStencilFaceState { + compare: GpuCompareFunction, + fail_op: GPUStencilOperation, + depth_fail_op: GPUStencilOperation, + pass_op: GPUStencilOperation, +} + +impl From<GpuStencilFaceState> for wgpu_types::StencilFaceState { + fn from(state: GpuStencilFaceState) -> Self { + wgpu_types::StencilFaceState { + compare: state.compare.into(), + fail_op: state.fail_op.into(), + depth_fail_op: state.depth_fail_op.into(), + pass_op: state.pass_op.into(), + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuDepthStencilState { + format: GpuTextureFormat, + depth_write_enabled: bool, + depth_compare: GpuCompareFunction, + stencil_front: GpuStencilFaceState, + stencil_back: GpuStencilFaceState, + stencil_read_mask: u32, + stencil_write_mask: u32, + depth_bias: i32, + depth_bias_slope_scale: f32, + depth_bias_clamp: f32, +} + +impl TryFrom<GpuDepthStencilState> for wgpu_types::DepthStencilState { + type Error = AnyError; + fn try_from( + state: GpuDepthStencilState, + ) -> Result<wgpu_types::DepthStencilState, AnyError> { + Ok(wgpu_types::DepthStencilState { + format: state.format.try_into()?, + depth_write_enabled: state.depth_write_enabled, + depth_compare: state.depth_compare.into(), + stencil: wgpu_types::StencilState { + front: state.stencil_front.into(), + back: state.stencil_back.into(), + read_mask: state.stencil_read_mask, + write_mask: state.stencil_write_mask, + }, + bias: wgpu_types::DepthBiasState { + constant: state.depth_bias, + slope_scale: state.depth_bias_slope_scale, + clamp: state.depth_bias_clamp, + }, + }) + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuVertexAttribute { + format: GpuVertexFormat, + offset: u64, + shader_location: u32, +} + +impl From<GpuVertexAttribute> for wgpu_types::VertexAttribute { + fn from(attribute: GpuVertexAttribute) -> Self { + wgpu_types::VertexAttribute { + format: attribute.format.into(), + offset: attribute.offset, + shader_location: attribute.shader_location, + } + } +} + +#[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")] +enum GpuVertexStepMode { + Vertex, + Instance, +} + +impl From<GpuVertexStepMode> for wgpu_types::VertexStepMode { + fn from(vsm: GpuVertexStepMode) -> wgpu_types::VertexStepMode { + use wgpu_types::VertexStepMode; + match vsm { + GpuVertexStepMode::Vertex => VertexStepMode::Vertex, + GpuVertexStepMode::Instance => VertexStepMode::Instance, + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuVertexBufferLayout { + array_stride: u64, + step_mode: GpuVertexStepMode, + attributes: Vec<GpuVertexAttribute>, +} + +impl<'a> From<GpuVertexBufferLayout> + for wgpu_core::pipeline::VertexBufferLayout<'a> +{ + fn from( + layout: GpuVertexBufferLayout, + ) -> wgpu_core::pipeline::VertexBufferLayout<'a> { + wgpu_core::pipeline::VertexBufferLayout { + array_stride: layout.array_stride, + step_mode: layout.step_mode.into(), + attributes: Cow::Owned( + layout.attributes.into_iter().map(Into::into).collect(), + ), + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuVertexState { + module: ResourceId, + entry_point: String, + buffers: Vec<Option<GpuVertexBufferLayout>>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuMultisampleState { + count: u32, + mask: u64, + alpha_to_coverage_enabled: bool, +} + +impl From<GpuMultisampleState> for wgpu_types::MultisampleState { + fn from(gms: GpuMultisampleState) -> wgpu_types::MultisampleState { + wgpu_types::MultisampleState { + count: gms.count, + mask: gms.mask, + alpha_to_coverage_enabled: gms.alpha_to_coverage_enabled, + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuFragmentState { + targets: Vec<GpuColorTargetState>, + module: u32, + entry_point: String, + // TODO(lucacasonato): constants +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateRenderPipelineArgs { + device_rid: ResourceId, + label: Option<String>, + layout: Option<ResourceId>, + vertex: GpuVertexState, + primitive: GpuPrimitiveState, + depth_stencil: Option<GpuDepthStencilState>, + multisample: 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)?; + let device = device_resource.0; + + let layout = if let Some(rid) = args.layout { + let pipeline_layout_resource = + state.resource_table.get::<WebGpuPipelineLayout>(rid)?; + Some(pipeline_layout_resource.0) + } else { + None + }; + + let vertex_shader_module_resource = + state + .resource_table + .get::<super::shader::WebGpuShaderModule>(args.vertex.module)?; + + let fragment = if let Some(fragment) = args.fragment { + let fragment_shader_module_resource = + state + .resource_table + .get::<super::shader::WebGpuShaderModule>(fragment.module)?; + + let mut targets = Vec::with_capacity(fragment.targets.len()); + + for target in fragment.targets { + targets.push(target.try_into()?); + } + + Some(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(targets), + }) + } else { + None + }; + + let vertex_buffers = args + .vertex + .buffers + .into_iter() + .flatten() + .map(Into::into) + .collect(); + + let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { + label: args.label.map(Cow::Owned), + layout, + vertex: wgpu_core::pipeline::VertexState { + stage: wgpu_core::pipeline::ProgrammableStageDescriptor { + module: vertex_shader_module_resource.0, + entry_point: Cow::Owned(args.vertex.entry_point), + }, + buffers: Cow::Owned(vertex_buffers), + }, + primitive: args.primitive.into(), + depth_stencil: args.depth_stencil.map(TryInto::try_into).transpose()?, + multisample: args.multisample.into(), + fragment, + }; + + let implicit_pipelines = match args.layout { + Some(_) => None, + None => Some(wgpu_core::device::ImplicitPipelineIds { + root_id: std::marker::PhantomData, + group_ids: &[std::marker::PhantomData; 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)?; + 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), + }) +} |