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