Skip to main content

wgpu_core/
pipeline.rs

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