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::{
18        BindGroupLayout, CreateBindGroupLayoutError, CreatePipelineLayoutError,
19        GetBindGroupLayoutError, PipelineLayout,
20    },
21    command::ColorAttachmentError,
22    device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext},
23    id::{PipelineCacheId, PipelineLayoutId, ShaderModuleId},
24    resource::{InvalidResourceError, Labeled, TrackingData},
25    resource_log, validation, Label,
26};
27
28/// Information about buffer bindings, which
29/// is validated against the shader (and pipeline)
30/// at draw time as opposed to initialization time.
31#[derive(Debug, Default)]
32pub(crate) struct LateSizedBufferGroup {
33    // The order has to match `BindGroup::late_buffer_binding_sizes`.
34    pub(crate) shader_sizes: Vec<wgt::BufferAddress>,
35}
36
37#[allow(clippy::large_enum_variant)]
38pub enum ShaderModuleSource<'a> {
39    #[cfg(feature = "wgsl")]
40    Wgsl(Cow<'a, str>),
41    #[cfg(feature = "glsl")]
42    Glsl(Cow<'a, str>, naga::front::glsl::Options),
43    #[cfg(feature = "spirv")]
44    SpirV(Cow<'a, [u32]>, naga::front::spv::Options),
45    Naga(Cow<'static, naga::Module>),
46    /// Dummy variant because `Naga` doesn't have a lifetime and without enough active features it
47    /// could be the last one active.
48    #[doc(hidden)]
49    Dummy(PhantomData<&'a ()>),
50}
51
52#[derive(Clone, Debug)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub struct ShaderModuleDescriptor<'a> {
55    pub label: Label<'a>,
56    #[cfg_attr(feature = "serde", serde(default))]
57    pub runtime_checks: wgt::ShaderRuntimeChecks,
58}
59
60pub type ShaderModuleDescriptorPassthrough<'a> =
61    wgt::CreateShaderModuleDescriptorPassthrough<'a, Label<'a>>;
62
63#[derive(Debug)]
64pub struct ShaderModule {
65    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynShaderModule>>,
66    pub(crate) device: Arc<Device>,
67    pub(crate) interface: Option<validation::Interface>,
68    /// The `label` from the descriptor used to create the resource.
69    pub(crate) label: String,
70}
71
72impl Drop for ShaderModule {
73    fn drop(&mut self) {
74        resource_log!("Destroy raw {}", self.error_ident());
75        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
76        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
77        unsafe {
78            self.device.raw().destroy_shader_module(raw);
79        }
80    }
81}
82
83crate::impl_resource_type!(ShaderModule);
84crate::impl_labeled!(ShaderModule);
85crate::impl_parent_device!(ShaderModule);
86crate::impl_storage_item!(ShaderModule);
87
88impl ShaderModule {
89    pub(crate) fn raw(&self) -> &dyn hal::DynShaderModule {
90        self.raw.as_ref()
91    }
92
93    pub(crate) fn finalize_entry_point_name(
94        &self,
95        stage: naga::ShaderStage,
96        entry_point: Option<&str>,
97    ) -> Result<String, validation::StageError> {
98        match &self.interface {
99            Some(interface) => interface.finalize_entry_point_name(stage, entry_point),
100            None => entry_point
101                .map(|ep| ep.to_string())
102                .ok_or(validation::StageError::NoEntryPointFound),
103        }
104    }
105}
106
107//Note: `Clone` would require `WithSpan: Clone`.
108#[derive(Clone, Debug, Error)]
109#[non_exhaustive]
110pub enum CreateShaderModuleError {
111    #[cfg(feature = "wgsl")]
112    #[error(transparent)]
113    Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),
114    #[cfg(feature = "glsl")]
115    #[error(transparent)]
116    ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseErrors>),
117    #[cfg(feature = "spirv")]
118    #[error(transparent)]
119    ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>),
120    #[error("Failed to generate the backend-specific code")]
121    Generation,
122    #[error(transparent)]
123    Device(#[from] DeviceError),
124    #[error(transparent)]
125    Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
126    #[error(transparent)]
127    MissingFeatures(#[from] MissingFeatures),
128    #[error(
129        "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
130    )]
131    InvalidGroupIndex {
132        bind: naga::ResourceBinding,
133        group: u32,
134        limit: u32,
135    },
136    #[error("Generic shader passthrough does not contain any code compatible with this backend.")]
137    NotCompiledForBackend,
138}
139
140impl WebGpuError for CreateShaderModuleError {
141    fn webgpu_error_type(&self) -> ErrorType {
142        let e: &dyn WebGpuError = match self {
143            Self::Device(e) => e,
144            Self::MissingFeatures(e) => e,
145
146            Self::Generation => return ErrorType::Internal,
147
148            Self::Validation(..) | Self::InvalidGroupIndex { .. } => return ErrorType::Validation,
149            #[cfg(feature = "wgsl")]
150            Self::Parsing(..) => return ErrorType::Validation,
151            #[cfg(feature = "glsl")]
152            Self::ParsingGlsl(..) => return ErrorType::Validation,
153            #[cfg(feature = "spirv")]
154            Self::ParsingSpirV(..) => return ErrorType::Validation,
155            Self::NotCompiledForBackend => return ErrorType::Validation,
156        };
157        e.webgpu_error_type()
158    }
159}
160
161/// Describes a programmable pipeline stage.
162#[derive(Clone, Debug)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164pub struct ProgrammableStageDescriptor<'a, SM = ShaderModuleId> {
165    /// The compiled shader module for this stage.
166    pub module: SM,
167    /// The name of the entry point in the compiled shader. The name is selected using the
168    /// following logic:
169    ///
170    /// * If `Some(name)` is specified, there must be a function with this name in the shader.
171    /// * If a single entry point associated with this stage must be in the shader, then proceed as
172    ///   if `Some(…)` was specified with that entry point's name.
173    pub entry_point: Option<Cow<'a, str>>,
174    /// Specifies the values of pipeline-overridable constants in the shader module.
175    ///
176    /// If an `@id` attribute was specified on the declaration,
177    /// the key must be the pipeline constant ID as a decimal ASCII number; if not,
178    /// the key must be the constant's identifier name.
179    ///
180    /// The value may represent any of WGSL's concrete scalar types.
181    pub constants: naga::back::PipelineConstants,
182    /// Whether workgroup scoped memory will be initialized with zero values for this stage.
183    ///
184    /// This is required by the WebGPU spec, but may have overhead which can be avoided
185    /// for cross-platform applications
186    pub zero_initialize_workgroup_memory: bool,
187}
188
189/// cbindgen:ignore
190pub type ResolvedProgrammableStageDescriptor<'a> =
191    ProgrammableStageDescriptor<'a, Arc<ShaderModule>>;
192
193/// Number of implicit bind groups derived at pipeline creation.
194pub type ImplicitBindGroupCount = u8;
195
196#[derive(Clone, Debug, Error)]
197#[non_exhaustive]
198pub enum ImplicitLayoutError {
199    #[error("Unable to reflect the shader {0:?} interface")]
200    ReflectionError(wgt::ShaderStages),
201    #[error(transparent)]
202    BindGroup(#[from] CreateBindGroupLayoutError),
203    #[error(transparent)]
204    Pipeline(#[from] CreatePipelineLayoutError),
205    #[error("Unable to create implicit pipeline layout from passthrough shader stage: {0:?}")]
206    Passthrough(wgt::ShaderStages),
207}
208
209impl WebGpuError for ImplicitLayoutError {
210    fn webgpu_error_type(&self) -> ErrorType {
211        let e: &dyn WebGpuError = match self {
212            Self::ReflectionError(_) => return ErrorType::Validation,
213            Self::BindGroup(e) => e,
214            Self::Pipeline(e) => e,
215            Self::Passthrough(_) => return ErrorType::Validation,
216        };
217        e.webgpu_error_type()
218    }
219}
220
221/// Describes a compute pipeline.
222#[derive(Clone, Debug)]
223#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
224pub struct ComputePipelineDescriptor<
225    'a,
226    PLL = PipelineLayoutId,
227    SM = ShaderModuleId,
228    PLC = PipelineCacheId,
229> {
230    pub label: Label<'a>,
231    /// The layout of bind groups for this pipeline.
232    pub layout: Option<PLL>,
233    /// The compiled compute stage and its entry point.
234    pub stage: ProgrammableStageDescriptor<'a, SM>,
235    /// The pipeline cache to use when creating this pipeline.
236    pub cache: Option<PLC>,
237}
238
239/// cbindgen:ignore
240pub type ResolvedComputePipelineDescriptor<'a> =
241    ComputePipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
242
243#[derive(Clone, Debug, Error)]
244#[non_exhaustive]
245pub enum CreateComputePipelineError {
246    #[error(transparent)]
247    Device(#[from] DeviceError),
248    #[error("Unable to derive an implicit layout")]
249    Implicit(#[from] ImplicitLayoutError),
250    #[error("Error matching shader requirements against the pipeline")]
251    Stage(#[from] validation::StageError),
252    #[error("Internal error: {0}")]
253    Internal(String),
254    #[error("Pipeline constant error: {0}")]
255    PipelineConstants(String),
256    #[error(transparent)]
257    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
258    #[error(transparent)]
259    InvalidResource(#[from] InvalidResourceError),
260}
261
262impl WebGpuError for CreateComputePipelineError {
263    fn webgpu_error_type(&self) -> ErrorType {
264        let e: &dyn WebGpuError = match self {
265            Self::Device(e) => e,
266            Self::InvalidResource(e) => e,
267            Self::MissingDownlevelFlags(e) => e,
268            Self::Implicit(e) => e,
269            Self::Stage(e) => e,
270            Self::Internal(_) => return ErrorType::Internal,
271            Self::PipelineConstants(_) => return ErrorType::Validation,
272        };
273        e.webgpu_error_type()
274    }
275}
276
277#[derive(Debug)]
278pub struct ComputePipeline {
279    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,
280    pub(crate) layout: Arc<PipelineLayout>,
281    pub(crate) device: Arc<Device>,
282    pub(crate) _shader_module: Arc<ShaderModule>,
283    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
284    /// The `label` from the descriptor used to create the resource.
285    pub(crate) label: String,
286    pub(crate) tracking_data: TrackingData,
287}
288
289impl Drop for ComputePipeline {
290    fn drop(&mut self) {
291        resource_log!("Destroy raw {}", self.error_ident());
292        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
293        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
294        unsafe {
295            self.device.raw().destroy_compute_pipeline(raw);
296        }
297    }
298}
299
300crate::impl_resource_type!(ComputePipeline);
301crate::impl_labeled!(ComputePipeline);
302crate::impl_parent_device!(ComputePipeline);
303crate::impl_storage_item!(ComputePipeline);
304crate::impl_trackable!(ComputePipeline);
305
306impl ComputePipeline {
307    pub(crate) fn raw(&self) -> &dyn hal::DynComputePipeline {
308        self.raw.as_ref()
309    }
310
311    pub fn get_bind_group_layout(
312        self: &Arc<Self>,
313        index: u32,
314    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
315        self.layout.get_bind_group_layout(index)
316    }
317}
318
319#[derive(Clone, Debug, Error)]
320#[non_exhaustive]
321pub enum CreatePipelineCacheError {
322    #[error(transparent)]
323    Device(#[from] DeviceError),
324    #[error("Pipeline cache validation failed")]
325    Validation(#[from] PipelineCacheValidationError),
326    #[error(transparent)]
327    MissingFeatures(#[from] MissingFeatures),
328}
329
330impl WebGpuError for CreatePipelineCacheError {
331    fn webgpu_error_type(&self) -> ErrorType {
332        let e: &dyn WebGpuError = match self {
333            Self::Device(e) => e,
334            Self::Validation(e) => e,
335            Self::MissingFeatures(e) => e,
336        };
337        e.webgpu_error_type()
338    }
339}
340
341#[derive(Debug)]
342pub struct PipelineCache {
343    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,
344    pub(crate) device: Arc<Device>,
345    /// The `label` from the descriptor used to create the resource.
346    pub(crate) label: String,
347}
348
349impl Drop for PipelineCache {
350    fn drop(&mut self) {
351        resource_log!("Destroy raw {}", self.error_ident());
352        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
353        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
354        unsafe {
355            self.device.raw().destroy_pipeline_cache(raw);
356        }
357    }
358}
359
360crate::impl_resource_type!(PipelineCache);
361crate::impl_labeled!(PipelineCache);
362crate::impl_parent_device!(PipelineCache);
363crate::impl_storage_item!(PipelineCache);
364
365impl PipelineCache {
366    pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {
367        self.raw.as_ref()
368    }
369}
370
371/// Describes how the vertex buffer is interpreted.
372#[derive(Clone, Debug)]
373#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
374#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
375pub struct VertexBufferLayout<'a> {
376    /// The stride, in bytes, between elements of this buffer.
377    pub array_stride: wgt::BufferAddress,
378    /// How often this vertex buffer is "stepped" forward.
379    pub step_mode: wgt::VertexStepMode,
380    /// The list of attributes which comprise a single vertex.
381    pub attributes: Cow<'a, [wgt::VertexAttribute]>,
382}
383
384/// A null vertex buffer layout that may be placed in unused slots.
385impl Default for VertexBufferLayout<'_> {
386    fn default() -> Self {
387        Self {
388            array_stride: Default::default(),
389            step_mode: Default::default(),
390            attributes: Cow::Borrowed(&[]),
391        }
392    }
393}
394
395/// Describes the vertex process in a render pipeline.
396#[derive(Clone, Debug)]
397#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
398pub struct VertexState<'a, SM = ShaderModuleId> {
399    /// The compiled vertex stage and its entry point.
400    pub stage: ProgrammableStageDescriptor<'a, SM>,
401    /// The format of any vertex buffers used with this pipeline.
402    pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
403}
404
405/// cbindgen:ignore
406pub type ResolvedVertexState<'a> = VertexState<'a, Arc<ShaderModule>>;
407
408/// Describes fragment processing in a render pipeline.
409#[derive(Clone, Debug)]
410#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
411pub struct FragmentState<'a, SM = ShaderModuleId> {
412    /// The compiled fragment stage and its entry point.
413    pub stage: ProgrammableStageDescriptor<'a, SM>,
414    /// The effect of draw calls on the color aspect of the output target.
415    pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
416}
417
418/// cbindgen:ignore
419pub type ResolvedFragmentState<'a> = FragmentState<'a, Arc<ShaderModule>>;
420
421/// Describes the task shader in a mesh shader pipeline.
422#[derive(Clone, Debug)]
423#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
424pub struct TaskState<'a, SM = ShaderModuleId> {
425    /// The compiled task stage and its entry point.
426    pub stage: ProgrammableStageDescriptor<'a, SM>,
427}
428
429pub type ResolvedTaskState<'a> = TaskState<'a, Arc<ShaderModule>>;
430
431/// Describes the mesh shader in a mesh shader pipeline.
432#[derive(Clone, Debug)]
433#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
434pub struct MeshState<'a, SM = ShaderModuleId> {
435    /// The compiled mesh stage and its entry point.
436    pub stage: ProgrammableStageDescriptor<'a, SM>,
437}
438
439pub type ResolvedMeshState<'a> = MeshState<'a, Arc<ShaderModule>>;
440
441/// Describes a vertex processor for either a conventional or mesh shading
442/// pipeline architecture.
443///
444/// This is not a public API. It is for use by `player` only. The public APIs
445/// are [`VertexState`], [`TaskState`], and [`MeshState`].
446///
447/// cbindgen:ignore
448#[doc(hidden)]
449#[derive(Clone, Debug)]
450#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
451pub enum RenderPipelineVertexProcessor<'a, SM = ShaderModuleId> {
452    Vertex(VertexState<'a, SM>),
453    Mesh(Option<TaskState<'a, SM>>, MeshState<'a, SM>),
454}
455
456/// Describes a render (graphics) pipeline.
457#[derive(Clone, Debug)]
458#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
459pub struct RenderPipelineDescriptor<
460    'a,
461    PLL = PipelineLayoutId,
462    SM = ShaderModuleId,
463    PLC = PipelineCacheId,
464> {
465    pub label: Label<'a>,
466    /// The layout of bind groups for this pipeline.
467    pub layout: Option<PLL>,
468    /// The vertex processing state for this pipeline.
469    pub vertex: VertexState<'a, SM>,
470    /// The properties of the pipeline at the primitive assembly and rasterization level.
471    #[cfg_attr(feature = "serde", serde(default))]
472    pub primitive: wgt::PrimitiveState,
473    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
474    #[cfg_attr(feature = "serde", serde(default))]
475    pub depth_stencil: Option<wgt::DepthStencilState>,
476    /// The multi-sampling properties of the pipeline.
477    #[cfg_attr(feature = "serde", serde(default))]
478    pub multisample: wgt::MultisampleState,
479    /// The fragment processing state for this pipeline.
480    pub fragment: Option<FragmentState<'a, SM>>,
481    /// If the pipeline will be used with a multiview render pass, this indicates how many array
482    /// layers the attachments will have.
483    pub multiview_mask: Option<NonZeroU32>,
484    /// The pipeline cache to use when creating this pipeline.
485    pub cache: Option<PLC>,
486}
487/// Describes a mesh shader pipeline.
488#[derive(Clone, Debug)]
489#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
490pub struct MeshPipelineDescriptor<
491    'a,
492    PLL = PipelineLayoutId,
493    SM = ShaderModuleId,
494    PLC = PipelineCacheId,
495> {
496    pub label: Label<'a>,
497    /// The layout of bind groups for this pipeline.
498    pub layout: Option<PLL>,
499    /// The task processing state for this pipeline.
500    pub task: Option<TaskState<'a, SM>>,
501    /// The mesh processing state for this pipeline
502    pub mesh: MeshState<'a, SM>,
503    /// The properties of the pipeline at the primitive assembly and rasterization level.
504    #[cfg_attr(feature = "serde", serde(default))]
505    pub primitive: wgt::PrimitiveState,
506    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
507    #[cfg_attr(feature = "serde", serde(default))]
508    pub depth_stencil: Option<wgt::DepthStencilState>,
509    /// The multi-sampling properties of the pipeline.
510    #[cfg_attr(feature = "serde", serde(default))]
511    pub multisample: wgt::MultisampleState,
512    /// The fragment processing state for this pipeline.
513    pub fragment: Option<FragmentState<'a, SM>>,
514    /// If the pipeline will be used with a multiview render pass, this indicates how many array
515    /// layers the attachments will have.
516    pub multiview: Option<NonZeroU32>,
517    /// The pipeline cache to use when creating this pipeline.
518    pub cache: Option<PLC>,
519}
520
521/// Describes a render (graphics) pipeline, with either conventional or mesh
522/// shading architecture.
523///
524/// This is not a public API. It is for use by `player` only. The public APIs
525/// are [`RenderPipelineDescriptor`] and [`MeshPipelineDescriptor`].
526///
527/// cbindgen:ignore
528#[doc(hidden)]
529#[derive(Clone, Debug)]
530#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
531pub struct GeneralRenderPipelineDescriptor<
532    'a,
533    PLL = PipelineLayoutId,
534    SM = ShaderModuleId,
535    PLC = PipelineCacheId,
536> {
537    pub label: Label<'a>,
538    /// The layout of bind groups for this pipeline.
539    pub layout: Option<PLL>,
540    /// The vertex processing state for this pipeline.
541    pub vertex: RenderPipelineVertexProcessor<'a, SM>,
542    /// The properties of the pipeline at the primitive assembly and rasterization level.
543    #[cfg_attr(feature = "serde", serde(default))]
544    pub primitive: wgt::PrimitiveState,
545    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
546    #[cfg_attr(feature = "serde", serde(default))]
547    pub depth_stencil: Option<wgt::DepthStencilState>,
548    /// The multi-sampling properties of the pipeline.
549    #[cfg_attr(feature = "serde", serde(default))]
550    pub multisample: wgt::MultisampleState,
551    /// The fragment processing state for this pipeline.
552    pub fragment: Option<FragmentState<'a, SM>>,
553    /// If the pipeline will be used with a multiview render pass, this indicates how many array
554    /// layers the attachments will have.
555    pub multiview_mask: Option<NonZeroU32>,
556    /// The pipeline cache to use when creating this pipeline.
557    pub cache: Option<PLC>,
558}
559impl<'a, PLL, SM, PLC> From<RenderPipelineDescriptor<'a, PLL, SM, PLC>>
560    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
561{
562    fn from(value: RenderPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
563        Self {
564            label: value.label,
565            layout: value.layout,
566            vertex: RenderPipelineVertexProcessor::Vertex(value.vertex),
567            primitive: value.primitive,
568            depth_stencil: value.depth_stencil,
569            multisample: value.multisample,
570            fragment: value.fragment,
571            multiview_mask: value.multiview_mask,
572            cache: value.cache,
573        }
574    }
575}
576impl<'a, PLL, SM, PLC> From<MeshPipelineDescriptor<'a, PLL, SM, PLC>>
577    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
578{
579    fn from(value: MeshPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
580        Self {
581            label: value.label,
582            layout: value.layout,
583            vertex: RenderPipelineVertexProcessor::Mesh(value.task, value.mesh),
584            primitive: value.primitive,
585            depth_stencil: value.depth_stencil,
586            multisample: value.multisample,
587            fragment: value.fragment,
588            multiview_mask: value.multiview,
589            cache: value.cache,
590        }
591    }
592}
593
594/// Not a public API. For use by `player` only.
595///
596/// cbindgen:ignore
597pub type ResolvedGeneralRenderPipelineDescriptor<'a> =
598    GeneralRenderPipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
599
600#[derive(Clone, Debug)]
601#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
602pub struct PipelineCacheDescriptor<'a> {
603    pub label: Label<'a>,
604    pub data: Option<Cow<'a, [u8]>>,
605    pub fallback: bool,
606}
607
608#[derive(Clone, Debug, Error)]
609#[non_exhaustive]
610pub enum ColorStateError {
611    #[error("Format {0:?} is not renderable")]
612    FormatNotRenderable(wgt::TextureFormat),
613    #[error("Format {0:?} is not blendable")]
614    FormatNotBlendable(wgt::TextureFormat),
615    #[error("Format {0:?} does not have a color aspect")]
616    FormatNotColor(wgt::TextureFormat),
617    #[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:?}.")]
618    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
619    #[error("Output format {pipeline} is incompatible with the shader {shader}")]
620    IncompatibleFormat {
621        pipeline: validation::NumericType,
622        shader: validation::NumericType,
623    },
624    #[error("Invalid write mask {0:?}")]
625    InvalidWriteMask(wgt::ColorWrites),
626    #[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.")]
627    BlendFactorOnUnsupportedTarget {
628        factor: wgt::BlendFactor,
629        target: u32,
630    },
631    #[error(
632        "Blend factor {factor:?} for render target {target} is not valid. Blend factor must be `one` when using min/max blend operations."
633    )]
634    InvalidMinMaxBlendFactor {
635        factor: wgt::BlendFactor,
636        target: u32,
637    },
638}
639
640#[derive(Clone, Debug, Error)]
641#[non_exhaustive]
642pub enum DepthStencilStateError {
643    #[error("Format {0:?} is not renderable")]
644    FormatNotRenderable(wgt::TextureFormat),
645    #[error("Format {0:?} is not a depth/stencil format")]
646    FormatNotDepthOrStencil(wgt::TextureFormat),
647    #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
648    FormatNotDepth(wgt::TextureFormat),
649    #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
650    FormatNotStencil(wgt::TextureFormat),
651    #[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:?}.")]
652    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
653    #[error("Depth bias is not compatible with non-triangle topology {0:?}")]
654    DepthBiasWithIncompatibleTopology(wgt::PrimitiveTopology),
655    #[error("Depth compare function must be specified for depth format {0:?}")]
656    MissingDepthCompare(wgt::TextureFormat),
657    #[error("Depth write enabled must be specified for depth format {0:?}")]
658    MissingDepthWriteEnabled(wgt::TextureFormat),
659}
660
661#[derive(Clone, Debug, Error)]
662#[non_exhaustive]
663pub enum CreateRenderPipelineError {
664    #[error(transparent)]
665    ColorAttachment(#[from] ColorAttachmentError),
666    #[error(transparent)]
667    Device(#[from] DeviceError),
668    #[error("Unable to derive an implicit layout")]
669    Implicit(#[from] ImplicitLayoutError),
670    #[error("Color state [{0}] is invalid")]
671    ColorState(u8, #[source] ColorStateError),
672    #[error("Depth/stencil state is invalid")]
673    DepthStencilState(#[from] DepthStencilStateError),
674    #[error("Invalid sample count {0}")]
675    InvalidSampleCount(u32),
676    #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
677    TooManyVertexBuffers { given: u32, limit: u32 },
678    #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
679    TooManyVertexAttributes { given: u32, limit: u32 },
680    #[error("Vertex attribute location {given} must be less than limit {limit}")]
681    VertexAttributeLocationTooLarge { given: u32, limit: u32 },
682    #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
683    VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
684    #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")]
685    VertexAttributeStrideTooLarge {
686        location: wgt::ShaderLocation,
687        given: u32,
688        limit: u32,
689    },
690    #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_ALIGNMENT`")]
691    UnalignedVertexStride {
692        index: u32,
693        stride: wgt::BufferAddress,
694    },
695    #[error("Vertex attribute at location {location} has invalid offset {offset}")]
696    InvalidVertexAttributeOffset {
697        location: wgt::ShaderLocation,
698        offset: wgt::BufferAddress,
699    },
700    #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
701    ShaderLocationClash(u32),
702    #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
703    StripIndexFormatForNonStripTopology {
704        strip_index_format: Option<wgt::IndexFormat>,
705        topology: wgt::PrimitiveTopology,
706    },
707    #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
708    ConservativeRasterizationNonFillPolygonMode,
709    #[error(transparent)]
710    MissingFeatures(#[from] MissingFeatures),
711    #[error(transparent)]
712    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
713    #[error("Error matching {stage:?} shader requirements against the pipeline")]
714    Stage {
715        stage: wgt::ShaderStages,
716        #[source]
717        error: validation::StageError,
718    },
719    #[error("Internal error in {stage:?} shader: {error}")]
720    Internal {
721        stage: wgt::ShaderStages,
722        error: String,
723    },
724    #[error("Pipeline constant error in {stage:?} shader: {error}")]
725    PipelineConstants {
726        stage: wgt::ShaderStages,
727        error: String,
728    },
729    #[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.")]
730    UnalignedShader { group: u32, binding: u32, size: u64 },
731    #[error("{}", concat!(
732        "At least one color attachment or depth-stencil attachment was expected, ",
733        "but no render target for the pipeline was specified."
734    ))]
735    NoTargetSpecified,
736    #[error(transparent)]
737    InvalidResource(#[from] InvalidResourceError),
738}
739
740impl WebGpuError for CreateRenderPipelineError {
741    fn webgpu_error_type(&self) -> ErrorType {
742        let e: &dyn WebGpuError = match self {
743            Self::Device(e) => e,
744            Self::InvalidResource(e) => e,
745            Self::MissingFeatures(e) => e,
746            Self::MissingDownlevelFlags(e) => e,
747
748            Self::Internal { .. } => return ErrorType::Internal,
749
750            Self::ColorAttachment(_)
751            | Self::Implicit(_)
752            | Self::ColorState(_, _)
753            | Self::DepthStencilState(_)
754            | Self::InvalidSampleCount(_)
755            | Self::TooManyVertexBuffers { .. }
756            | Self::TooManyVertexAttributes { .. }
757            | Self::VertexAttributeLocationTooLarge { .. }
758            | Self::VertexStrideTooLarge { .. }
759            | Self::UnalignedVertexStride { .. }
760            | Self::InvalidVertexAttributeOffset { .. }
761            | Self::ShaderLocationClash(_)
762            | Self::StripIndexFormatForNonStripTopology { .. }
763            | Self::ConservativeRasterizationNonFillPolygonMode
764            | Self::Stage { .. }
765            | Self::UnalignedShader { .. }
766            | Self::NoTargetSpecified
767            | Self::PipelineConstants { .. }
768            | Self::VertexAttributeStrideTooLarge { .. } => return ErrorType::Validation,
769        };
770        e.webgpu_error_type()
771    }
772}
773
774bitflags::bitflags! {
775    #[repr(transparent)]
776    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
777    pub struct PipelineFlags: u32 {
778        const BLEND_CONSTANT = 1 << 0;
779        const STENCIL_REFERENCE = 1 << 1;
780        const WRITES_DEPTH = 1 << 2;
781        const WRITES_STENCIL = 1 << 3;
782    }
783}
784
785/// How a render pipeline will retrieve attributes from a particular vertex buffer.
786#[derive(Clone, Copy, Debug)]
787pub struct VertexStep {
788    /// The byte stride in the buffer between one attribute value and the next.
789    pub stride: wgt::BufferAddress,
790
791    /// The byte size required to fit the last vertex in the stream.
792    pub last_stride: wgt::BufferAddress,
793
794    /// Whether the buffer is indexed by vertex number or instance number.
795    pub mode: wgt::VertexStepMode,
796}
797
798impl Default for VertexStep {
799    fn default() -> Self {
800        Self {
801            stride: 0,
802            last_stride: 0,
803            mode: wgt::VertexStepMode::Vertex,
804        }
805    }
806}
807
808#[derive(Debug)]
809pub struct RenderPipeline {
810    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
811    pub(crate) device: Arc<Device>,
812    pub(crate) layout: Arc<PipelineLayout>,
813    pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
814    pub(crate) pass_context: RenderPassContext,
815    pub(crate) flags: PipelineFlags,
816    pub(crate) topology: wgt::PrimitiveTopology,
817    pub(crate) strip_index_format: Option<wgt::IndexFormat>,
818    pub(crate) vertex_steps: Vec<VertexStep>,
819    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
820    /// The `label` from the descriptor used to create the resource.
821    pub(crate) label: String,
822    pub(crate) tracking_data: TrackingData,
823    /// Whether this is a mesh shader pipeline
824    pub(crate) is_mesh: bool,
825}
826
827impl Drop for RenderPipeline {
828    fn drop(&mut self) {
829        resource_log!("Destroy raw {}", self.error_ident());
830        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
831        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
832        unsafe {
833            self.device.raw().destroy_render_pipeline(raw);
834        }
835    }
836}
837
838crate::impl_resource_type!(RenderPipeline);
839crate::impl_labeled!(RenderPipeline);
840crate::impl_parent_device!(RenderPipeline);
841crate::impl_storage_item!(RenderPipeline);
842crate::impl_trackable!(RenderPipeline);
843
844impl RenderPipeline {
845    pub(crate) fn raw(&self) -> &dyn hal::DynRenderPipeline {
846        self.raw.as_ref()
847    }
848
849    pub fn get_bind_group_layout(
850        self: &Arc<Self>,
851        index: u32,
852    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
853        self.layout.get_bind_group_layout(index)
854    }
855}