summaryrefslogtreecommitdiff
path: root/ext/webgpu/pipeline.rs
diff options
context:
space:
mode:
authorAaron O'Mullan <aaron.omullan@gmail.com>2022-01-20 15:23:53 +0100
committerGitHub <noreply@github.com>2022-01-20 15:23:53 +0100
commit3ab68bd0a2aff6df12388f2c3b5ed7ae3333a6ca (patch)
tree9a81824deb4e5a2b29c3eeb5a2adaa3e00720c45 /ext/webgpu/pipeline.rs
parent1cc38f5155bdc5605d74cd959660fa04f782ac63 (diff)
revert(#13402): experiment: wgpu sync (#13439)
Diffstat (limited to 'ext/webgpu/pipeline.rs')
-rw-r--r--ext/webgpu/pipeline.rs791
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),
+ })
+}