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            BuiltIn::PointCoord => InterStageBuiltIn::PointCoord,
69            BuiltIn::Barycentric { .. } => InterStageBuiltIn::Barycentric,
70            BuiltIn::ViewIndex => InterStageBuiltIn::ViewIndex,
71            BuiltIn::BaseInstance
72            | BuiltIn::BaseVertex
73            | BuiltIn::ClipDistance
74            | BuiltIn::CullDistance
75            | BuiltIn::InstanceIndex
76            | BuiltIn::PointSize
77            | BuiltIn::VertexIndex
78            | BuiltIn::DrawIndex
79            | BuiltIn::FragDepth
80            | BuiltIn::GlobalInvocationId
81            | BuiltIn::LocalInvocationId
82            | BuiltIn::LocalInvocationIndex
83            | BuiltIn::WorkGroupId
84            | BuiltIn::WorkGroupSize
85            | BuiltIn::NumWorkGroups
86            | BuiltIn::NumSubgroups
87            | BuiltIn::SubgroupId
88            | BuiltIn::MeshTaskSize
89            | BuiltIn::CullPrimitive
90            | BuiltIn::PointIndex
91            | BuiltIn::LineIndices
92            | BuiltIn::TriangleIndices
93            | BuiltIn::VertexCount
94            | BuiltIn::Vertices
95            | BuiltIn::PrimitiveCount
96            | BuiltIn::Primitives
97            | BuiltIn::RayInvocationId
98            | BuiltIn::NumRayInvocations
99            | BuiltIn::InstanceCustomData
100            | BuiltIn::GeometryIndex
101            | BuiltIn::WorldRayOrigin
102            | BuiltIn::WorldRayDirection
103            | BuiltIn::ObjectRayOrigin
104            | BuiltIn::ObjectRayDirection
105            | BuiltIn::RayTmin
106            | BuiltIn::RayTCurrentMax
107            | BuiltIn::ObjectToWorld
108            | BuiltIn::WorldToObject
109            | BuiltIn::HitKind => return None,
110        }))
111    }
112}
113
114/// A [`naga::BuiltIn`] that counts towards
115/// a [`MaxFragmentShaderInputDeduction::InterStageBuiltIn`].
116///
117/// See also <https://www.w3.org/TR/webgpu/#inter-stage-builtins>.
118#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119pub enum InterStageBuiltIn {
120    // Standard for WebGPU
121    Position,
122    FrontFacing,
123    SampleIndex,
124    SampleMask,
125    PrimitiveIndex,
126    SubgroupInvocationId,
127    SubgroupSize,
128
129    // Non-standard
130    PointCoord,
131    Barycentric,
132    ViewIndex,
133}
134
135pub(in crate::validation) fn display_deductions_as_optional_list<T>(
136    deductions: &[T],
137    accessor: fn(&T) -> u32,
138) -> impl Display + '_
139where
140    T: Debug,
141{
142    struct DisplayFromFn<F>(F);
143
144    impl<F> Display for DisplayFromFn<F>
145    where
146        F: Fn(&mut Formatter<'_>) -> fmt::Result,
147    {
148        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
149            let Self(inner) = self;
150            inner(f)
151        }
152    }
153
154    DisplayFromFn(move |f: &mut Formatter<'_>| {
155        let relevant_deductions = deductions
156            .iter()
157            .map(|deduction| (deduction, accessor(deduction)))
158            .filter(|(_, effective_deduction)| *effective_deduction > 0);
159        if relevant_deductions.clone().next().is_some() {
160            writeln!(f, "; note that some deductions apply during validation:")?;
161            let mut wrote_something = false;
162            for deduction in deductions {
163                let deducted_amount = accessor(deduction);
164                if deducted_amount > 0 {
165                    writeln!(f, "\n- {deduction:?}: {}", accessor(deduction))?;
166                    wrote_something = true;
167                }
168            }
169            debug_assert!(
170                wrote_something,
171                "no substantial deductions found in error display"
172            );
173        }
174        Ok(())
175    })
176}