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