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