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