1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-28 01:59:06 -05:00
denoland-deno/ext/webgpu/pipeline.rs
2021-11-02 10:03:37 -04:00

791 lines
22 KiB
Rust

// Copyright 2018-2021 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),
})
}