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