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("Unable to reflect the shader {0:?} interface")]
194    ReflectionError(wgt::ShaderStages),
195    #[error(transparent)]
196    BindGroup(#[from] CreateBindGroupLayoutError),
197    #[error(transparent)]
198    Pipeline(#[from] CreatePipelineLayoutError),
199}
200
201impl WebGpuError for ImplicitLayoutError {
202    fn webgpu_error_type(&self) -> ErrorType {
203        let e: &dyn WebGpuError = match self {
204            Self::ReflectionError(_) => return ErrorType::Validation,
205            Self::BindGroup(e) => e,
206            Self::Pipeline(e) => e,
207        };
208        e.webgpu_error_type()
209    }
210}
211
212/// Describes a compute pipeline.
213#[derive(Clone, Debug)]
214#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
215pub struct ComputePipelineDescriptor<
216    'a,
217    PLL = PipelineLayoutId,
218    SM = ShaderModuleId,
219    PLC = PipelineCacheId,
220> {
221    pub label: Label<'a>,
222    /// The layout of bind groups for this pipeline.
223    pub layout: Option<PLL>,
224    /// The compiled compute stage and its entry point.
225    pub stage: ProgrammableStageDescriptor<'a, SM>,
226    /// The pipeline cache to use when creating this pipeline.
227    pub cache: Option<PLC>,
228}
229
230/// cbindgen:ignore
231pub type ResolvedComputePipelineDescriptor<'a> =
232    ComputePipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
233
234#[derive(Clone, Debug, Error)]
235#[non_exhaustive]
236pub enum CreateComputePipelineError {
237    #[error(transparent)]
238    Device(#[from] DeviceError),
239    #[error("Unable to derive an implicit layout")]
240    Implicit(#[from] ImplicitLayoutError),
241    #[error("Error matching shader requirements against the pipeline")]
242    Stage(#[from] validation::StageError),
243    #[error("Internal error: {0}")]
244    Internal(String),
245    #[error("Pipeline constant error: {0}")]
246    PipelineConstants(String),
247    #[error(transparent)]
248    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
249    #[error(transparent)]
250    InvalidResource(#[from] InvalidResourceError),
251}
252
253impl WebGpuError for CreateComputePipelineError {
254    fn webgpu_error_type(&self) -> ErrorType {
255        let e: &dyn WebGpuError = match self {
256            Self::Device(e) => e,
257            Self::InvalidResource(e) => e,
258            Self::MissingDownlevelFlags(e) => e,
259            Self::Implicit(e) => e,
260            Self::Stage(e) => e,
261            Self::Internal(_) => return ErrorType::Internal,
262            Self::PipelineConstants(_) => return ErrorType::Validation,
263        };
264        e.webgpu_error_type()
265    }
266}
267
268#[derive(Debug)]
269pub struct ComputePipeline {
270    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,
271    pub(crate) layout: Arc<PipelineLayout>,
272    pub(crate) device: Arc<Device>,
273    pub(crate) _shader_module: Arc<ShaderModule>,
274    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
275    /// The `label` from the descriptor used to create the resource.
276    pub(crate) label: String,
277    pub(crate) tracking_data: TrackingData,
278}
279
280impl Drop for ComputePipeline {
281    fn drop(&mut self) {
282        resource_log!("Destroy raw {}", self.error_ident());
283        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
284        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
285        unsafe {
286            self.device.raw().destroy_compute_pipeline(raw);
287        }
288    }
289}
290
291crate::impl_resource_type!(ComputePipeline);
292crate::impl_labeled!(ComputePipeline);
293crate::impl_parent_device!(ComputePipeline);
294crate::impl_storage_item!(ComputePipeline);
295crate::impl_trackable!(ComputePipeline);
296
297impl ComputePipeline {
298    pub(crate) fn raw(&self) -> &dyn hal::DynComputePipeline {
299        self.raw.as_ref()
300    }
301}
302
303#[derive(Clone, Debug, Error)]
304#[non_exhaustive]
305pub enum CreatePipelineCacheError {
306    #[error(transparent)]
307    Device(#[from] DeviceError),
308    #[error("Pipeline cache validation failed")]
309    Validation(#[from] PipelineCacheValidationError),
310    #[error(transparent)]
311    MissingFeatures(#[from] MissingFeatures),
312}
313
314impl WebGpuError for CreatePipelineCacheError {
315    fn webgpu_error_type(&self) -> ErrorType {
316        let e: &dyn WebGpuError = match self {
317            Self::Device(e) => e,
318            Self::Validation(e) => e,
319            Self::MissingFeatures(e) => e,
320        };
321        e.webgpu_error_type()
322    }
323}
324
325#[derive(Debug)]
326pub struct PipelineCache {
327    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,
328    pub(crate) device: Arc<Device>,
329    /// The `label` from the descriptor used to create the resource.
330    pub(crate) label: String,
331}
332
333impl Drop for PipelineCache {
334    fn drop(&mut self) {
335        resource_log!("Destroy raw {}", self.error_ident());
336        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
337        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
338        unsafe {
339            self.device.raw().destroy_pipeline_cache(raw);
340        }
341    }
342}
343
344crate::impl_resource_type!(PipelineCache);
345crate::impl_labeled!(PipelineCache);
346crate::impl_parent_device!(PipelineCache);
347crate::impl_storage_item!(PipelineCache);
348
349impl PipelineCache {
350    pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {
351        self.raw.as_ref()
352    }
353}
354
355/// Describes how the vertex buffer is interpreted.
356#[derive(Clone, Debug)]
357#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
358#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
359pub struct VertexBufferLayout<'a> {
360    /// The stride, in bytes, between elements of this buffer.
361    pub array_stride: wgt::BufferAddress,
362    /// How often this vertex buffer is "stepped" forward.
363    pub step_mode: wgt::VertexStepMode,
364    /// The list of attributes which comprise a single vertex.
365    pub attributes: Cow<'a, [wgt::VertexAttribute]>,
366}
367
368/// A null vertex buffer layout that may be placed in unused slots.
369impl Default for VertexBufferLayout<'_> {
370    fn default() -> Self {
371        Self {
372            array_stride: Default::default(),
373            step_mode: Default::default(),
374            attributes: Cow::Borrowed(&[]),
375        }
376    }
377}
378
379/// Describes the vertex process in a render pipeline.
380#[derive(Clone, Debug)]
381#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
382pub struct VertexState<'a, SM = ShaderModuleId> {
383    /// The compiled vertex stage and its entry point.
384    pub stage: ProgrammableStageDescriptor<'a, SM>,
385    /// The format of any vertex buffers used with this pipeline.
386    pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
387}
388
389/// cbindgen:ignore
390pub type ResolvedVertexState<'a> = VertexState<'a, Arc<ShaderModule>>;
391
392/// Describes fragment processing in a render pipeline.
393#[derive(Clone, Debug)]
394#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
395pub struct FragmentState<'a, SM = ShaderModuleId> {
396    /// The compiled fragment stage and its entry point.
397    pub stage: ProgrammableStageDescriptor<'a, SM>,
398    /// The effect of draw calls on the color aspect of the output target.
399    pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
400}
401
402/// cbindgen:ignore
403pub type ResolvedFragmentState<'a> = FragmentState<'a, Arc<ShaderModule>>;
404
405/// Describes the task shader in a mesh shader pipeline.
406#[derive(Clone, Debug)]
407#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
408pub struct TaskState<'a, SM = ShaderModuleId> {
409    /// The compiled task stage and its entry point.
410    pub stage: ProgrammableStageDescriptor<'a, SM>,
411}
412
413pub type ResolvedTaskState<'a> = TaskState<'a, Arc<ShaderModule>>;
414
415/// Describes the mesh shader in a mesh shader pipeline.
416#[derive(Clone, Debug)]
417#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
418pub struct MeshState<'a, SM = ShaderModuleId> {
419    /// The compiled mesh stage and its entry point.
420    pub stage: ProgrammableStageDescriptor<'a, SM>,
421}
422
423pub type ResolvedMeshState<'a> = MeshState<'a, Arc<ShaderModule>>;
424
425#[derive(Clone, Debug)]
426#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
427pub(crate) enum RenderPipelineVertexProcessor<'a, SM = ShaderModuleId> {
428    Vertex(VertexState<'a, SM>),
429    Mesh(Option<TaskState<'a, SM>>, MeshState<'a, SM>),
430}
431
432/// Describes a render (graphics) pipeline.
433#[derive(Clone, Debug)]
434#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
435pub struct RenderPipelineDescriptor<
436    'a,
437    PLL = PipelineLayoutId,
438    SM = ShaderModuleId,
439    PLC = PipelineCacheId,
440> {
441    pub label: Label<'a>,
442    /// The layout of bind groups for this pipeline.
443    pub layout: Option<PLL>,
444    /// The vertex processing state for this pipeline.
445    pub vertex: VertexState<'a, SM>,
446    /// The properties of the pipeline at the primitive assembly and rasterization level.
447    #[cfg_attr(feature = "serde", serde(default))]
448    pub primitive: wgt::PrimitiveState,
449    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
450    #[cfg_attr(feature = "serde", serde(default))]
451    pub depth_stencil: Option<wgt::DepthStencilState>,
452    /// The multi-sampling properties of the pipeline.
453    #[cfg_attr(feature = "serde", serde(default))]
454    pub multisample: wgt::MultisampleState,
455    /// The fragment processing state for this pipeline.
456    pub fragment: Option<FragmentState<'a, SM>>,
457    /// If the pipeline will be used with a multiview render pass, this indicates how many array
458    /// layers the attachments will have.
459    pub multiview: Option<NonZeroU32>,
460    /// The pipeline cache to use when creating this pipeline.
461    pub cache: Option<PLC>,
462}
463/// Describes a mesh shader pipeline.
464#[derive(Clone, Debug)]
465#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
466pub struct MeshPipelineDescriptor<
467    'a,
468    PLL = PipelineLayoutId,
469    SM = ShaderModuleId,
470    PLC = PipelineCacheId,
471> {
472    pub label: Label<'a>,
473    /// The layout of bind groups for this pipeline.
474    pub layout: Option<PLL>,
475    /// The task processing state for this pipeline.
476    pub task: Option<TaskState<'a, SM>>,
477    /// The mesh processing state for this pipeline
478    pub mesh: MeshState<'a, SM>,
479    /// The properties of the pipeline at the primitive assembly and rasterization level.
480    #[cfg_attr(feature = "serde", serde(default))]
481    pub primitive: wgt::PrimitiveState,
482    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
483    #[cfg_attr(feature = "serde", serde(default))]
484    pub depth_stencil: Option<wgt::DepthStencilState>,
485    /// The multi-sampling properties of the pipeline.
486    #[cfg_attr(feature = "serde", serde(default))]
487    pub multisample: wgt::MultisampleState,
488    /// The fragment processing state for this pipeline.
489    pub fragment: Option<FragmentState<'a, SM>>,
490    /// If the pipeline will be used with a multiview render pass, this indicates how many array
491    /// layers the attachments will have.
492    pub multiview: Option<NonZeroU32>,
493    /// The pipeline cache to use when creating this pipeline.
494    pub cache: Option<PLC>,
495}
496
497/// Describes a render (graphics) pipeline.
498#[derive(Clone, Debug)]
499#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
500pub(crate) struct GeneralRenderPipelineDescriptor<
501    'a,
502    PLL = PipelineLayoutId,
503    SM = ShaderModuleId,
504    PLC = PipelineCacheId,
505> {
506    pub label: Label<'a>,
507    /// The layout of bind groups for this pipeline.
508    pub layout: Option<PLL>,
509    /// The vertex processing state for this pipeline.
510    pub vertex: RenderPipelineVertexProcessor<'a, SM>,
511    /// The properties of the pipeline at the primitive assembly and rasterization level.
512    #[cfg_attr(feature = "serde", serde(default))]
513    pub primitive: wgt::PrimitiveState,
514    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
515    #[cfg_attr(feature = "serde", serde(default))]
516    pub depth_stencil: Option<wgt::DepthStencilState>,
517    /// The multi-sampling properties of the pipeline.
518    #[cfg_attr(feature = "serde", serde(default))]
519    pub multisample: wgt::MultisampleState,
520    /// The fragment processing state for this pipeline.
521    pub fragment: Option<FragmentState<'a, SM>>,
522    /// If the pipeline will be used with a multiview render pass, this indicates how many array
523    /// layers the attachments will have.
524    pub multiview: Option<NonZeroU32>,
525    /// The pipeline cache to use when creating this pipeline.
526    pub cache: Option<PLC>,
527}
528impl<'a, PLL, SM, PLC> From<RenderPipelineDescriptor<'a, PLL, SM, PLC>>
529    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
530{
531    fn from(value: RenderPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
532        Self {
533            label: value.label,
534            layout: value.layout,
535            vertex: RenderPipelineVertexProcessor::Vertex(value.vertex),
536            primitive: value.primitive,
537            depth_stencil: value.depth_stencil,
538            multisample: value.multisample,
539            fragment: value.fragment,
540            multiview: value.multiview,
541            cache: value.cache,
542        }
543    }
544}
545impl<'a, PLL, SM, PLC> From<MeshPipelineDescriptor<'a, PLL, SM, PLC>>
546    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
547{
548    fn from(value: MeshPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
549        Self {
550            label: value.label,
551            layout: value.layout,
552            vertex: RenderPipelineVertexProcessor::Mesh(value.task, value.mesh),
553            primitive: value.primitive,
554            depth_stencil: value.depth_stencil,
555            multisample: value.multisample,
556            fragment: value.fragment,
557            multiview: value.multiview,
558            cache: value.cache,
559        }
560    }
561}
562
563/// cbindgen:ignore
564pub(crate) type ResolvedGeneralRenderPipelineDescriptor<'a> =
565    GeneralRenderPipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
566
567#[derive(Clone, Debug)]
568#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
569pub struct PipelineCacheDescriptor<'a> {
570    pub label: Label<'a>,
571    pub data: Option<Cow<'a, [u8]>>,
572    pub fallback: bool,
573}
574
575#[derive(Clone, Debug, Error)]
576#[non_exhaustive]
577pub enum ColorStateError {
578    #[error("Format {0:?} is not renderable")]
579    FormatNotRenderable(wgt::TextureFormat),
580    #[error("Format {0:?} is not blendable")]
581    FormatNotBlendable(wgt::TextureFormat),
582    #[error("Format {0:?} does not have a color aspect")]
583    FormatNotColor(wgt::TextureFormat),
584    #[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:?}.")]
585    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
586    #[error("Output format {pipeline} is incompatible with the shader {shader}")]
587    IncompatibleFormat {
588        pipeline: validation::NumericType,
589        shader: validation::NumericType,
590    },
591    #[error("Invalid write mask {0:?}")]
592    InvalidWriteMask(wgt::ColorWrites),
593}
594
595#[derive(Clone, Debug, Error)]
596#[non_exhaustive]
597pub enum DepthStencilStateError {
598    #[error("Format {0:?} is not renderable")]
599    FormatNotRenderable(wgt::TextureFormat),
600    #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
601    FormatNotDepth(wgt::TextureFormat),
602    #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
603    FormatNotStencil(wgt::TextureFormat),
604    #[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:?}.")]
605    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
606}
607
608#[derive(Clone, Debug, Error)]
609#[non_exhaustive]
610pub enum CreateRenderPipelineError {
611    #[error(transparent)]
612    ColorAttachment(#[from] ColorAttachmentError),
613    #[error(transparent)]
614    Device(#[from] DeviceError),
615    #[error("Unable to derive an implicit layout")]
616    Implicit(#[from] ImplicitLayoutError),
617    #[error("Color state [{0}] is invalid")]
618    ColorState(u8, #[source] ColorStateError),
619    #[error("Depth/stencil state is invalid")]
620    DepthStencilState(#[from] DepthStencilStateError),
621    #[error("Invalid sample count {0}")]
622    InvalidSampleCount(u32),
623    #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
624    TooManyVertexBuffers { given: u32, limit: u32 },
625    #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
626    TooManyVertexAttributes { given: u32, limit: u32 },
627    #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
628    VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
629    #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")]
630    VertexAttributeStrideTooLarge {
631        location: wgt::ShaderLocation,
632        given: u32,
633        limit: u32,
634    },
635    #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_ALIGNMENT`")]
636    UnalignedVertexStride {
637        index: u32,
638        stride: wgt::BufferAddress,
639    },
640    #[error("Vertex attribute at location {location} has invalid offset {offset}")]
641    InvalidVertexAttributeOffset {
642        location: wgt::ShaderLocation,
643        offset: wgt::BufferAddress,
644    },
645    #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
646    ShaderLocationClash(u32),
647    #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
648    StripIndexFormatForNonStripTopology {
649        strip_index_format: Option<wgt::IndexFormat>,
650        topology: wgt::PrimitiveTopology,
651    },
652    #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
653    ConservativeRasterizationNonFillPolygonMode,
654    #[error(transparent)]
655    MissingFeatures(#[from] MissingFeatures),
656    #[error(transparent)]
657    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
658    #[error("Error matching {stage:?} shader requirements against the pipeline")]
659    Stage {
660        stage: wgt::ShaderStages,
661        #[source]
662        error: validation::StageError,
663    },
664    #[error("Internal error in {stage:?} shader: {error}")]
665    Internal {
666        stage: wgt::ShaderStages,
667        error: String,
668    },
669    #[error("Pipeline constant error in {stage:?} shader: {error}")]
670    PipelineConstants {
671        stage: wgt::ShaderStages,
672        error: String,
673    },
674    #[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.")]
675    UnalignedShader { group: u32, binding: u32, size: u64 },
676    #[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.")]
677    BlendFactorOnUnsupportedTarget {
678        factor: wgt::BlendFactor,
679        target: u32,
680    },
681    #[error("Pipeline expects the shader entry point to make use of dual-source blending.")]
682    PipelineExpectsShaderToUseDualSourceBlending,
683    #[error("Shader entry point expects the pipeline to make use of dual-source blending.")]
684    ShaderExpectsPipelineToUseDualSourceBlending,
685    #[error("{}", concat!(
686        "At least one color attachment or depth-stencil attachment was expected, ",
687        "but no render target for the pipeline was specified."
688    ))]
689    NoTargetSpecified,
690    #[error(transparent)]
691    InvalidResource(#[from] InvalidResourceError),
692}
693
694impl WebGpuError for CreateRenderPipelineError {
695    fn webgpu_error_type(&self) -> ErrorType {
696        let e: &dyn WebGpuError = match self {
697            Self::Device(e) => e,
698            Self::InvalidResource(e) => e,
699            Self::MissingFeatures(e) => e,
700            Self::MissingDownlevelFlags(e) => e,
701
702            Self::Internal { .. } => return ErrorType::Internal,
703
704            Self::ColorAttachment(_)
705            | Self::Implicit(_)
706            | Self::ColorState(_, _)
707            | Self::DepthStencilState(_)
708            | Self::InvalidSampleCount(_)
709            | Self::TooManyVertexBuffers { .. }
710            | Self::TooManyVertexAttributes { .. }
711            | Self::VertexStrideTooLarge { .. }
712            | Self::UnalignedVertexStride { .. }
713            | Self::InvalidVertexAttributeOffset { .. }
714            | Self::ShaderLocationClash(_)
715            | Self::StripIndexFormatForNonStripTopology { .. }
716            | Self::ConservativeRasterizationNonFillPolygonMode
717            | Self::Stage { .. }
718            | Self::UnalignedShader { .. }
719            | Self::BlendFactorOnUnsupportedTarget { .. }
720            | Self::PipelineExpectsShaderToUseDualSourceBlending
721            | Self::ShaderExpectsPipelineToUseDualSourceBlending
722            | Self::NoTargetSpecified
723            | Self::PipelineConstants { .. }
724            | Self::VertexAttributeStrideTooLarge { .. } => return ErrorType::Validation,
725        };
726        e.webgpu_error_type()
727    }
728}
729
730bitflags::bitflags! {
731    #[repr(transparent)]
732    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
733    pub struct PipelineFlags: u32 {
734        const BLEND_CONSTANT = 1 << 0;
735        const STENCIL_REFERENCE = 1 << 1;
736        const WRITES_DEPTH = 1 << 2;
737        const WRITES_STENCIL = 1 << 3;
738    }
739}
740
741/// How a render pipeline will retrieve attributes from a particular vertex buffer.
742#[derive(Clone, Copy, Debug)]
743pub struct VertexStep {
744    /// The byte stride in the buffer between one attribute value and the next.
745    pub stride: wgt::BufferAddress,
746
747    /// The byte size required to fit the last vertex in the stream.
748    pub last_stride: wgt::BufferAddress,
749
750    /// Whether the buffer is indexed by vertex number or instance number.
751    pub mode: wgt::VertexStepMode,
752}
753
754impl Default for VertexStep {
755    fn default() -> Self {
756        Self {
757            stride: 0,
758            last_stride: 0,
759            mode: wgt::VertexStepMode::Vertex,
760        }
761    }
762}
763
764#[derive(Debug)]
765pub struct RenderPipeline {
766    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
767    pub(crate) device: Arc<Device>,
768    pub(crate) layout: Arc<PipelineLayout>,
769    pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
770    pub(crate) pass_context: RenderPassContext,
771    pub(crate) flags: PipelineFlags,
772    pub(crate) strip_index_format: Option<wgt::IndexFormat>,
773    pub(crate) vertex_steps: Vec<VertexStep>,
774    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
775    /// The `label` from the descriptor used to create the resource.
776    pub(crate) label: String,
777    pub(crate) tracking_data: TrackingData,
778    /// Whether this is a mesh shader pipeline
779    pub(crate) is_mesh: bool,
780}
781
782impl Drop for RenderPipeline {
783    fn drop(&mut self) {
784        resource_log!("Destroy raw {}", self.error_ident());
785        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
786        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
787        unsafe {
788            self.device.raw().destroy_render_pipeline(raw);
789        }
790    }
791}
792
793crate::impl_resource_type!(RenderPipeline);
794crate::impl_labeled!(RenderPipeline);
795crate::impl_parent_device!(RenderPipeline);
796crate::impl_storage_item!(RenderPipeline);
797crate::impl_trackable!(RenderPipeline);
798
799impl RenderPipeline {
800    pub(crate) fn raw(&self) -> &dyn hal::DynRenderPipeline {
801        self.raw.as_ref()
802    }
803}