Skip to main content

wgpu_core/
pipeline.rs

1use alloc::string::ToString as _;
2use alloc::{
3    borrow::{Cow, ToOwned},
4    boxed::Box,
5    string::String,
6    sync::Arc,
7    vec::Vec,
8};
9use core::{marker::PhantomData, mem::ManuallyDrop, num::NonZeroU32};
10
11use arrayvec::ArrayVec;
12use naga::error::ShaderError;
13use thiserror::Error;
14use wgt::error::{ErrorType, WebGpuError};
15
16pub use crate::pipeline_cache::PipelineCacheValidationError;
17use crate::{
18    api_log,
19    binding_model::{
20        BindGroupLayout, CreateBindGroupLayoutError, CreatePipelineLayoutError,
21        GetBindGroupLayoutError, PipelineLayout,
22    },
23    command::ColorAttachmentError,
24    device::{
25        AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
26        RenderPassContext,
27    },
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    pub(crate) compilation_info: wgt::CompilationInfo,
83}
84
85impl Drop for ShaderModule {
86    #[allow(trivial_casts)]
87    fn drop(&mut self) {
88        profiling::scope!("ShaderModule::drop");
89        api_log!("ShaderModule::drop {:?}", self as *const _);
90        resource_log!("Destroy raw {}", self.error_ident());
91        #[cfg(feature = "trace")]
92        if let Some(t) = self.device.trace.lock().as_mut() {
93            use crate::device::trace::{to_trace, Action};
94
95            t.add(Action::DropShaderModule(unsafe { to_trace(self) }));
96        }
97        let ResourceState::Valid(state) =
98            core::mem::replace(&mut self.state, ResourceState::Invalid)
99        else {
100            return;
101        };
102        unsafe {
103            self.device.raw().destroy_shader_module(state.raw);
104        }
105    }
106}
107
108crate::impl_resource_type!(ShaderModule);
109crate::impl_labeled!(ShaderModule);
110crate::impl_parent_device!(ShaderModule);
111crate::impl_storage_item!(ShaderModule);
112
113impl ShaderModule {
114    pub(crate) fn state(&self) -> Result<&ShaderModuleState, InvalidResourceError> {
115        let ResourceState::Valid(state) = &self.state else {
116            return Err(InvalidResourceError(self.error_ident()));
117        };
118        Ok(state)
119    }
120
121    pub(crate) fn invalid(
122        device: Arc<Device>,
123        label: String,
124        compilation_info: wgt::CompilationInfo,
125    ) -> Arc<Self> {
126        Arc::new(Self {
127            state: ResourceState::Invalid,
128            device,
129            label,
130            compilation_info,
131        })
132    }
133
134    pub fn compilation_info(&self) -> &wgt::CompilationInfo {
135        &self.compilation_info
136    }
137
138    /// Select an entry point name, given an optional name and a shader stage.
139    ///
140    /// This function takes care of turning the `Option<&str>`
141    /// [`ProgrammableStageDescriptor::entry_point`][ep] into a specific name.
142    ///
143    /// For non-passthrough shaders, if `entry_point` is `Some`, then return it
144    /// as a `String`. Otherwise, return the name of the unique entry point in
145    /// `self`'s module for `stage`; if there is not exactly one such entry
146    /// point, return an error.
147    ///
148    /// The non-passthrough case counts on `Interface::check_stage` to verify
149    /// that an entry point with the given name actually exists.
150    ///
151    /// For passthrough shaders, if `entry_point` is `Some`, verify that an
152    /// entry point by that name exists (returning an error if not), and return
153    /// it as a `String`. Otherwise, if `entry_point` is `None`, then check that
154    /// this module has exactly one entry point, and return its name.
155    ///
156    /// [ep]: crate::pipeline::ProgrammableStageDescriptor::entry_point
157    pub(crate) fn finalize_entry_point_name(
158        &self,
159        stage: naga::ShaderStage,
160        entry_point: Option<&str>,
161    ) -> Result<String, validation::StageError> {
162        let state = self.state()?;
163        match state.interface {
164            ShaderMetaData::Interface(ref interface) => {
165                interface.finalize_entry_point_name(stage, entry_point)
166            }
167            ShaderMetaData::Passthrough(ref interface) => {
168                finalize_passthrough_entry_point_name(interface, entry_point)
169            }
170        }
171    }
172}
173
174fn finalize_passthrough_entry_point_name(
175    interface: &validation::PassthroughInterface,
176    entry_point: Option<&str>,
177) -> Result<String, validation::StageError> {
178    if let Some(ep) = entry_point {
179        return if interface.entry_point_names.contains(ep) {
180            Ok(ep.to_owned())
181        } else {
182            Err(validation::StageError::MissingEntryPoint(ep.to_owned()))
183        };
184    }
185
186    match interface.entry_point_names.len() {
187        0 => Err(validation::StageError::NoEntryPointFound),
188        1 => Ok(interface
189            .entry_point_names
190            .iter()
191            .next()
192            .unwrap()
193            .to_owned()),
194        _ => Err(validation::StageError::MultipleEntryPointsFound),
195    }
196}
197
198//Note: `Clone` would require `WithSpan: Clone`.
199#[derive(Clone, Debug, Error)]
200#[non_exhaustive]
201pub enum CreateShaderModuleError {
202    // These variants deliberately don't forward to `ShaderError`'s `Display`,
203    // which would include the shader source text and detailed compiler messages:
204    // per the WebGPU specification <https://gpuweb.github.io/gpuweb/#dom-gpudevice-createshadermodule>,
205    // the message of the validation error raised by `createShaderModule` should not include those details,
206    // since they are accessible via `getCompilationInfo()`.
207    #[cfg(feature = "wgsl")]
208    #[error("Shader '{label}' parsing error. Concrete error is available via `get_compilation_info`", label = _0.label.as_deref().unwrap_or_default())]
209    Parsing(ShaderError<naga::front::wgsl::ParseError>),
210
211    #[cfg(feature = "glsl")]
212    #[error("Shader '{label}' parsing error. Concrete error is available via `get_compilation_info`", label = _0.label.as_deref().unwrap_or_default())]
213    ParsingGlsl(ShaderError<naga::front::glsl::ParseErrors>),
214
215    #[cfg(feature = "spirv")]
216    #[error("Shader '{label}' parsing error. Concrete error is available via `get_compilation_info`", label = _0.label.as_deref().unwrap_or_default())]
217    ParsingSpirV(ShaderError<naga::front::spv::Error>),
218
219    #[error("Failed to generate the backend-specific code")]
220    Generation,
221
222    #[error(transparent)]
223    Device(#[from] DeviceError),
224
225    #[error("Shader '{label}' validation error. Concrete error is available via `get_compilation_info`", label = _0.label.as_deref().unwrap_or_default())]
226    Validation(ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
227
228    #[error(transparent)]
229    MissingFeatures(#[from] MissingFeatures),
230
231    #[error(
232        "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
233    )]
234    InvalidGroupIndex {
235        bind: naga::ResourceBinding,
236        group: u32,
237        limit: u32,
238    },
239
240    #[error("Generic shader passthrough does not contain any code compatible with this backend.")]
241    NotCompiledForBackend,
242
243    #[error(
244        "Generic passthrough shaders which use GLSL or DXIL must contain exactly one entry point."
245    )]
246    IncorrectPassthroughEntryPointCount,
247}
248
249impl WebGpuError for CreateShaderModuleError {
250    fn webgpu_error_type(&self) -> ErrorType {
251        match self {
252            Self::Device(e) => e.webgpu_error_type(),
253            Self::MissingFeatures(e) => e.webgpu_error_type(),
254
255            Self::Generation => ErrorType::Internal,
256
257            Self::Validation(..)
258            | Self::InvalidGroupIndex { .. }
259            | Self::IncorrectPassthroughEntryPointCount
260            | Self::NotCompiledForBackend => ErrorType::Validation,
261            #[cfg(feature = "wgsl")]
262            Self::Parsing(..) => ErrorType::Validation,
263            #[cfg(feature = "glsl")]
264            Self::ParsingGlsl(..) => ErrorType::Validation,
265            #[cfg(feature = "spirv")]
266            Self::ParsingSpirV(..) => ErrorType::Validation,
267        }
268    }
269}
270
271#[cfg(feature = "wgsl")]
272pub(crate) fn wgsl_to_compilation_info(
273    value: &ShaderError<naga::front::wgsl::ParseError>,
274) -> wgt::CompilationInfo {
275    use alloc::{string::ToString, vec};
276    wgt::CompilationInfo {
277        messages: vec![wgt::CompilationMessage {
278            message: value.to_string(),
279            message_type: wgt::CompilationMessageType::Error,
280            location: value
281                .inner
282                .location(&value.source)
283                .as_ref()
284                .map(naga_to_source_location),
285        }],
286    }
287}
288#[cfg(feature = "glsl")]
289pub(crate) fn glsl_to_compilation_info(
290    value: &ShaderError<naga::front::glsl::ParseErrors>,
291) -> wgt::CompilationInfo {
292    use alloc::string::ToString;
293    let messages = value
294        .inner
295        .errors
296        .iter()
297        .map(|err| wgt::CompilationMessage {
298            message: err.to_string(),
299            message_type: wgt::CompilationMessageType::Error,
300            location: err
301                .location(&value.source)
302                .as_ref()
303                .map(naga_to_source_location),
304        })
305        .collect();
306    wgt::CompilationInfo { messages }
307}
308
309#[cfg(feature = "spirv")]
310pub(crate) fn spirv_to_compilation_info(
311    value: &ShaderError<naga::front::spv::Error>,
312) -> wgt::CompilationInfo {
313    use alloc::{string::ToString, vec};
314    wgt::CompilationInfo {
315        messages: vec![wgt::CompilationMessage {
316            message: value.to_string(),
317            message_type: wgt::CompilationMessageType::Error,
318            location: None,
319        }],
320    }
321}
322
323pub(crate) fn naga_to_compilation_info(
324    value: &ShaderError<naga::WithSpan<naga::valid::ValidationError>>,
325) -> wgt::CompilationInfo {
326    use alloc::{string::ToString, vec};
327    wgt::CompilationInfo {
328        messages: vec![wgt::CompilationMessage {
329            message: value.to_string(),
330            message_type: wgt::CompilationMessageType::Error,
331            location: value
332                .inner
333                .location(&value.source)
334                .as_ref()
335                .map(naga_to_source_location),
336        }],
337    }
338}
339
340fn naga_to_source_location(value: &naga::SourceLocation) -> wgt::SourceLocation {
341    wgt::SourceLocation {
342        length: value.length,
343        offset: value.offset,
344        line_number: value.line_number,
345        line_position: value.line_position,
346    }
347}
348
349pub(crate) fn shader_module_error_into_compilation_info(
350    value: &CreateShaderModuleError,
351) -> wgt::CompilationInfo {
352    match value {
353        #[cfg(feature = "wgsl")]
354        CreateShaderModuleError::Parsing(v) => wgsl_to_compilation_info(v),
355        #[cfg(feature = "glsl")]
356        CreateShaderModuleError::ParsingGlsl(v) => glsl_to_compilation_info(v),
357        #[cfg(feature = "spirv")]
358        CreateShaderModuleError::ParsingSpirV(v) => spirv_to_compilation_info(v),
359        CreateShaderModuleError::Validation(v) => naga_to_compilation_info(v),
360        // Device errors are reported through the error sink, and are not compilation errors.
361        // Same goes for native shader module generation errors.
362        CreateShaderModuleError::Device(_) | CreateShaderModuleError::Generation => {
363            wgt::CompilationInfo {
364                messages: Vec::new(),
365            }
366        }
367        // Everything else is an error message without location information.
368        _ => wgt::CompilationInfo {
369            messages: alloc::vec![wgt::CompilationMessage {
370                message: value.to_string(),
371                message_type: wgt::CompilationMessageType::Error,
372                location: None,
373            }],
374        },
375    }
376}
377
378/// Describes a programmable pipeline stage.
379#[derive(Clone, Debug)]
380#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
381/// cbindgen:ignore
382pub struct ProgrammableStageDescriptor<'a, SM = Arc<ShaderModule>> {
383    /// The compiled shader module for this stage.
384    pub module: SM,
385
386    /// The name of the entry point in `module` that this stage should use.
387    ///
388    /// - If this is `Some(name)`, `module` must contain an entry point with the
389    ///   given name.
390    ///
391    /// - If this is `None`, `module` must have only one entry point for this
392    ///   stage; we use that one.
393    pub entry_point: Option<Cow<'a, str>>,
394
395    /// Values for pipeline-overridable constants in `module` that this stage
396    /// should use.
397    ///
398    /// If an `@id` attribute was specified on the declaration,
399    /// the key must be the pipeline constant ID as a decimal ASCII number; if not,
400    /// the key must be the constant's identifier name.
401    ///
402    /// The value may represent any of WGSL's concrete scalar types.
403    pub constants: naga::back::PipelineConstants,
404
405    /// Whether variables in the workgroup address space will be initialized
406    /// with zero values for this stage.
407    ///
408    /// The WebGPU spec requires variables in the workgroup address space to be
409    /// zeroed. However, initialization does impose some overhead, and
410    /// non-browser applications may not need it.
411    pub zero_initialize_workgroup_memory: bool,
412}
413
414/// Number of implicit bind groups derived at pipeline creation.
415pub type ImplicitBindGroupCount = u8;
416
417#[derive(Clone, Debug, Error)]
418#[non_exhaustive]
419pub enum ImplicitLayoutError {
420    #[error("Unable to reflect the shader {0:?} interface")]
421    ReflectionError(wgt::ShaderStages),
422    #[error(transparent)]
423    BindGroup(#[from] CreateBindGroupLayoutError),
424    #[error(transparent)]
425    Pipeline(#[from] CreatePipelineLayoutError),
426    #[error("Unable to create implicit pipeline layout from passthrough shader stage: {0:?}")]
427    Passthrough(wgt::ShaderStages),
428}
429
430impl WebGpuError for ImplicitLayoutError {
431    fn webgpu_error_type(&self) -> ErrorType {
432        match self {
433            Self::ReflectionError(_) => ErrorType::Validation,
434            Self::BindGroup(e) => e.webgpu_error_type(),
435            Self::Pipeline(e) => e.webgpu_error_type(),
436            Self::Passthrough(_) => ErrorType::Validation,
437        }
438    }
439}
440
441/// Describes a compute pipeline.
442#[derive(Clone, Debug)]
443#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
444/// cbindgen:ignore
445pub struct ComputePipelineDescriptor<
446    'a,
447    PLL = Arc<PipelineLayout>,
448    SM = Arc<ShaderModule>,
449    PLC = Arc<PipelineCache>,
450> {
451    pub label: Label<'a>,
452    /// The layout of bind groups for this pipeline.
453    pub layout: Option<PLL>,
454    /// The compiled compute stage and its entry point.
455    pub stage: ProgrammableStageDescriptor<'a, SM>,
456    /// The pipeline cache to use when creating this pipeline.
457    pub cache: Option<PLC>,
458}
459
460#[derive(Clone, Debug, Error)]
461#[non_exhaustive]
462pub enum CreateComputePipelineError {
463    #[error(transparent)]
464    Device(#[from] DeviceError),
465    #[error("Unable to derive an implicit layout")]
466    Implicit(#[from] ImplicitLayoutError),
467    #[error("Error matching shader requirements against the pipeline")]
468    Stage(#[from] validation::StageError),
469    #[error("Internal error: {0}")]
470    Internal(String),
471    #[error("Pipeline constant error: {0}")]
472    PipelineConstants(String),
473    #[error(transparent)]
474    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
475    #[error(transparent)]
476    InvalidResource(#[from] InvalidResourceError),
477}
478
479impl WebGpuError for CreateComputePipelineError {
480    fn webgpu_error_type(&self) -> ErrorType {
481        match self {
482            Self::Device(e) => e.webgpu_error_type(),
483            Self::InvalidResource(e) => e.webgpu_error_type(),
484            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
485            Self::Implicit(e) => e.webgpu_error_type(),
486            Self::Stage(e) => e.webgpu_error_type(),
487            Self::Internal(_) => ErrorType::Internal,
488            Self::PipelineConstants(_) => ErrorType::Validation,
489        }
490    }
491}
492
493#[derive(Debug)]
494pub struct ComputePipelineState {
495    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,
496    pub(crate) layout: Arc<PipelineLayout>,
497    pub(crate) _shader_module: Arc<ShaderModule>,
498}
499
500#[derive(Debug)]
501pub struct ComputePipeline {
502    pub(crate) state: ResourceState<ComputePipelineState>,
503    pub(crate) device: Arc<Device>,
504    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
505    pub(crate) immediate_slots_required: naga::valid::ImmediateSlots,
506    /// The `label` from the descriptor used to create the resource.
507    pub(crate) label: String,
508    pub(crate) tracking_data: TrackingData,
509}
510
511impl Drop for ComputePipeline {
512    #[allow(trivial_casts)]
513    fn drop(&mut self) {
514        profiling::scope!("ComputePipeline::drop");
515        api_log!("ComputePipeline::drop {:?}", self as *const _);
516        resource_log!("Destroy raw {}", self.error_ident());
517        #[cfg(feature = "trace")]
518        {
519            use crate::device::trace;
520            if let Some(t) = self.device.trace.lock().as_mut() {
521                t.add(trace::Action::DropComputePipeline(unsafe {
522                    trace::to_trace(self)
523                }));
524            }
525        }
526        let ResourceState::Valid(state) = &mut self.state else {
527            return;
528        };
529        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
530        let raw = unsafe { ManuallyDrop::take(&mut state.raw) };
531        unsafe {
532            self.device.raw().destroy_compute_pipeline(raw);
533        }
534    }
535}
536
537crate::impl_resource_type!(ComputePipeline);
538crate::impl_labeled!(ComputePipeline);
539crate::impl_parent_device!(ComputePipeline);
540crate::impl_storage_item!(ComputePipeline);
541crate::impl_trackable!(ComputePipeline);
542
543impl ComputePipeline {
544    pub(crate) fn raw(&self) -> Result<&dyn hal::DynComputePipeline, InvalidResourceError> {
545        let ResourceState::Valid(state) = &self.state else {
546            return Err(InvalidResourceError(self.error_ident()));
547        };
548        Ok(state.raw.as_ref())
549    }
550
551    pub(crate) fn layout(&self) -> Result<&Arc<PipelineLayout>, InvalidResourceError> {
552        let ResourceState::Valid(state) = &self.state else {
553            return Err(InvalidResourceError(self.error_ident()));
554        };
555        Ok(&state.layout)
556    }
557
558    pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
559        let ResourceState::Valid(_) = &self.state else {
560            return Err(InvalidResourceError(self.error_ident()));
561        };
562        Ok(())
563    }
564
565    pub(crate) fn invalid(device: Arc<Device>, label: String) -> Arc<Self> {
566        Arc::new(Self {
567            tracking_data: TrackingData::new(device.tracker_indices.compute_pipelines.clone()),
568            state: ResourceState::Invalid,
569            device,
570            late_sized_buffer_groups: ArrayVec::new(),
571            immediate_slots_required: naga::valid::ImmediateSlots::default(),
572            label,
573        })
574    }
575
576    pub fn get_bind_group_layout_inner(
577        self: &Arc<Self>,
578        index: u32,
579    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
580        self.layout()?.get_bind_group_layout(index, self.into())
581    }
582
583    pub fn get_bind_group_layout(self: &Arc<Self>, index: u32) -> Arc<BindGroupLayout> {
584        let bgl = self
585            .get_bind_group_layout_inner(index)
586            .unwrap_or_else(|err| {
587                self.device
588                    .handle_error_nolabel(err, "ComputePipeline::get_bind_group_layout");
589                BindGroupLayout::invalid(&self.device, String::new())
590            });
591        #[cfg(feature = "trace")]
592        if let Some(ref mut trace) = *self.device.trace.lock() {
593            use crate::device::trace;
594            use trace::IntoTrace;
595            trace.add(trace::Action::GetComputePipelineBindGroupLayout {
596                id: bgl.to_trace(),
597                pipeline: self.to_trace(),
598                index,
599            });
600        };
601        bgl
602    }
603}
604
605#[derive(Clone, Debug, Error)]
606#[non_exhaustive]
607pub enum CreatePipelineCacheError {
608    #[error(transparent)]
609    Device(#[from] DeviceError),
610    #[error("Pipeline cache validation failed")]
611    Validation(#[from] PipelineCacheValidationError),
612    #[error(transparent)]
613    MissingFeatures(#[from] MissingFeatures),
614}
615
616impl WebGpuError for CreatePipelineCacheError {
617    fn webgpu_error_type(&self) -> ErrorType {
618        match self {
619            Self::Device(e) => e.webgpu_error_type(),
620            Self::Validation(e) => e.webgpu_error_type(),
621            Self::MissingFeatures(e) => e.webgpu_error_type(),
622        }
623    }
624}
625
626#[derive(Debug)]
627pub struct PipelineCache {
628    pub(crate) raw: ResourceState<Box<dyn hal::DynPipelineCache>>,
629    pub(crate) device: Arc<Device>,
630    /// The `label` from the descriptor used to create the resource.
631    pub(crate) label: String,
632}
633
634impl Drop for PipelineCache {
635    #[allow(trivial_casts)]
636    fn drop(&mut self) {
637        profiling::scope!("PipelineCache::drop");
638        api_log!("PipelineCache::drop {:?}", self as *const _);
639        #[cfg(feature = "trace")]
640        if let Some(t) = self.device.trace.lock().as_mut() {
641            use crate::device::trace::{to_trace, Action};
642            t.add(Action::DropPipelineCache(unsafe { to_trace(self) }));
643        }
644        resource_log!("Destroy raw {}", self.error_ident());
645        if let ResourceState::Valid(raw) = core::mem::replace(&mut self.raw, ResourceState::Invalid)
646        {
647            unsafe {
648                self.device.raw().destroy_pipeline_cache(raw);
649            }
650        }
651    }
652}
653
654crate::impl_resource_type!(PipelineCache);
655crate::impl_labeled!(PipelineCache);
656crate::impl_parent_device!(PipelineCache);
657crate::impl_storage_item!(PipelineCache);
658
659impl PipelineCache {
660    pub(crate) fn raw(&self) -> Result<&dyn hal::DynPipelineCache, InvalidResourceError> {
661        self.raw
662            .as_ref()
663            .valid()
664            .map(|raw| raw.as_ref())
665            .ok_or_else(|| InvalidResourceError(self.error_ident()))
666    }
667
668    pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
669        self.raw().map(|_| ())
670    }
671
672    pub(crate) fn invalid(device: Arc<Device>, desc: &PipelineCacheDescriptor) -> Arc<Self> {
673        Arc::new(Self {
674            raw: ResourceState::Invalid,
675            device,
676            label: desc.label.to_string(),
677        })
678    }
679
680    pub fn get_data(self: &Arc<Self>) -> Option<Vec<u8>> {
681        api_log!("PipelineCache::get_data");
682
683        let ResourceState::Valid(raw) = &self.raw else {
684            return None;
685        };
686
687        if !self.device.is_valid() {
688            return None;
689        }
690        let mut vec = unsafe { self.device.raw().pipeline_cache_get_data(raw.as_ref()) }?;
691        let validation_key = self.device.raw().pipeline_cache_validation_key()?;
692
693        let mut header_contents = [0; pipeline_cache::HEADER_LENGTH];
694        pipeline_cache::add_cache_header(
695            &mut header_contents,
696            &vec,
697            &self.device.adapter.raw.info,
698            validation_key,
699        );
700
701        let deleted = vec.splice(..0, header_contents).collect::<Vec<_>>();
702        debug_assert!(deleted.is_empty());
703
704        Some(vec)
705    }
706}
707
708/// Describes how the vertex buffer is interpreted.
709#[derive(Clone, Debug)]
710#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
711#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
712pub struct VertexBufferLayout<'a> {
713    /// The stride, in bytes, between elements of this buffer.
714    pub array_stride: wgt::BufferAddress,
715    /// How often this vertex buffer is "stepped" forward.
716    pub step_mode: wgt::VertexStepMode,
717    /// The list of attributes which comprise a single vertex.
718    pub attributes: Cow<'a, [wgt::VertexAttribute]>,
719}
720
721/// A null vertex buffer layout that may be placed in unused slots.
722impl Default for VertexBufferLayout<'_> {
723    fn default() -> Self {
724        Self {
725            array_stride: Default::default(),
726            step_mode: Default::default(),
727            attributes: Cow::Borrowed(&[]),
728        }
729    }
730}
731
732/// Describes the vertex process in a render pipeline.
733#[derive(Clone, Debug)]
734#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
735/// cbindgen:ignore
736pub struct VertexState<'a, SM = Arc<ShaderModule>> {
737    /// The compiled vertex stage and its entry point.
738    pub stage: ProgrammableStageDescriptor<'a, SM>,
739    /// The format of any vertex buffers used with this pipeline.
740    pub buffers: Cow<'a, [Option<VertexBufferLayout<'a>>]>,
741}
742
743/// Describes fragment processing in a render pipeline.
744#[derive(Clone, Debug)]
745#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
746/// cbindgen:ignore
747pub struct FragmentState<'a, SM = Arc<ShaderModule>> {
748    /// The compiled fragment stage and its entry point.
749    pub stage: ProgrammableStageDescriptor<'a, SM>,
750    /// The effect of draw calls on the color aspect of the output target.
751    pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
752}
753
754/// Describes the task shader in a mesh shader pipeline.
755#[derive(Clone, Debug)]
756#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
757pub struct TaskState<'a, SM = Arc<ShaderModule>> {
758    /// The compiled task stage and its entry point.
759    pub stage: ProgrammableStageDescriptor<'a, SM>,
760}
761
762/// Describes the mesh shader in a mesh shader pipeline.
763#[derive(Clone, Debug)]
764#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
765pub struct MeshState<'a, SM = Arc<ShaderModule>> {
766    /// The compiled mesh stage and its entry point.
767    pub stage: ProgrammableStageDescriptor<'a, SM>,
768}
769
770/// Describes a vertex processor for either a conventional or mesh shading
771/// pipeline architecture.
772///
773/// This is not a public API. It is for use by `player` only. The public APIs
774/// are [`VertexState`], [`TaskState`], and [`MeshState`].
775///
776/// cbindgen:ignore
777#[doc(hidden)]
778#[derive(Clone, Debug)]
779#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
780pub enum RenderPipelineVertexProcessor<'a, SM = Arc<ShaderModule>> {
781    Vertex(VertexState<'a, SM>),
782    Mesh(Option<TaskState<'a, SM>>, MeshState<'a, SM>),
783}
784
785/// Describes a render (graphics) pipeline.
786#[derive(Clone, Debug)]
787#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
788pub struct RenderPipelineDescriptor<
789    'a,
790    PLL = Arc<PipelineLayout>,
791    SM = Arc<ShaderModule>,
792    PLC = Arc<PipelineCache>,
793> {
794    pub label: Label<'a>,
795    /// The layout of bind groups for this pipeline.
796    pub layout: Option<PLL>,
797    /// The vertex processing state for this pipeline.
798    pub vertex: VertexState<'a, SM>,
799    /// The properties of the pipeline at the primitive assembly and rasterization level.
800    #[cfg_attr(feature = "serde", serde(default))]
801    pub primitive: wgt::PrimitiveState,
802    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
803    #[cfg_attr(feature = "serde", serde(default))]
804    pub depth_stencil: Option<wgt::DepthStencilState>,
805    /// The multi-sampling properties of the pipeline.
806    #[cfg_attr(feature = "serde", serde(default))]
807    pub multisample: wgt::MultisampleState,
808    /// The fragment processing state for this pipeline.
809    pub fragment: Option<FragmentState<'a, SM>>,
810    /// If the pipeline will be used with a multiview render pass, this indicates how many array
811    /// layers the attachments will have.
812    pub multiview_mask: Option<NonZeroU32>,
813    /// The pipeline cache to use when creating this pipeline.
814    pub cache: Option<PLC>,
815}
816/// Describes a mesh shader pipeline.
817#[derive(Clone, Debug)]
818#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
819pub struct MeshPipelineDescriptor<
820    'a,
821    PLL = Arc<PipelineLayout>,
822    SM = Arc<ShaderModule>,
823    PLC = Arc<PipelineCache>,
824> {
825    pub label: Label<'a>,
826    /// The layout of bind groups for this pipeline.
827    pub layout: Option<PLL>,
828    /// The task processing state for this pipeline.
829    pub task: Option<TaskState<'a, SM>>,
830    /// The mesh processing state for this pipeline
831    pub mesh: MeshState<'a, SM>,
832    /// The properties of the pipeline at the primitive assembly and rasterization level.
833    #[cfg_attr(feature = "serde", serde(default))]
834    pub primitive: wgt::PrimitiveState,
835    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
836    #[cfg_attr(feature = "serde", serde(default))]
837    pub depth_stencil: Option<wgt::DepthStencilState>,
838    /// The multi-sampling properties of the pipeline.
839    #[cfg_attr(feature = "serde", serde(default))]
840    pub multisample: wgt::MultisampleState,
841    /// The fragment processing state for this pipeline.
842    pub fragment: Option<FragmentState<'a, SM>>,
843    /// If the pipeline will be used with a multiview render pass, this indicates how many array
844    /// layers the attachments will have.
845    pub multiview: Option<NonZeroU32>,
846    /// The pipeline cache to use when creating this pipeline.
847    pub cache: Option<PLC>,
848}
849
850/// Describes a render (graphics) pipeline, with either conventional or mesh
851/// shading architecture.
852///
853/// This is not a public API. It is for use by `player` only. The public APIs
854/// are [`RenderPipelineDescriptor`] and [`MeshPipelineDescriptor`].
855///
856/// cbindgen:ignore
857#[doc(hidden)]
858#[derive(Clone, Debug)]
859#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
860pub struct GeneralRenderPipelineDescriptor<
861    'a,
862    PLL = Arc<PipelineLayout>,
863    SM = Arc<ShaderModule>,
864    PLC = Arc<PipelineCache>,
865> {
866    pub label: Label<'a>,
867    /// The layout of bind groups for this pipeline.
868    pub layout: Option<PLL>,
869    /// The vertex processing state for this pipeline.
870    pub vertex: RenderPipelineVertexProcessor<'a, SM>,
871    /// The properties of the pipeline at the primitive assembly and rasterization level.
872    #[cfg_attr(feature = "serde", serde(default))]
873    pub primitive: wgt::PrimitiveState,
874    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
875    #[cfg_attr(feature = "serde", serde(default))]
876    pub depth_stencil: Option<wgt::DepthStencilState>,
877    /// The multi-sampling properties of the pipeline.
878    #[cfg_attr(feature = "serde", serde(default))]
879    pub multisample: wgt::MultisampleState,
880    /// The fragment processing state for this pipeline.
881    pub fragment: Option<FragmentState<'a, SM>>,
882    /// If the pipeline will be used with a multiview render pass, this indicates how many array
883    /// layers the attachments will have.
884    pub multiview_mask: Option<NonZeroU32>,
885    /// The pipeline cache to use when creating this pipeline.
886    pub cache: Option<PLC>,
887}
888impl<'a, PLL, SM, PLC> From<RenderPipelineDescriptor<'a, PLL, SM, PLC>>
889    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
890{
891    fn from(value: RenderPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
892        Self {
893            label: value.label,
894            layout: value.layout,
895            vertex: RenderPipelineVertexProcessor::Vertex(value.vertex),
896            primitive: value.primitive,
897            depth_stencil: value.depth_stencil,
898            multisample: value.multisample,
899            fragment: value.fragment,
900            multiview_mask: value.multiview_mask,
901            cache: value.cache,
902        }
903    }
904}
905impl<'a, PLL, SM, PLC> From<MeshPipelineDescriptor<'a, PLL, SM, PLC>>
906    for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
907{
908    fn from(value: MeshPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
909        Self {
910            label: value.label,
911            layout: value.layout,
912            vertex: RenderPipelineVertexProcessor::Mesh(value.task, value.mesh),
913            primitive: value.primitive,
914            depth_stencil: value.depth_stencil,
915            multisample: value.multisample,
916            fragment: value.fragment,
917            multiview_mask: value.multiview,
918            cache: value.cache,
919        }
920    }
921}
922
923/// Not a public API. For use by `player` only.
924///
925/// cbindgen:ignore
926pub type ResolvedGeneralRenderPipelineDescriptor<'a> =
927    GeneralRenderPipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
928
929#[derive(Clone, Debug)]
930#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
931pub struct PipelineCacheDescriptor<'a> {
932    pub label: Label<'a>,
933    pub data: Option<Cow<'a, [u8]>>,
934    pub fallback: bool,
935}
936
937#[derive(Clone, Debug, Error)]
938#[non_exhaustive]
939pub enum ColorStateError {
940    #[error("Format {0:?} is not renderable")]
941    FormatNotRenderable(wgt::TextureFormat),
942    #[error("Format {0:?} is not blendable")]
943    FormatNotBlendable(wgt::TextureFormat),
944    #[error("Format {0:?} does not have a color aspect")]
945    FormatNotColor(wgt::TextureFormat),
946    #[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:?}.")]
947    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
948    #[error("Output format {pipeline} is incompatible with the shader {shader}")]
949    IncompatibleFormat {
950        pipeline: validation::NumericType,
951        shader: validation::NumericType,
952    },
953    #[error("Invalid write mask {0:?}")]
954    InvalidWriteMask(wgt::ColorWrites),
955    #[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.")]
956    BlendFactorOnUnsupportedTarget {
957        factor: wgt::BlendFactor,
958        target: u32,
959    },
960    #[error("The {which} blend factor {factor:?} is not valid because the shader output does have an alpha channel.")]
961    InvalidAlphaBlend {
962        which: &'static str,
963        factor: wgt::BlendFactor,
964    },
965    #[error(
966        "Blend factor {factor:?} for render target {target} is not valid. Blend factor must be `one` when using min/max blend operations."
967    )]
968    InvalidMinMaxBlendFactor {
969        factor: wgt::BlendFactor,
970        target: u32,
971    },
972    #[error("Shader does not produce an output at this index")]
973    OutputNotPresent,
974}
975
976#[derive(Clone, Debug, Error)]
977#[non_exhaustive]
978pub enum DepthStencilStateError {
979    #[error("Format {0:?} is not renderable")]
980    FormatNotRenderable(wgt::TextureFormat),
981    #[error("Format {0:?} is not a depth/stencil format")]
982    FormatNotDepthOrStencil(wgt::TextureFormat),
983    #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
984    FormatNotDepth(wgt::TextureFormat),
985    #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
986    FormatNotStencil(wgt::TextureFormat),
987    #[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:?}.")]
988    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
989    #[error("Depth bias is not compatible with non-triangle topology {0:?}")]
990    DepthBiasWithIncompatibleTopology(wgt::PrimitiveTopology),
991    #[error("Depth compare function must be specified for depth format {0:?}")]
992    MissingDepthCompare(wgt::TextureFormat),
993    #[error("Depth write enabled must be specified for depth format {0:?}")]
994    MissingDepthWriteEnabled(wgt::TextureFormat),
995}
996
997#[derive(Clone, Debug, Error)]
998#[non_exhaustive]
999pub enum CreateRenderPipelineError {
1000    #[error(transparent)]
1001    ColorAttachment(#[from] ColorAttachmentError),
1002    #[error(transparent)]
1003    Device(#[from] DeviceError),
1004    #[error("Unable to derive an implicit layout")]
1005    Implicit(#[from] ImplicitLayoutError),
1006    #[error("Color state [{0}] is invalid")]
1007    ColorState(u8, #[source] ColorStateError),
1008    #[error("Depth/stencil state is invalid")]
1009    DepthStencilState(#[from] DepthStencilStateError),
1010    #[error("Invalid sample count {0}")]
1011    InvalidSampleCount(u32),
1012    #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
1013    TooManyVertexBuffers { given: u32, limit: u32 },
1014    #[error("The number of bind groups + vertex buffers {given} exceeds the limit {limit}")]
1015    TooManyBindGroupsPlusVertexBuffers { given: u32, limit: u32 },
1016    #[error("The number of vertex-stage buffers and acceleration structures {given} exceeds the limit {limit}")]
1017    TooManyBuffersAndAccelerationStructuresInVertexStage { given: u32, limit: u32 },
1018    #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
1019    TooManyVertexAttributes { given: u32, limit: u32 },
1020    #[error("Vertex attribute location {given} must be less than limit {limit}")]
1021    VertexAttributeLocationTooLarge { given: u32, limit: u32 },
1022    #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
1023    VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
1024    #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")]
1025    VertexAttributeStrideTooLarge {
1026        location: wgt::ShaderLocation,
1027        given: u32,
1028        limit: u32,
1029    },
1030    #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_ALIGNMENT`")]
1031    UnalignedVertexStride {
1032        index: u32,
1033        stride: wgt::BufferAddress,
1034    },
1035    #[error("Vertex attribute at location {location} has invalid offset {offset}")]
1036    InvalidVertexAttributeOffset {
1037        location: wgt::ShaderLocation,
1038        offset: wgt::BufferAddress,
1039    },
1040    #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
1041    ShaderLocationClash(u32),
1042    #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
1043    StripIndexFormatForNonStripTopology {
1044        strip_index_format: Option<wgt::IndexFormat>,
1045        topology: wgt::PrimitiveTopology,
1046    },
1047    #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
1048    ConservativeRasterizationNonFillPolygonMode,
1049    #[error(transparent)]
1050    MissingFeatures(#[from] MissingFeatures),
1051    #[error(transparent)]
1052    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1053    #[error("Error matching {stage:?} shader requirements against the pipeline")]
1054    Stage {
1055        stage: wgt::ShaderStages,
1056        #[source]
1057        error: validation::StageError,
1058    },
1059    #[error("Internal error in {stage:?} shader: {error}")]
1060    Internal {
1061        stage: wgt::ShaderStages,
1062        error: String,
1063    },
1064    #[error("Pipeline constant error in {stage:?} shader: {error}")]
1065    PipelineConstants {
1066        stage: wgt::ShaderStages,
1067        error: String,
1068    },
1069    #[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.")]
1070    UnalignedShader { group: u32, binding: u32, size: u64 },
1071    #[error("Dual-source blending requires exactly one color target, but {count} color targets are present")]
1072    DualSourceBlendingWithMultipleColorTargets { count: usize },
1073    #[error("{}", concat!(
1074        "At least one color attachment or depth-stencil attachment was expected, ",
1075        "but no render target for the pipeline was specified."
1076    ))]
1077    NoTargetSpecified,
1078    #[error(transparent)]
1079    InvalidResource(#[from] InvalidResourceError),
1080}
1081
1082impl WebGpuError for CreateRenderPipelineError {
1083    fn webgpu_error_type(&self) -> ErrorType {
1084        match self {
1085            Self::Device(e) => e.webgpu_error_type(),
1086            Self::InvalidResource(e) => e.webgpu_error_type(),
1087            Self::MissingFeatures(e) => e.webgpu_error_type(),
1088            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1089
1090            Self::Internal { .. } => ErrorType::Internal,
1091
1092            Self::ColorAttachment(_)
1093            | Self::Implicit(_)
1094            | Self::ColorState(_, _)
1095            | Self::DepthStencilState(_)
1096            | Self::InvalidSampleCount(_)
1097            | Self::TooManyVertexBuffers { .. }
1098            | Self::TooManyBindGroupsPlusVertexBuffers { .. }
1099            | Self::TooManyBuffersAndAccelerationStructuresInVertexStage { .. }
1100            | Self::TooManyVertexAttributes { .. }
1101            | Self::VertexAttributeLocationTooLarge { .. }
1102            | Self::VertexStrideTooLarge { .. }
1103            | Self::UnalignedVertexStride { .. }
1104            | Self::InvalidVertexAttributeOffset { .. }
1105            | Self::ShaderLocationClash(_)
1106            | Self::StripIndexFormatForNonStripTopology { .. }
1107            | Self::ConservativeRasterizationNonFillPolygonMode
1108            | Self::Stage { .. }
1109            | Self::UnalignedShader { .. }
1110            | Self::DualSourceBlendingWithMultipleColorTargets { .. }
1111            | Self::NoTargetSpecified
1112            | Self::PipelineConstants { .. }
1113            | Self::VertexAttributeStrideTooLarge { .. } => ErrorType::Validation,
1114        }
1115    }
1116}
1117
1118bitflags::bitflags! {
1119    #[repr(transparent)]
1120    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1121    pub struct PipelineFlags: u32 {
1122        const BLEND_CONSTANT = 1 << 0;
1123        const STENCIL_REFERENCE = 1 << 1;
1124        const WRITES_DEPTH = 1 << 2;
1125        const WRITES_STENCIL = 1 << 3;
1126    }
1127}
1128
1129/// How a render pipeline will retrieve attributes from a particular vertex buffer.
1130#[derive(Clone, Copy, Debug)]
1131pub struct VertexStep {
1132    /// The byte stride in the buffer between one attribute value and the next.
1133    pub stride: wgt::BufferAddress,
1134
1135    /// The byte size required to fit the last vertex in the stream.
1136    pub last_stride: wgt::BufferAddress,
1137
1138    /// Whether the buffer is indexed by vertex number or instance number.
1139    pub mode: wgt::VertexStepMode,
1140}
1141
1142impl Default for VertexStep {
1143    fn default() -> Self {
1144        Self {
1145            stride: 0,
1146            last_stride: 0,
1147            mode: wgt::VertexStepMode::Vertex,
1148        }
1149    }
1150}
1151
1152#[derive(Debug)]
1153pub(crate) struct RenderPipelineState {
1154    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
1155    pub(crate) layout: Arc<PipelineLayout>,
1156}
1157
1158#[derive(Debug)]
1159pub struct RenderPipeline {
1160    pub(crate) state: ResourceState<RenderPipelineState>,
1161    pub(crate) device: Arc<Device>,
1162    pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
1163    pub(crate) pass_context: RenderPassContext,
1164    pub(crate) flags: PipelineFlags,
1165    pub(crate) topology: wgt::PrimitiveTopology,
1166    pub(crate) strip_index_format: Option<wgt::IndexFormat>,
1167    pub(crate) vertex_steps: Vec<Option<VertexStep>>,
1168    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
1169    pub(crate) immediate_slots_required: naga::valid::ImmediateSlots,
1170    /// The `label` from the descriptor used to create the resource.
1171    pub(crate) label: String,
1172    pub(crate) tracking_data: TrackingData,
1173    /// Whether this is a mesh shader pipeline
1174    pub(crate) is_mesh: bool,
1175    pub(crate) has_task_shader: bool,
1176}
1177
1178impl Drop for RenderPipeline {
1179    #[allow(trivial_casts)]
1180    fn drop(&mut self) {
1181        profiling::scope!("RenderPipeline::drop");
1182        api_log!("RenderPipeline::drop {:?}", self as *const _);
1183        resource_log!("Destroy raw {}", self.error_ident());
1184        #[cfg(feature = "trace")]
1185        {
1186            use crate::device::trace;
1187            if let Some(t) = self.device.trace.lock().as_mut() {
1188                t.add(trace::Action::DropRenderPipeline(unsafe {
1189                    trace::to_trace(self)
1190                }));
1191            }
1192        }
1193        let ResourceState::Valid(state) = &mut self.state else {
1194            return;
1195        };
1196        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1197        let raw = unsafe { ManuallyDrop::take(&mut state.raw) };
1198        unsafe {
1199            self.device.raw().destroy_render_pipeline(raw);
1200        }
1201    }
1202}
1203
1204crate::impl_resource_type!(RenderPipeline);
1205crate::impl_labeled!(RenderPipeline);
1206crate::impl_parent_device!(RenderPipeline);
1207crate::impl_storage_item!(RenderPipeline);
1208crate::impl_trackable!(RenderPipeline);
1209
1210impl RenderPipeline {
1211    pub(crate) fn raw(&self) -> Result<&dyn hal::DynRenderPipeline, InvalidResourceError> {
1212        let ResourceState::Valid(state) = &self.state else {
1213            return Err(InvalidResourceError(self.error_ident()));
1214        };
1215        Ok(state.raw.as_ref())
1216    }
1217
1218    pub(crate) fn layout(&self) -> Result<&Arc<PipelineLayout>, InvalidResourceError> {
1219        let ResourceState::Valid(state) = &self.state else {
1220            return Err(InvalidResourceError(self.error_ident()));
1221        };
1222        Ok(&state.layout)
1223    }
1224
1225    pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1226        let ResourceState::Valid(_) = &self.state else {
1227            return Err(InvalidResourceError(self.error_ident()));
1228        };
1229        Ok(())
1230    }
1231
1232    pub(crate) fn invalid(device: Arc<Device>, label: String) -> Arc<Self> {
1233        Arc::new(Self {
1234            tracking_data: TrackingData::new(device.tracker_indices.render_pipelines.clone()),
1235            state: ResourceState::Invalid,
1236            device,
1237            _shader_modules: ArrayVec::new(),
1238            pass_context: RenderPassContext {
1239                attachments: AttachmentData {
1240                    colors: ArrayVec::new(),
1241                    resolves: ArrayVec::new(),
1242                    depth_stencil: None,
1243                },
1244                sample_count: 0,
1245                multiview_mask: None,
1246            },
1247            flags: PipelineFlags::empty(),
1248            topology: wgt::PrimitiveTopology::TriangleList,
1249            strip_index_format: None,
1250            vertex_steps: Vec::new(),
1251            late_sized_buffer_groups: ArrayVec::new(),
1252            immediate_slots_required: naga::valid::ImmediateSlots::default(),
1253            label,
1254            is_mesh: false,
1255            has_task_shader: false,
1256        })
1257    }
1258
1259    pub fn get_bind_group_layout_inner(
1260        self: &Arc<Self>,
1261        index: u32,
1262    ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
1263        self.layout()?.get_bind_group_layout(index, self.into())
1264    }
1265
1266    pub fn get_bind_group_layout(self: &Arc<Self>, index: u32) -> Arc<BindGroupLayout> {
1267        let bgl = self
1268            .get_bind_group_layout_inner(index)
1269            .unwrap_or_else(|err| {
1270                self.device
1271                    .handle_error_nolabel(err, "RenderPipeline::get_bind_group_layout");
1272                BindGroupLayout::invalid(&self.device, String::new())
1273            });
1274        #[cfg(feature = "trace")]
1275        if let Some(ref mut trace) = *self.device.trace.lock() {
1276            use crate::device::trace;
1277            use trace::IntoTrace;
1278            trace.add(trace::Action::GetRenderPipelineBindGroupLayout {
1279                id: bgl.to_trace(),
1280                pipeline: self.to_trace(),
1281                index,
1282            });
1283        };
1284        bgl
1285    }
1286}
1287
1288#[cfg(test)]
1289mod tests {
1290    use super::*;
1291
1292    fn passthrough_interface(entry_point_names: &[&str]) -> validation::PassthroughInterface {
1293        validation::PassthroughInterface {
1294            entry_point_names: entry_point_names
1295                .iter()
1296                .map(|name| (*name).to_owned())
1297                .collect(),
1298        }
1299    }
1300
1301    #[test]
1302    fn select_implicit_passthrough_entry_point() {
1303        let empty = passthrough_interface(&[]);
1304        assert!(matches!(
1305            finalize_passthrough_entry_point_name(&empty, None),
1306            Err(validation::StageError::NoEntryPointFound)
1307        ));
1308
1309        let single = passthrough_interface(&["main"]);
1310        assert_eq!(
1311            finalize_passthrough_entry_point_name(&single, None).unwrap(),
1312            "main"
1313        );
1314
1315        let multiple = passthrough_interface(&["vertex", "fragment"]);
1316        assert!(matches!(
1317            finalize_passthrough_entry_point_name(&multiple, None),
1318            Err(validation::StageError::MultipleEntryPointsFound)
1319        ));
1320    }
1321
1322    #[test]
1323    fn select_explicit_passthrough_entry_point() {
1324        let interface = passthrough_interface(&["main"]);
1325        assert_eq!(
1326            finalize_passthrough_entry_point_name(&interface, Some("main")).unwrap(),
1327            "main"
1328        );
1329        assert!(matches!(
1330            finalize_passthrough_entry_point_name(&interface, Some("missing")),
1331            Err(validation::StageError::MissingEntryPoint(name)) if name == "missing"
1332        ));
1333    }
1334}