wgpu_core/validation/
shader_io_deductions.rs

1use core::fmt::{self, Debug, Display, Formatter};
2
3#[cfg(doc)]
4#[expect(unused_imports)]
5use crate::validation::StageError;
6
7/// Max shader I/O variable deductions for vertex shader output. Used by
8/// [`StageError::TooManyUserDefinedVertexOutputs`] and
9/// [`StageError::VertexOutputLocationTooLarge`].
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum MaxVertexShaderOutputDeduction {
12    /// When a pipeline's [`crate::pipeline::RenderPipelineDescriptor::primitive`] is set to
13    /// [`wgt::PrimitiveTopology::PointList`].
14    PointListPrimitiveTopology,
15}
16
17impl MaxVertexShaderOutputDeduction {
18    pub fn for_variables(self) -> u32 {
19        match self {
20            Self::PointListPrimitiveTopology => 1,
21        }
22    }
23
24    pub fn for_location(self) -> u32 {
25        match self {
26            Self::PointListPrimitiveTopology => 0,
27        }
28    }
29}
30
31/// Max shader I/O variable deductions for vertex shader output. Used by
32/// [`StageError::TooManyUserDefinedFragmentInputs`] and
33/// [`StageError::FragmentInputLocationTooLarge`].
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub enum MaxFragmentShaderInputDeduction {
36    InterStageBuiltIn(InterStageBuiltIn),
37}
38
39impl MaxFragmentShaderInputDeduction {
40    pub fn for_variables(self) -> u32 {
41        match self {
42            Self::InterStageBuiltIn(builtin) => match builtin {
43                InterStageBuiltIn::FrontFacing
44                | InterStageBuiltIn::SampleIndex
45                | InterStageBuiltIn::SampleMask
46                | InterStageBuiltIn::PrimitiveIndex
47                | InterStageBuiltIn::SubgroupInvocationId
48                | InterStageBuiltIn::SubgroupSize
49                | InterStageBuiltIn::ViewIndex
50                | InterStageBuiltIn::PointCoord => 1,
51                InterStageBuiltIn::Barycentric => 3,
52                InterStageBuiltIn::Position => 0,
53            },
54        }
55    }
56
57    pub fn from_inter_stage_builtin(builtin: naga::BuiltIn) -> Option<Self> {
58        use naga::BuiltIn;
59
60        Some(Self::InterStageBuiltIn(match builtin {
61            BuiltIn::Position { .. } => InterStageBuiltIn::Position,
62            BuiltIn::FrontFacing => InterStageBuiltIn::FrontFacing,
63            BuiltIn::SampleIndex => InterStageBuiltIn::SampleIndex,
64            BuiltIn::SampleMask => InterStageBuiltIn::SampleMask,
65            BuiltIn::PrimitiveIndex => InterStageBuiltIn::PrimitiveIndex,
66            BuiltIn::SubgroupSize => InterStageBuiltIn::SubgroupSize,
67            BuiltIn::SubgroupInvocationId => InterStageBuiltIn::SubgroupInvocationId,
68
69            BuiltIn::PointCoord => InterStageBuiltIn::PointCoord,
70            BuiltIn::Barycentric { .. } => InterStageBuiltIn::Barycentric,
71            BuiltIn::ViewIndex => InterStageBuiltIn::ViewIndex,
72
73            BuiltIn::BaseInstance
74            | BuiltIn::BaseVertex
75            | BuiltIn::ClipDistance
76            | BuiltIn::CullDistance
77            | BuiltIn::InstanceIndex
78            | BuiltIn::PointSize
79            | BuiltIn::VertexIndex
80            | BuiltIn::DrawID
81            | BuiltIn::FragDepth
82            | BuiltIn::GlobalInvocationId
83            | BuiltIn::LocalInvocationId
84            | BuiltIn::LocalInvocationIndex
85            | BuiltIn::WorkGroupId
86            | BuiltIn::WorkGroupSize
87            | BuiltIn::NumWorkGroups
88            | BuiltIn::NumSubgroups
89            | BuiltIn::SubgroupId
90            | BuiltIn::MeshTaskSize
91            | BuiltIn::CullPrimitive
92            | BuiltIn::PointIndex
93            | BuiltIn::LineIndices
94            | BuiltIn::TriangleIndices
95            | BuiltIn::VertexCount
96            | BuiltIn::Vertices
97            | BuiltIn::PrimitiveCount
98            | BuiltIn::Primitives => return None,
99        }))
100    }
101}
102
103/// A [`naga::BuiltIn`] that counts towards
104/// a [`MaxFragmentShaderInputDeduction::InterStageBuiltIn`].
105///
106/// See also <https://www.w3.org/TR/webgpu/#inter-stage-builtins>.
107#[derive(Clone, Copy, Debug, Eq, PartialEq)]
108pub enum InterStageBuiltIn {
109    // Standard for WebGPU
110    Position,
111    FrontFacing,
112    SampleIndex,
113    SampleMask,
114    PrimitiveIndex,
115    SubgroupInvocationId,
116    SubgroupSize,
117
118    // Non-standard
119    PointCoord,
120    Barycentric,
121    ViewIndex,
122}
123
124pub(in crate::validation) fn display_deductions_as_optional_list<T>(
125    deductions: &[T],
126    accessor: fn(&T) -> u32,
127) -> impl Display + '_
128where
129    T: Debug,
130{
131    struct DisplayFromFn<F>(F);
132
133    impl<F> Display for DisplayFromFn<F>
134    where
135        F: Fn(&mut Formatter<'_>) -> fmt::Result,
136    {
137        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
138            let Self(inner) = self;
139            inner(f)
140        }
141    }
142
143    DisplayFromFn(move |f: &mut Formatter<'_>| {
144        let relevant_deductions = deductions
145            .iter()
146            .map(|deduction| (deduction, accessor(deduction)))
147            .filter(|(_, effective_deduction)| *effective_deduction > 0);
148        if relevant_deductions.clone().next().is_some() {
149            writeln!(f, "; note that some deductions apply during validation:")?;
150            let mut wrote_something = false;
151            for deduction in deductions {
152                let deducted_amount = accessor(deduction);
153                if deducted_amount > 0 {
154                    writeln!(f, "\n- {deduction:?}: {}", accessor(deduction))?;
155                    wrote_something = true;
156                }
157            }
158            debug_assert!(
159                wrote_something,
160                "no substantial deductions found in error display"
161            );
162        }
163        Ok(())
164    })
165}