Skip to main content

wgpu_core/
pipeline.rs

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