wgpu_core/
pipeline.rs

1use alloc::{
2    borrow::Cow,
3    boxed::Box,
4    string::{String, ToString as _},
5    sync::Arc,
6    vec::Vec,
7};
8use core::{marker::PhantomData, mem::ManuallyDrop, num::NonZeroU32};
9
10use arrayvec::ArrayVec;
11use naga::error::ShaderError;
12use thiserror::Error;
13use wgt::error::{ErrorType, WebGpuError};
14
15pub use crate::pipeline_cache::PipelineCacheValidationError;
16use crate::{
17    binding_model::{CreateBindGroupLayoutError, CreatePipelineLayoutError, PipelineLayout},
18    command::ColorAttachmentError,
19    device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext},
20    id::{PipelineCacheId, PipelineLayoutId, ShaderModuleId},
21    resource::{InvalidResourceError, Labeled, TrackingData},
22    resource_log, validation, Label,
23};
24
25/// Information about buffer bindings, which
26/// is validated against the shader (and pipeline)
27/// at draw time as opposed to initialization time.
28#[derive(Debug)]
29pub(crate) struct LateSizedBufferGroup {
30    // The order has to match `BindGroup::late_buffer_binding_sizes`.
31    pub(crate) shader_sizes: Vec<wgt::BufferAddress>,
32}
33
34#[allow(clippy::large_enum_variant)]
35pub enum ShaderModuleSource<'a> {
36    #[cfg(feature = "wgsl")]
37    Wgsl(Cow<'a, str>),
38    #[cfg(feature = "glsl")]
39    Glsl(Cow<'a, str>, naga::front::glsl::Options),
40    #[cfg(feature = "spirv")]
41    SpirV(Cow<'a, [u32]>, naga::front::spv::Options),
42    Naga(Cow<'static, naga::Module>),
43    /// Dummy variant because `Naga` doesn't have a lifetime and without enough active features it
44    /// could be the last one active.
45    #[doc(hidden)]
46    Dummy(PhantomData<&'a ()>),
47}
48
49#[derive(Clone, Debug)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51pub struct ShaderModuleDescriptor<'a> {
52    pub label: Label<'a>,
53    #[cfg_attr(feature = "serde", serde(default))]
54    pub runtime_checks: wgt::ShaderRuntimeChecks,
55}
56
57pub type ShaderModuleDescriptorPassthrough<'a> =
58    wgt::CreateShaderModuleDescriptorPassthrough<'a, Label<'a>>;
59
60#[derive(Debug)]
61pub struct ShaderModule {
62    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynShaderModule>>,
63    pub(crate) device: Arc<Device>,
64    pub(crate) interface: Option<validation::Interface>,
65    /// The `label` from the descriptor used to create the resource.
66    pub(crate) label: String,
67}
68
69impl Drop for ShaderModule {
70    fn drop(&mut self) {
71        resource_log!("Destroy raw {}", self.error_ident());
72        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
73        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
74        unsafe {
75            self.device.raw().destroy_shader_module(raw);
76        }
77    }
78}
79
80crate::impl_resource_type!(ShaderModule);
81crate::impl_labeled!(ShaderModule);
82crate::impl_parent_device!(ShaderModule);
83crate::impl_storage_item!(ShaderModule);
84
85impl ShaderModule {
86    pub(crate) fn raw(&self) -> &dyn hal::DynShaderModule {
87        self.raw.as_ref()
88    }
89
90    pub(crate) fn finalize_entry_point_name(
91        &self,
92        stage_bit: wgt::ShaderStages,
93        entry_point: Option<&str>,
94    ) -> Result<String, validation::StageError> {
95        match &self.interface {
96            Some(interface) => interface.finalize_entry_point_name(stage_bit, entry_point),
97            None => entry_point
98                .map(|ep| ep.to_string())
99                .ok_or(validation::StageError::NoEntryPointFound),
100        }
101    }
102}
103
104//Note: `Clone` would require `WithSpan: Clone`.
105#[derive(Clone, Debug, Error)]
106#[non_exhaustive]
107pub enum CreateShaderModuleError {
108    #[cfg(feature = "wgsl")]
109    #[error(transparent)]
110    Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),
111    #[cfg(feature = "glsl")]
112    #[error(transparent)]
113    ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseErrors>),
114    #[cfg(feature = "spirv")]
115    #[error(transparent)]
116    ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>),
117    #[error("Failed to generate the backend-specific code")]
118    Generation,
119    #[error(transparent)]
120    Device(#[from] DeviceError),
121    #[error(transparent)]
122    Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
123    #[error(transparent)]
124    MissingFeatures(#[from] MissingFeatures),
125    #[error(
126        "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
127    )]
128    InvalidGroupIndex {
129        bind: naga::ResourceBinding,
130        group: u32,
131        limit: u32,
132    },
133}
134
135impl WebGpuError for CreateShaderModuleError {
136    fn webgpu_error_type(&self) -> ErrorType {
137        let e: &dyn WebGpuError = match self {
138            Self::Device(e) => e,
139            Self::MissingFeatures(e) => e,
140
141            Self::Generation => return ErrorType::Internal,
142
143            Self::Validation(..) | Self::InvalidGroupIndex { .. } => return ErrorType::Validation,
144            #[cfg(feature = "wgsl")]
145            Self::Parsing(..) => return ErrorType::Validation,
146            #[cfg(feature = "glsl")]
147            Self::ParsingGlsl(..) => return ErrorType::Validation,
148            #[cfg(feature = "spirv")]
149            Self::ParsingSpirV(..) => return ErrorType::Validation,
150        };
151        e.webgpu_error_type()
152    }
153}
154
155/// Describes a programmable pipeline stage.
156#[derive(Clone, Debug)]
157#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
158pub struct ProgrammableStageDescriptor<'a, SM = ShaderModuleId> {
159    /// The compiled shader module for this stage.
160    pub module: SM,
161    /// The name of the entry point in the compiled shader. The name is selected using the
162    /// following logic:
163    ///
164    /// * If `Some(name)` is specified, there must be a function with this name in the shader.
165    /// * If a single entry point associated with this stage must be in the shader, then proceed as
166    ///   if `Some(…)` was specified with that entry point's name.
167    pub entry_point: Option<Cow<'a, str>>,
168    /// Specifies the values of pipeline-overridable constants in the shader module.
169    ///
170    /// If an `@id` attribute was specified on the declaration,
171    /// the key must be the pipeline constant ID as a decimal ASCII number; if not,
172    /// the key must be the constant's identifier name.
173    ///
174    /// The value may represent any of WGSL's concrete scalar types.
175    pub constants: naga::back::PipelineConstants,
176    /// Whether workgroup scoped memory will be initialized with zero values for this stage.
177    ///
178    /// This is required by the WebGPU spec, but may have overhead which can be avoided
179    /// for cross-platform applications
180    pub zero_initialize_workgroup_memory: bool,
181}
182
183/// cbindgen:ignore
184pub type ResolvedProgrammableStageDescriptor<'a> =
185    ProgrammableStageDescriptor<'a, Arc<ShaderModule>>;
186
187/// Number of implicit bind groups derived at pipeline creation.
188pub type ImplicitBindGroupCount = u8;
189
190#[derive(Clone, Debug, Error)]
191#[non_exhaustive]
192pub enum ImplicitLayoutError {
193    #[error("The implicit_pipeline_ids arg is required")]
194    MissingImplicitPipelineIds,
195    #[error("Missing IDs for deriving {0} bind groups")]
196    MissingIds(ImplicitBindGroupCount),
197    #[error("Unable to reflect the shader {0:?} interface")]
198    ReflectionError(wgt::ShaderStages),
199    #[error(transparent)]
200    BindGroup(#[from] CreateBindGroupLayoutError),
201    #[error(transparent)]
202    Pipeline(#[from] CreatePipelineLayoutError),
203}
204
205impl WebGpuError for ImplicitLayoutError {
206    fn webgpu_error_type(&self) -> ErrorType {
207        let e: &dyn WebGpuError = match self {
208            Self::MissingImplicitPipelineIds | Self::MissingIds(_) | Self::ReflectionError(_) => {
209                return ErrorType::Validation
210            }
211            Self::BindGroup(e) => e,
212            Self::Pipeline(e) => e,
213        };
214        e.webgpu_error_type()
215    }
216}
217
218/// Describes a compute pipeline.
219#[derive(Clone, Debug)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221pub struct ComputePipelineDescriptor<
222    'a,
223    PLL = PipelineLayoutId,
224    SM = ShaderModuleId,
225    PLC = PipelineCacheId,
226> {
227    pub label: Label<'a>,
228    /// The layout of bind groups for this pipeline.
229    pub layout: Option<PLL>,
230    /// The compiled compute stage and its entry point.
231    pub stage: ProgrammableStageDescriptor<'a, SM>,
232    /// The pipeline cache to use when creating this pipeline.
233    pub cache: Option<PLC>,
234}
235
236/// cbindgen:ignore
237pub type ResolvedComputePipelineDescriptor<'a> =
238    ComputePipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
239
240#[derive(Clone, Debug, Error)]
241#[non_exhaustive]
242pub enum CreateComputePipelineError {
243    #[error(transparent)]
244    Device(#[from] DeviceError),
245    #[error("Unable to derive an implicit layout")]
246    Implicit(#[from] ImplicitLayoutError),
247    #[error("Error matching shader requirements against the pipeline")]
248    Stage(#[from] validation::StageError),
249    #[error("Internal error: {0}")]
250    Internal(String),
251    #[error("Pipeline constant error: {0}")]
252    PipelineConstants(String),
253    #[error(transparent)]
254    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
255    #[error(transparent)]
256    InvalidResource(#[from] InvalidResourceError),
257}
258
259impl WebGpuError for CreateComputePipelineError {
260    fn webgpu_error_type(&self) -> ErrorType {
261        let e: &dyn WebGpuError = match self {
262            Self::Device(e) => e,
263            Self::InvalidResource(e) => e,
264            Self::MissingDownlevelFlags(e) => e,
265            Self::Implicit(e) => e,
266            Self::Stage(e) => e,
267            Self::Internal(_) => return ErrorType::Internal,
268            Self::PipelineConstants(_) => return ErrorType::Validation,
269        };
270        e.webgpu_error_type()
271    }
272}
273
274#[derive(Debug)]
275pub struct ComputePipeline {
276    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,
277    pub(crate) layout: Arc<PipelineLayout>,
278    pub(crate) device: Arc<Device>,
279    pub(crate) _shader_module: Arc<ShaderModule>,
280    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
281    /// The `label` from the descriptor used to create the resource.
282    pub(crate) label: String,
283    pub(crate) tracking_data: TrackingData,
284}
285
286impl Drop for ComputePipeline {
287    fn drop(&mut self) {
288        resource_log!("Destroy raw {}", self.error_ident());
289        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
290        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
291        unsafe {
292            self.device.raw().destroy_compute_pipeline(raw);
293        }
294    }
295}
296
297crate::impl_resource_type!(ComputePipeline);
298crate::impl_labeled!(ComputePipeline);
299crate::impl_parent_device!(ComputePipeline);
300crate::impl_storage_item!(ComputePipeline);
301crate::impl_trackable!(ComputePipeline);
302
303impl ComputePipeline {
304    pub(crate) fn raw(&self) -> &dyn hal::DynComputePipeline {
305        self.raw.as_ref()
306    }
307}
308
309#[derive(Clone, Debug, Error)]
310#[non_exhaustive]
311pub enum CreatePipelineCacheError {
312    #[error(transparent)]
313    Device(#[from] DeviceError),
314    #[error("Pipeline cache validation failed")]
315    Validation(#[from] PipelineCacheValidationError),
316    #[error(transparent)]
317    MissingFeatures(#[from] MissingFeatures),
318}
319
320impl WebGpuError for CreatePipelineCacheError {
321    fn webgpu_error_type(&self) -> ErrorType {
322        let e: &dyn WebGpuError = match self {
323            Self::Device(e) => e,
324            Self::Validation(e) => e,
325            Self::MissingFeatures(e) => e,
326        };
327        e.webgpu_error_type()
328    }
329}
330
331#[derive(Debug)]
332pub struct PipelineCache {
333    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,
334    pub(crate) device: Arc<Device>,
335    /// The `label` from the descriptor used to create the resource.
336    pub(crate) label: String,
337}
338
339impl Drop for PipelineCache {
340    fn drop(&mut self) {
341        resource_log!("Destroy raw {}", self.error_ident());
342        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
343        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
344        unsafe {
345            self.device.raw().destroy_pipeline_cache(raw);
346        }
347    }
348}
349
350crate::impl_resource_type!(PipelineCache);
351crate::impl_labeled!(PipelineCache);
352crate::impl_parent_device!(PipelineCache);
353crate::impl_storage_item!(PipelineCache);
354
355impl PipelineCache {
356    pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {
357        self.raw.as_ref()
358    }
359}
360
361/// Describes how the vertex buffer is interpreted.
362#[derive(Clone, Debug)]
363#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
364#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
365pub struct VertexBufferLayout<'a> {
366    /// The stride, in bytes, between elements of this buffer.
367    pub array_stride: wgt::BufferAddress,
368    /// How often this vertex buffer is "stepped" forward.
369    pub step_mode: wgt::VertexStepMode,
370    /// The list of attributes which comprise a single vertex.
371    pub attributes: Cow<'a, [wgt::VertexAttribute]>,
372}
373
374/// Describes the vertex process in a render pipeline.
375#[derive(Clone, Debug)]
376#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
377pub struct VertexState<'a, SM = ShaderModuleId> {
378    /// The compiled vertex stage and its entry point.
379    pub stage: ProgrammableStageDescriptor<'a, SM>,
380    /// The format of any vertex buffers used with this pipeline.
381    pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
382}
383
384/// cbindgen:ignore
385pub type ResolvedVertexState<'a> = VertexState<'a, Arc<ShaderModule>>;
386
387/// Describes fragment processing in a render pipeline.
388#[derive(Clone, Debug)]
389#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
390pub struct FragmentState<'a, SM = ShaderModuleId> {
391    /// The compiled fragment stage and its entry point.
392    pub stage: ProgrammableStageDescriptor<'a, SM>,
393    /// The effect of draw calls on the color aspect of the output target.
394    pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
395}
396
397/// cbindgen:ignore
398pub type ResolvedFragmentState<'a> = FragmentState<'a, Arc<ShaderModule>>;
399
400/// Describes a render (graphics) pipeline.
401#[derive(Clone, Debug)]
402#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
403pub struct RenderPipelineDescriptor<
404    'a,
405    PLL = PipelineLayoutId,
406    SM = ShaderModuleId,
407    PLC = PipelineCacheId,
408> {
409    pub label: Label<'a>,
410    /// The layout of bind groups for this pipeline.
411    pub layout: Option<PLL>,
412    /// The vertex processing state for this pipeline.
413    pub vertex: VertexState<'a, SM>,
414    /// The properties of the pipeline at the primitive assembly and rasterization level.
415    #[cfg_attr(feature = "serde", serde(default))]
416    pub primitive: wgt::PrimitiveState,
417    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
418    #[cfg_attr(feature = "serde", serde(default))]
419    pub depth_stencil: Option<wgt::DepthStencilState>,
420    /// The multi-sampling properties of the pipeline.
421    #[cfg_attr(feature = "serde", serde(default))]
422    pub multisample: wgt::MultisampleState,
423    /// The fragment processing state for this pipeline.
424    pub fragment: Option<FragmentState<'a, SM>>,
425    /// If the pipeline will be used with a multiview render pass, this indicates how many array
426    /// layers the attachments will have.
427    pub multiview: Option<NonZeroU32>,
428    /// The pipeline cache to use when creating this pipeline.
429    pub cache: Option<PLC>,
430}
431
432/// cbindgen:ignore
433pub type ResolvedRenderPipelineDescriptor<'a> =
434    RenderPipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
435
436#[derive(Clone, Debug)]
437#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
438pub struct PipelineCacheDescriptor<'a> {
439    pub label: Label<'a>,
440    pub data: Option<Cow<'a, [u8]>>,
441    pub fallback: bool,
442}
443
444#[derive(Clone, Debug, Error)]
445#[non_exhaustive]
446pub enum ColorStateError {
447    #[error("Format {0:?} is not renderable")]
448    FormatNotRenderable(wgt::TextureFormat),
449    #[error("Format {0:?} is not blendable")]
450    FormatNotBlendable(wgt::TextureFormat),
451    #[error("Format {0:?} does not have a color aspect")]
452    FormatNotColor(wgt::TextureFormat),
453    #[error("Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.")]
454    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
455    #[error("Output format {pipeline} is incompatible with the shader {shader}")]
456    IncompatibleFormat {
457        pipeline: validation::NumericType,
458        shader: validation::NumericType,
459    },
460    #[error("Invalid write mask {0:?}")]
461    InvalidWriteMask(wgt::ColorWrites),
462}
463
464#[derive(Clone, Debug, Error)]
465#[non_exhaustive]
466pub enum DepthStencilStateError {
467    #[error("Format {0:?} is not renderable")]
468    FormatNotRenderable(wgt::TextureFormat),
469    #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
470    FormatNotDepth(wgt::TextureFormat),
471    #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
472    FormatNotStencil(wgt::TextureFormat),
473    #[error("Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.")]
474    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
475}
476
477#[derive(Clone, Debug, Error)]
478#[non_exhaustive]
479pub enum CreateRenderPipelineError {
480    #[error(transparent)]
481    ColorAttachment(#[from] ColorAttachmentError),
482    #[error(transparent)]
483    Device(#[from] DeviceError),
484    #[error("Unable to derive an implicit layout")]
485    Implicit(#[from] ImplicitLayoutError),
486    #[error("Color state [{0}] is invalid")]
487    ColorState(u8, #[source] ColorStateError),
488    #[error("Depth/stencil state is invalid")]
489    DepthStencilState(#[from] DepthStencilStateError),
490    #[error("Invalid sample count {0}")]
491    InvalidSampleCount(u32),
492    #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
493    TooManyVertexBuffers { given: u32, limit: u32 },
494    #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
495    TooManyVertexAttributes { given: u32, limit: u32 },
496    #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
497    VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
498    #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")]
499    VertexAttributeStrideTooLarge {
500        location: wgt::ShaderLocation,
501        given: u32,
502        limit: u32,
503    },
504    #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
505    UnalignedVertexStride {
506        index: u32,
507        stride: wgt::BufferAddress,
508    },
509    #[error("Vertex attribute at location {location} has invalid offset {offset}")]
510    InvalidVertexAttributeOffset {
511        location: wgt::ShaderLocation,
512        offset: wgt::BufferAddress,
513    },
514    #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
515    ShaderLocationClash(u32),
516    #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
517    StripIndexFormatForNonStripTopology {
518        strip_index_format: Option<wgt::IndexFormat>,
519        topology: wgt::PrimitiveTopology,
520    },
521    #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
522    ConservativeRasterizationNonFillPolygonMode,
523    #[error(transparent)]
524    MissingFeatures(#[from] MissingFeatures),
525    #[error(transparent)]
526    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
527    #[error("Error matching {stage:?} shader requirements against the pipeline")]
528    Stage {
529        stage: wgt::ShaderStages,
530        #[source]
531        error: validation::StageError,
532    },
533    #[error("Internal error in {stage:?} shader: {error}")]
534    Internal {
535        stage: wgt::ShaderStages,
536        error: String,
537    },
538    #[error("Pipeline constant error in {stage:?} shader: {error}")]
539    PipelineConstants {
540        stage: wgt::ShaderStages,
541        error: String,
542    },
543    #[error("In the provided shader, the type given for group {group} binding {binding} has a size of {size}. As the device does not support `DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED`, the type must have a size that is a multiple of 16 bytes.")]
544    UnalignedShader { group: u32, binding: u32, size: u64 },
545    #[error("Using the blend factor {factor:?} for render target {target} is not possible. Only the first render target may be used when dual-source blending.")]
546    BlendFactorOnUnsupportedTarget {
547        factor: wgt::BlendFactor,
548        target: u32,
549    },
550    #[error("Pipeline expects the shader entry point to make use of dual-source blending.")]
551    PipelineExpectsShaderToUseDualSourceBlending,
552    #[error("Shader entry point expects the pipeline to make use of dual-source blending.")]
553    ShaderExpectsPipelineToUseDualSourceBlending,
554    #[error("{}", concat!(
555        "At least one color attachment or depth-stencil attachment was expected, ",
556        "but no render target for the pipeline was specified."
557    ))]
558    NoTargetSpecified,
559    #[error(transparent)]
560    InvalidResource(#[from] InvalidResourceError),
561}
562
563impl WebGpuError for CreateRenderPipelineError {
564    fn webgpu_error_type(&self) -> ErrorType {
565        let e: &dyn WebGpuError = match self {
566            Self::Device(e) => e,
567            Self::InvalidResource(e) => e,
568            Self::MissingFeatures(e) => e,
569            Self::MissingDownlevelFlags(e) => e,
570
571            Self::Internal { .. } => return ErrorType::Internal,
572
573            Self::ColorAttachment(_)
574            | Self::Implicit(_)
575            | Self::ColorState(_, _)
576            | Self::DepthStencilState(_)
577            | Self::InvalidSampleCount(_)
578            | Self::TooManyVertexBuffers { .. }
579            | Self::TooManyVertexAttributes { .. }
580            | Self::VertexStrideTooLarge { .. }
581            | Self::UnalignedVertexStride { .. }
582            | Self::InvalidVertexAttributeOffset { .. }
583            | Self::ShaderLocationClash(_)
584            | Self::StripIndexFormatForNonStripTopology { .. }
585            | Self::ConservativeRasterizationNonFillPolygonMode
586            | Self::Stage { .. }
587            | Self::UnalignedShader { .. }
588            | Self::BlendFactorOnUnsupportedTarget { .. }
589            | Self::PipelineExpectsShaderToUseDualSourceBlending
590            | Self::ShaderExpectsPipelineToUseDualSourceBlending
591            | Self::NoTargetSpecified
592            | Self::PipelineConstants { .. }
593            | Self::VertexAttributeStrideTooLarge { .. } => return ErrorType::Validation,
594        };
595        e.webgpu_error_type()
596    }
597}
598
599bitflags::bitflags! {
600    #[repr(transparent)]
601    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
602    pub struct PipelineFlags: u32 {
603        const BLEND_CONSTANT = 1 << 0;
604        const STENCIL_REFERENCE = 1 << 1;
605        const WRITES_DEPTH = 1 << 2;
606        const WRITES_STENCIL = 1 << 3;
607    }
608}
609
610/// How a render pipeline will retrieve attributes from a particular vertex buffer.
611#[derive(Clone, Copy, Debug)]
612pub struct VertexStep {
613    /// The byte stride in the buffer between one attribute value and the next.
614    pub stride: wgt::BufferAddress,
615
616    /// The byte size required to fit the last vertex in the stream.
617    pub last_stride: wgt::BufferAddress,
618
619    /// Whether the buffer is indexed by vertex number or instance number.
620    pub mode: wgt::VertexStepMode,
621}
622
623impl Default for VertexStep {
624    fn default() -> Self {
625        Self {
626            stride: 0,
627            last_stride: 0,
628            mode: wgt::VertexStepMode::Vertex,
629        }
630    }
631}
632
633#[derive(Debug)]
634pub struct RenderPipeline {
635    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
636    pub(crate) device: Arc<Device>,
637    pub(crate) layout: Arc<PipelineLayout>,
638    pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
639    pub(crate) pass_context: RenderPassContext,
640    pub(crate) flags: PipelineFlags,
641    pub(crate) strip_index_format: Option<wgt::IndexFormat>,
642    pub(crate) vertex_steps: Vec<VertexStep>,
643    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
644    /// The `label` from the descriptor used to create the resource.
645    pub(crate) label: String,
646    pub(crate) tracking_data: TrackingData,
647}
648
649impl Drop for RenderPipeline {
650    fn drop(&mut self) {
651        resource_log!("Destroy raw {}", self.error_ident());
652        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
653        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
654        unsafe {
655            self.device.raw().destroy_render_pipeline(raw);
656        }
657    }
658}
659
660crate::impl_resource_type!(RenderPipeline);
661crate::impl_labeled!(RenderPipeline);
662crate::impl_parent_device!(RenderPipeline);
663crate::impl_storage_item!(RenderPipeline);
664crate::impl_trackable!(RenderPipeline);
665
666impl RenderPipeline {
667    pub(crate) fn raw(&self) -> &dyn hal::DynRenderPipeline {
668        self.raw.as_ref()
669    }
670}